Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
22ff2a8
TunerStudio: Sensors tab and 12ch scope (encoder, speed, Iuvw/αβ)
uLipe Apr 23, 2026
0a68041
doc: remove duplicated docs folder
uLipe Apr 23, 2026
311a464
fix(tuner,scope,studio): keep scope stream before esp_foc_run
uLipe Apr 23, 2026
f494015
aaaaaaaaaa
uLipe Apr 28, 2026
ae630d2
fix: Tuner Studio stack and Q16 motor-control pipeline integration
uLipe Apr 29, 2026
6241f3c
controls: fix dt inconsistency for calculating speed
uLipe May 4, 2026
8851c45
foc_math: fix trigo scaling values from foc pipeline
uLipe May 4, 2026
74f4fa9
ci: fix unit tests
uLipe May 5, 2026
e7bb23d
project: refactor the inclusion files
uLipe May 15, 2026
22beaa5
foc_core: refactor the voltage modulation pipeline
uLipe May 15, 2026
0f4841b
controls: drop auto tuner and add unify openloop path
uLipe May 15, 2026
78b6d03
drivers: reintroduce the simulated rotor sensor.
uLipe May 15, 2026
4b9c58d
observer: remove observer and sensorless infrastructure.
uLipe May 15, 2026
261e931
core: merge controls into esp_foc core
uLipe May 15, 2026
2e77a4e
doc: update readme
uLipe May 15, 2026
fb9247f
tuner: refactor the tuner protocol
uLipe May 19, 2026
0560ec6
Add encoder PLL at PWM rate for continuous rotor angle tracking.
uLipe Jun 1, 2026
858ac34
Replace esp_adc continuous with HAL/DMA unified isensor driver.
uLipe Jun 1, 2026
e023b31
Add bench axis mode and locked-rotor isensor characterization example.
uLipe Jun 1, 2026
502570c
Unify isensor ADC with ETM HW trigger and drop oneshot driver.
uLipe Jun 1, 2026
cb40e82
Port per-target ADC calibration and align isensor to 3.3 V full scale.
uLipe Jun 1, 2026
af81eaf
Add FOC-in-the-loop simulated plant for FITL builds without HW drivers.
uLipe Jun 1, 2026
9fc2bf8
Convert FITL PMSM plant hot path to Q16 for soft-float targets.
uLipe Jun 1, 2026
8e6150a
Replace TunerStudio with espFoC Tool host stack (Phase 0).
uLipe Jun 1, 2026
c2d34f0
Polish espFoC Tool views with NVS diff, split Config/Current, and rec…
uLipe Jun 1, 2026
0ae483a
Document espFoC Tool workflow and re-enable host CI tests.
uLipe Jun 1, 2026
87fd337
Unify espFoC Tool GUI into Tune/Dashboard and drop QML spike.
uLipe Jun 2, 2026
699af4e
Fix FITL GPTimer tick not reaching inverter callback in CI.
uLipe Jun 2, 2026
ad45742
Add USB Serial/JTAG tuner bridge for chips without USB OTG.
uLipe Jun 2, 2026
a5a50ad
Fix runner task affinity on single-core ESP32 targets.
uLipe Jun 2, 2026
0e4c523
fix: V-001 security vulnerability
orbisai0security Jun 2, 2026
1daec19
fix: add bounds check before memcpy in esp_foc_tuner.c
orbisai0security Jun 2, 2026
3da2f33
test: replace libcheck invariant test with runnable Unity reactor test
orbisai0security Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
38 changes: 15 additions & 23 deletions .github/workflows/espfoc_flow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,31 +50,13 @@ jobs:
run: |
./idf/install.sh
. ./idf/export.sh
pushd examples/axis_sensored
idf.py set-target esp32s3
idf.py build
idf.py set-target esp32p4
idf.py build
popd
pushd examples/tuner_demo
idf.py set-target esp32
idf.py build
popd
pushd examples/tuner_studio_target
# Default sdkconfig drives USB-CDC; valid on S3/P4. We exercise
# all three paths so a CDC-only or UART-only regression on any
# target is caught — and so the ESP32-P4 one-shot ADC current
# sensor branch also sees compiler eyes on every PR.
# The ESP32-specific overrides live in sdkconfig.defaults.esp32
# (auto-loaded by IDF on set-target esp32).
pushd examples/axis_tuning
idf.py set-target esp32s3
idf.py build
rm -rf build sdkconfig
idf.py set-target esp32p4
idf.py set-target esp32c6
idf.py build
rm -rf build sdkconfig
idf.py set-target esp32
idf.py build
popd
pushd examples/test_drivers/test_current_sense
idf.py set-target esp32s3
Expand All @@ -94,7 +76,7 @@ jobs:
popd

python_tests:
name: TunerStudio host tests
name: espFoC Tool host tests
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -110,18 +92,20 @@ jobs:
sudo apt-get update
sudo apt-get install -y libegl1 libxkbcommon0 libdbus-1-3
python -m pip install --upgrade pip
pip install -r tools/espfoc_studio/requirements.txt
pip install -r tools/espfoc_tool/requirements.txt

- name: Run host test suite
env:
QT_QPA_PLATFORM: offscreen
PYTHONPATH: ${{ github.workspace }}/tools
ESPFOC_TOOL_NO_GL: "1"
run: |
python -m pytest tools/espfoc_studio/tests/ -v --tb=short
python -m pytest tools/espfoc_tool/tests/ -v --tb=short

unit_tests:
name: Unit tests (build and run on QEMU)
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down Expand Up @@ -164,6 +148,7 @@ jobs:
idf.py -D TEST_COMPONENTS=espFoC build

- name: Run unit tests on QEMU
timeout-minutes: 15
env:
IDF_PATH: ${{ github.workspace }}/idf
run: |
Expand All @@ -172,6 +157,13 @@ jobs:
cd examples/unit_test_runner
python run_unit_tests_qemu.py

- name: Build unit test runner (esp32c6 compile check)
run: |
. ./idf/export.sh
cd examples/unit_test_runner
idf.py set-target esp32c6
idf.py -D TEST_COMPONENTS=espFoC build

# Optional: run on real device on self-hosted runner (ESPPORT set).
- name: Run unit tests on device
env:
Expand Down
139 changes: 74 additions & 65 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
set(srcs "source/motor_control/esp_foc_core.c"
"source/motor_control/control_strategy/esp_foc_control_current_mode_sensored.c"
"source/motor_control/esp_foc_angle_predictor.c"
"source/motor_control/esp_foc_axis_bench.c"
"source/motor_control/esp_foc_estimator_q16.c"
"source/motor_control/esp_foc_biquad_q16.c"
"source/motor_control/esp_foc_calibration.c"
"source/motor_control/esp_foc_design_mpz.c"
"source/motor_control/esp_foc_link.c"
"source/motor_control/esp_foc_axis_tuning.c"
"source/motor_control/esp_foc_injection.c"
"source/motor_control/esp_foc_scope.c"
"source/calibration/esp_foc_calibration_format.c"
"source/calibration/esp_foc_calibration_nvs.c"
"source/calibration/esp_foc_calibration_axis.c"
"source/gui_link/esp_foc_link.c"
"source/gui_link/esp_foc_scope.c"
"source/motor_control/esp_foc_iq31.c"
"source/motor_control/esp_foc_q16.c"
"source/motor_control/observers/esp_foc_pll_observer.c"
"source/motor_control/observers/esp_foc_pmsm_model_observer.c"
"source/motor_control/observers/esp_foc_kf_observer.c"
"source/motor_control/observers/esp_foc_simu_observer.c"
)

set(includes "include" "source/motor_control")
Expand All @@ -22,73 +17,87 @@ if(ESP_PLATFORM)

idf_build_get_property(target IDF_TARGET)

set(requires esp_driver_uart esp_driver_pcnt esp_driver_gpio esp_driver_mcpwm esp_adc driver freertos esp_system esp_timer nvs_flash)
# esp_tinyusb is fetched conditionally for USB-capable targets via
# idf_component.yml; declare the priv require eagerly here so the bridge
# source file finds tinyusb.h on those targets even when the user has not
# enabled CONFIG_ESP_FOC_BRIDGE_USBCDC at component-graph resolution time.
set(requires driver freertos esp_system esp_timer nvs_flash esp_hw_support efuse)

if(CONFIG_ESP_FOC_FITL)
list(APPEND srcs "source/motor_control/foc_in_the_loop/esp_foc_itl_plant.c"
"source/motor_control/foc_in_the_loop/esp_foc_in_the_loop.c")
list(APPEND includes "source/motor_control/foc_in_the_loop"
"source/drivers/foc_in_the_loop")
list(APPEND srcs "source/osal/os_interface_idf.c")

if(CONFIG_FOC_ITL_TICK_MCPWM)
list(APPEND srcs "source/drivers/foc_in_the_loop/esp_foc_itl_tick_mcpwm.c")
list(APPEND requires esp_driver_mcpwm)
elseif(CONFIG_FOC_ITL_TICK_GPTIMER)
list(APPEND srcs "source/drivers/foc_in_the_loop/esp_foc_itl_tick_gptimer.c")
list(APPEND requires esp_driver_gptimer)
endif()
else()
list(APPEND requires esp_driver_uart esp_driver_pcnt esp_driver_gpio esp_driver_mcpwm)
list(APPEND includes "source/drivers" "source/drivers/cali")
list(APPEND srcs "source/drivers/inverter_3pwm_mcpwm.c"
"source/drivers/inverter_6pwm_mcpwm.c"
"source/drivers/rotor_sensor_as5600.c"
"source/drivers/rotor_sensor_as5048.c"
"source/drivers/rotor_sensor_pcnt.c"
"source/drivers/rotor_sensor_simu.c"
"source/drivers/current_sensor_adc.c"
"source/drivers/esp_foc_adc_cali_lut.c"
"source/drivers/cali/esp_foc_adc_cali_curve.c"
"source/drivers/cali/esp_foc_adc_range_extend.c"
"source/osal/os_interface_idf.c")

if(target STREQUAL "esp32")
list(APPEND srcs "source/drivers/cali/esp_foc_adc_cali_line_esp32.c")
elseif(target STREQUAL "esp32s2")
list(APPEND srcs "source/drivers/cali/esp_foc_adc_cali_line_esp32s2.c")
elseif(target STREQUAL "esp32c2")
list(APPEND srcs "source/drivers/cali/esp_foc_adc_cali_line_esp32c2.c")
elseif(EXISTS "${CMAKE_CURRENT_LIST_DIR}/source/drivers/cali/coeff/esp_foc_curve_coeff_${target}.c")
list(APPEND srcs "source/drivers/cali/coeff/esp_foc_curve_coeff_${target}.c")
endif()

if(CONFIG_SOC_ETM_SUPPORTED)
list(APPEND srcs "source/drivers/isensor_adc_etm.c")
endif()

if(target STREQUAL "esp32")
list(APPEND srcs "source/drivers/isensor_adc_dma_esp32.c")
list(APPEND requires esp_driver_i2s)
elseif(target STREQUAL "esp32s2")
list(APPEND srcs "source/drivers/isensor_adc_dma_esp32s2.c")
list(APPEND requires esp_driver_spi)
else()
list(APPEND srcs "source/drivers/isensor_adc_dma_gdma.c")
list(APPEND requires esp_mm)
endif()
endif()

if(target STREQUAL "esp32s2" OR target STREQUAL "esp32s3" OR target STREQUAL "esp32p4")
list(APPEND requires espressif__esp_tinyusb)
endif()
list(APPEND includes "source/drivers")
list(APPEND srcs "source/drivers/inverter_3pwm_mcpwm.c"
"source/drivers/inverter_6pwm_mcpwm.c"
"source/drivers/rotor_sensor_as5600.c"
"source/drivers/rotor_sensor_as5048.c"
"source/drivers/rotor_sensor_pcnt.c"
"source/drivers/current_sensor_adc.c"
"source/drivers/current_sensor_adc_one_shot.c"
"source/osal/os_interface_idf.c")

if(CONFIG_ESP_FOC_TUNER_ENABLE)
list(APPEND srcs "source/motor_control/esp_foc_tuner.c")
list(APPEND srcs "source/gui_link/esp_foc_tuner.c"
"source/gui_link/esp_foc_link_session.c")
endif()
if(CONFIG_ESP_FOC_BRIDGE_UART)
list(APPEND srcs "source/drivers/esp_foc_bridge_uart.c")
list(APPEND srcs "source/drivers/gui_link/esp_foc_bridge_uart.c")
list(APPEND requires esp_driver_uart)
endif()
if(CONFIG_ESP_FOC_BRIDGE_USBCDC)
list(APPEND srcs "source/drivers/esp_foc_bridge_usbcdc.c")
if(target STREQUAL "esp32s2" OR target STREQUAL "esp32s3" OR target STREQUAL "esp32p4")
list(APPEND srcs "source/drivers/gui_link/esp_foc_bridge_usbcdc.c")
else()
list(APPEND srcs "source/drivers/gui_link/esp_foc_bridge_usb_serial_jtag.c")
list(APPEND requires esp_driver_usb_serial_jtag)
endif()
endif()

idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${includes}"
PRIV_REQUIRES "${requires}"
LDFRAGMENTS linker.lf)

# --- Build-time PI autotuning -------------------------------------------
# Generates esp_foc_autotuned_gains.h into the build dir (never the source
# tree) so it stays out of git automatically. The component code consumes
# the header behind CONFIG_ESP_FOC_USE_AUTOGEN_GAINS.
if(CONFIG_ESP_FOC_USE_AUTOGEN_GAINS)
idf_build_get_property(python PYTHON)
set(autogen_dir "${CMAKE_CURRENT_BINARY_DIR}/gen")
set(autogen_header "${autogen_dir}/esp_foc_autotuned_gains.h")
set(autogen_report "${autogen_dir}/esp_foc_autotuned_report.txt")
string(REPLACE "\"" "" motor_profile "${CONFIG_ESP_FOC_MOTOR_PROFILE}")
set(motor_profile_path
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/motors/${motor_profile}.json")
if(NOT EXISTS "${motor_profile_path}")
message(FATAL_ERROR
"espFoC autotuner: motor profile not found: ${motor_profile_path}\n"
"Add a JSON under scripts/motors/ or change CONFIG_ESP_FOC_MOTOR_PROFILE.")
endif()
add_custom_command(
OUTPUT ${autogen_header}
COMMAND ${CMAKE_COMMAND} -E make_directory ${autogen_dir}
COMMAND ${python} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_pi_gains.py
--motor ${motor_profile_path}
--output ${autogen_header}
--report ${autogen_report}
--pwm-rate-hz ${CONFIG_ESP_FOC_AUTOGEN_PWM_RATE_HZ}
--decimation ${CONFIG_ESP_FOC_AUTOGEN_DECIMATION}
DEPENDS ${motor_profile_path}
${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_pi_gains.py
COMMENT "espFoC: autotuning PI gains for profile '${motor_profile}'"
VERBATIM)
add_custom_target(esp_foc_autogen_gains DEPENDS ${autogen_header})
add_dependencies(${COMPONENT_LIB} esp_foc_autogen_gains)
target_include_directories(${COMPONENT_LIB} PRIVATE ${autogen_dir})
target_compile_definitions(${COMPONENT_LIB} PRIVATE ESP_FOC_AUTOGEN_GAINS_AVAILABLE=1)
endif()

endif()
Loading