Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
target: esp32s3
- path: 'components/binary-log/example'
target: esp32s3
- path: 'components/bldc_current_sense/example'
target: esp32s3
- path: 'components/bldc_haptics/example'
target: esp32s3
- path: 'components/bldc_motor/example'
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/upload_components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ jobs:
components/base_peripheral
components/bdc_driver
components/binary-log
components/bldc_current_sense
components/bldc_driver
components/bldc_haptics
components/bldc_motor
components/bldc_types
components/ble_gatt_server
components/bm8563
components/bmi270
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ lib/pc
_build/
__pycache__/
managed_components/
.cache
4 changes: 4 additions & 0 deletions components/bldc_current_sense/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(
INCLUDE_DIRS "include"
REQUIRES base_component math bldc_driver bldc_types
)
41 changes: 41 additions & 0 deletions components/bldc_current_sense/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# BLDC Current Sense

[![Badge](https://components.espressif.com/components/espp/bldc_current_sense/badge.svg)](https://components.espressif.com/components/espp/bldc_current_sense)

The `bldc_current_sense` component provides `espp::CurrentSense`, a field-oriented
control (FOC) current sensor for BLDC motors. It implements the
`CurrentSensorConcept` used by `espp::BldcMotor`, so it can be supplied as a
motor's current sense to enable current-feedback torque control
(`TorqueControlType::DC_CURRENT` and `TorqueControlType::FOC_CURRENT`).

`espp::CurrentSense` is intentionally decoupled from any specific ADC. You
provide a `read_phase_currents` callback that returns the most recently sampled
phase currents (in amps). The class then:

- subtracts the per-phase zero-current offset captured during `driver_align()`,
- applies any per-phase gain-sign correction,
- reconstructs an unmeasured phase (return `NAN` for it) assuming `Ia + Ib + Ic = 0`,
- runs the Clarke + Park transforms to produce the d/q currents
(`get_foc_currents()`) and the signed DC current magnitude (`get_dc_current()`).

## PWM-synchronized sampling

Accurate low-side current sensing requires sampling while the low-side FETs are
conducting (the PWM center). Use
`espp::BldcDriver::register_pwm_sample_callback()` to trigger your ADC read at
the timer "empty" (TEZ) event, and have your `read_phase_currents` callback
return the latest sampled values.

## Status

> **Experimental.** Zero-current offset calibration is implemented, but automatic
> phase-ordering / gain-sign discovery is not — set `phase_gain_signs` in the
> `Config` if a sense channel is inverted. Current-mode torque control depends on
> accurate, PWM-synchronized sampling and per-board tuning and must be validated
> on hardware.

## Example

The example builds a software model of the analog front-end (no motor hardware
required) to exercise the calibration + Clarke/Park pipeline, and statically
verifies that `espp::CurrentSense` satisfies the `CurrentSensorConcept`.
22 changes: 22 additions & 0 deletions components/bldc_current_sense/example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.20)

set(ENV{IDF_COMPONENT_MANAGER} "0")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

# add the component directories that we want to use
set(EXTRA_COMPONENT_DIRS
"../../../components/"
)

set(
COMPONENTS
"main esptool_py task monitor bldc_types bldc_driver bldc_current_sense"
CACHE STRING
"List of components to include"
)

project(bldc_current_sense_example)

set(CMAKE_CXX_STANDARD 20)
34 changes: 34 additions & 0 deletions components/bldc_current_sense/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# BLDC Current Sense Example

This example exercises the `espp::CurrentSense` FOC current sensor without any
motor hardware. It:

- statically asserts that `espp::CurrentSense` satisfies the
`CurrentSensorConcept` required by `espp::BldcMotor`,
- builds a small software model of the analog front-end that produces phase
currents from a commanded `(Id, Iq)` at a given electrical angle (with a fixed
per-channel bias, and phase C unmeasured to exercise reconstruction),
- runs `driver_align()` to capture the zero-current offsets,
- commands a known q-axis current and prints the recovered `Id`/`Iq` and signed
DC current across a range of electrical angles, confirming the offset removal +
Clarke/Park + phase-reconstruction pipeline.

## How to use example

### Build and Flash

Build the project and flash it to the board, then run monitor tool to view
serial output:

```
idf.py -p PORT flash monitor
```

(Replace PORT with the name of the serial port to use.)

(To exit the serial monitor, type ``Ctrl-]``.)

## Example Output

The recovered `Iq` should track the commanded value (with `Id ~ 0`) at every
electrical angle, demonstrating the current-sense math end to end.
2 changes: 2 additions & 0 deletions components/bldc_current_sense/example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS ".")
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include <atomic>
#include <chrono>
#include <cmath>
#include <thread>

#include "bldc_concepts.hpp"
#include "bldc_driver.hpp"
#include "current_sense.hpp"
#include "logger.hpp"

using namespace std::chrono_literals;

// Verify at compile time that espp::CurrentSense satisfies the
// CurrentSensorConcept that espp::BldcMotor requires for its current sense.
static_assert(espp::CurrentSensorConcept<espp::CurrentSense<espp::BldcDriver>>,
"CurrentSense must satisfy CurrentSensorConcept");

// ---------------------------------------------------------------------------
// Simple software model of the analog front-end so the example can run without
// any motor hardware. It produces phase currents (in amps) from a commanded
// (Id, Iq) at a given electrical angle, plus a fixed per-channel bias to
// exercise the offset calibration. Phase C is reported as NAN to exercise the
// two-phase reconstruction path.
// ---------------------------------------------------------------------------
struct SimFrontEnd {
std::atomic<float> id{0.0f};
std::atomic<float> iq{0.0f};
std::atomic<float> angle{0.0f};
// analog bias of the sense channels (what offset calibration should remove)
float bias_a{1.65f};
float bias_b{1.66f};

espp::PhaseCurrent read() const {
const float th = angle.load();
const float a = id.load() * std::cos(th) - iq.load() * std::sin(th); // alpha
const float b = id.load() * std::sin(th) + iq.load() * std::cos(th); // beta
// inverse (amplitude-invariant) Clarke -> phase currents
espp::PhaseCurrent pc;
pc.a = a + bias_a;
pc.b = (-0.5f * a + espp::_SQRT3_2 * b) + bias_b;
pc.c = NAN; // phase C not measured on this "board"
return pc;
}
};

extern "C" void app_main(void) {
espp::Logger logger(
{.tag = "BLDC Current Sense Example", .level = espp::Logger::Verbosity::INFO});
logger.info("Starting example!");

//! [bldc current sense example]
static SimFrontEnd front_end;

espp::CurrentSense<espp::BldcDriver> current_sense({
// the read callback returns the latest sampled phase currents (amps). On
// real hardware you would trigger this read from
// BldcDriver::register_pwm_sample_callback() so it is PWM-synchronized.
.read_phase_currents = []() -> espp::PhaseCurrent { return front_end.read(); },
.driver = nullptr, // no driver needed for this offline example
.calibration_samples = 200,
.log_level = espp::Logger::Verbosity::INFO,
});

// 1) Calibrate the zero-current offsets while the "motor" is at rest.
front_end.id = 0.0f;
front_end.iq = 0.0f;
bool ok = current_sense.driver_align(1.0f);
logger.info("Calibration {}", ok ? "succeeded" : "failed");

// 2) Command a known (Id, Iq) at a few electrical angles and confirm the
// current sense recovers it (Id ~ 0, Iq ~ commanded), proving the
// offset removal + Clarke/Park + phase reconstruction pipeline.
const float commanded_iq = 2.5f;
front_end.id = 0.0f;
front_end.iq = commanded_iq;
for (float angle = 0.0f; angle < espp::_2PI; angle += espp::_PI_3) {
front_end.angle = angle;
auto dq = current_sense.get_foc_currents(angle);
float dc = current_sense.get_dc_current(angle);
logger.info(
"angle={:5.2f} rad -> Id={:6.3f} A, Iq={:6.3f} A (commanded {:.3f}), |Idc|={:6.3f} A",
angle, dq.d, dq.q, commanded_iq, dc);
}
//! [bldc current sense example]

logger.info("Example complete!");
while (true) {
std::this_thread::sleep_for(1s);
}
}
14 changes: 14 additions & 0 deletions components/bldc_current_sense/example/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CONFIG_IDF_TARGET="esp32s3"

CONFIG_COMPILER_OPTIMIZATION_PERF=y

CONFIG_FREERTOS_HZ=1000

CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="8MB"

CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240

CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
25 changes: 25 additions & 0 deletions components/bldc_current_sense/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## IDF Component Manager Manifest File
license: "MIT"
description: "FOC current sense for BLDC motor control (espp)"
url: "https://github.com/esp-cpp/espp/tree/main/components/bldc_current_sense"
repository: "git://github.com/esp-cpp/espp.git"
maintainers:
- William Emfinger <waemfinger@gmail.com>
documentation: "https://esp-cpp.github.io/espp/bldc/bldc_current_sense.html"
examples:
- path: example
tags:
- cpp
- Component
- BLDC
- Motor
- Control
- FOC
- Current
dependencies:
idf:
version: '>=5.0'
espp/base_component: '>=1.0'
espp/math: '>=1.0'
espp/bldc_driver: '>=1.0'
espp/bldc_types: '>=1.0'
Loading
Loading