Skip to content

Commit 12a0760

Browse files
authored
Feature/motorgo support (#10)
* update to latest motorgo api * feat: add component for tinys3 test stand * feat: update to allow kconfig to switch between motorgo-mini and tinys3 test stand hardware. Simplified main code accordingly * update readme.
1 parent 8e967c1 commit 12a0760

File tree

7 files changed

+227
-101
lines changed

7 files changed

+227
-101
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ set(EXTRA_COMPONENT_DIRS
1212

1313
set(
1414
COMPONENTS
15-
"main esptool_py cli filters i2c task monitor mt6701 bldc_motor bldc_driver bldc_haptics"
15+
"main esptool_py cli filters bldc_haptics motorgo-mini tinys3_test_stand"
1616
CACHE STRING
1717
"List of components to include"
1818
)

README.md

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,31 @@ designed to enable running various experiments in openloop control, closed loop
55
control, and user interface / haptics using a small BLDC motor that's commonly
66
used in camera gimbals.
77

8+
The code supports running on the custom BLDC Test Stand (`TinyS3 Test Stand`),
9+
as well as on the `MotorGo-Mini`. You can select which hardware you want to run
10+
on via `menuconfig`.
11+
812
## Hardware
913

10-
* TinyS3
11-
* BLDC Motor (I used [these A and B motors](https://www.aliexpress.us/item/3256802907900422.html) from aliexpress)
12-
* MT6701 Magnetic Encoder Breakout (I used [this purple one (color B)](https://www.aliexpress.us/item/3256804851103272.html) from aliexpress)
13-
* TMC6300 BOB
14-
* Mini solderless breadboard
15-
* 3d printed test stand enclosure (.stl and source files in the [mcad](./mcad) directory)
16-
* Benchtop power supply (currently running at 5V 1A so many things should work)
14+
Either:
15+
* TinyS3 Test Stand:
16+
* TinyS3
17+
* BLDC Motor (I used [these A and B motors](https://www.aliexpress.us/item/3256802907900422.html) from aliexpress)
18+
* MT6701 Magnetic Encoder Breakout (I used [this purple one (color B)](https://www.aliexpress.us/item/3256804851103272.html) from aliexpress)
19+
* TMC6300 BOB
20+
* Mini solderless breadboard
21+
* 3d printed test stand enclosure (.stl and source files in the [mcad](./mcad) directory)
22+
* Benchtop power supply (currently running at 5V 1A so many things should work)
23+
or:
24+
* MotorGo-Mini
25+
26+
### Configure
27+
28+
```
29+
idf.py menuconfig
30+
```
1731

18-
:warning:
19-
> NOTE: you MUST make sure that you run this code with the
20-
> `zero_electrical_offset` value set to 0 (or not provided) at least once
21-
> otherwise the sample will not work and could potentially damage your motor.
32+
Select which hardware you are building and deploying to.
2233

2334
### Build and Flash
2435

@@ -82,7 +93,11 @@ You must run this calibration any time you change your hardware configuration
8293

8394
## Code Breakdown
8495

85-
This example is relatively complex, but builds complex haptic behavior using the
96+
This example encapsulates the board support packages into one of two components:
97+
* `espp::TinyS3TestStand` (in this repo)
98+
* `espp::MotorGoMini` (in espp)
99+
100+
These components build complex haptic behavior using the
86101
following components:
87102

88103
* `espp::Mt6701`
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
idf_component_register(
2+
INCLUDE_DIRS "include"
3+
REQUIRES base_component filters math mt6701 pid bldc_driver bldc_motor i2c
4+
)
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#pragma once
2+
3+
#pragma once
4+
5+
#include <string>
6+
#include <vector>
7+
8+
#include "base_component.hpp"
9+
#include "bldc_driver.hpp"
10+
#include "bldc_motor.hpp"
11+
#include "i2c.hpp"
12+
#include "mt6701.hpp"
13+
#include "simple_lowpass_filter.hpp"
14+
15+
namespace espp {
16+
/// This class acts as a board support component for the TinyS3 BLDC Test Stand.
17+
/// It provides a high-level interface to the system's functionality.
18+
///
19+
/// High level overview of the system
20+
/// - ESP32s3 module, using TinyS3
21+
/// - TMC6300-BOB motor driver on a breakout board
22+
/// - One MT6701 magnetic encoder connected via I2C
23+
class TinyS3TestStand : public BaseComponent {
24+
public:
25+
using Encoder = espp::Mt6701<>;
26+
using BldcMotor = espp::BldcMotor<espp::BldcDriver, Encoder>;
27+
28+
/// Constructor
29+
/// \param verbosity The verbosity level for the logger of the MotorGo-Mini
30+
/// and its components
31+
explicit TinyS3TestStand(espp::Logger::Verbosity verbosity = espp::Logger::Verbosity::WARN)
32+
: BaseComponent("TinyS3 Test Stand", verbosity) {
33+
init();
34+
}
35+
36+
/// Get a reference to the encoder
37+
/// \return A reference to the encoder
38+
Encoder &encoder() { return encoder_; }
39+
40+
/// Get a reference to the motor driver
41+
/// \return A reference to the motor driver
42+
espp::BldcDriver &motor_driver() { return motor_driver_; }
43+
44+
/// Get a reference to the motor
45+
/// \return A reference to the motor
46+
BldcMotor &motor() { return motor_; }
47+
48+
protected:
49+
static constexpr auto I2C_PORT = I2C_NUM_0;
50+
static constexpr auto I2C_SDA_PIN = GPIO_NUM_8;
51+
static constexpr auto I2C_SCL_PIN = GPIO_NUM_9;
52+
53+
static constexpr uint64_t core_update_period_us = 1000; // 1 ms
54+
55+
static constexpr auto MOTOR_A_H = GPIO_NUM_1;
56+
static constexpr auto MOTOR_A_L = GPIO_NUM_2;
57+
static constexpr auto MOTOR_B_H = GPIO_NUM_3;
58+
static constexpr auto MOTOR_B_L = GPIO_NUM_4;
59+
static constexpr auto MOTOR_C_H = GPIO_NUM_5;
60+
static constexpr auto MOTOR_C_L = GPIO_NUM_21;
61+
static constexpr auto MOTOR_ENABLE = GPIO_NUM_34;
62+
static constexpr auto MOTOR_FAULT = GPIO_NUM_36;
63+
64+
void init() {
65+
init_encoder();
66+
init_motor();
67+
}
68+
69+
void init_encoder() {
70+
bool run_task = true;
71+
std::error_code ec;
72+
encoder_.initialize(run_task, ec);
73+
if (ec) {
74+
logger_.error("Could not initialize encoder: {}", ec.message());
75+
}
76+
}
77+
78+
void init_motor() { motor_.initialize(); }
79+
80+
/// I2C bus for external communication
81+
I2c i2c_{{
82+
.port = I2C_PORT,
83+
.sda_io_num = I2C_SDA_PIN,
84+
.scl_io_num = I2C_SCL_PIN,
85+
.sda_pullup_en = GPIO_PULLUP_ENABLE,
86+
.scl_pullup_en = GPIO_PULLUP_ENABLE,
87+
.clk_speed = 1 * 1000 * 1000, // MT6701 supports 1 MHz I2C
88+
}};
89+
90+
// Encoder
91+
Encoder encoder_{
92+
{.write = std::bind(&espp::I2c::write, &i2c_, std::placeholders::_1, std::placeholders::_2,
93+
std::placeholders::_3),
94+
.read = std::bind(&espp::I2c::read, &i2c_, std::placeholders::_1, std::placeholders::_2,
95+
std::placeholders::_3),
96+
.update_period = std::chrono::duration<float>(core_update_period_us / 1e6f),
97+
.auto_init = false, // we have to initialize the SPI first before we can use the encoder
98+
.log_level = get_log_level()}};
99+
100+
// Driver
101+
espp::BldcDriver motor_driver_{{.gpio_a_h = MOTOR_A_H,
102+
.gpio_a_l = MOTOR_A_L,
103+
.gpio_b_h = MOTOR_B_H,
104+
.gpio_b_l = MOTOR_B_L,
105+
.gpio_c_h = MOTOR_C_H,
106+
.gpio_c_l = MOTOR_C_L,
107+
.gpio_enable = MOTOR_ENABLE,
108+
.gpio_fault = MOTOR_FAULT,
109+
.power_supply_voltage = 5.0f,
110+
.limit_voltage = 5.0f,
111+
.log_level = get_log_level()}};
112+
113+
// Filters
114+
espp::SimpleLowpassFilter motor_velocity_filter_{{.time_constant = 0.005f}};
115+
espp::SimpleLowpassFilter motor_angle_filter_{{.time_constant = 0.001f}};
116+
117+
// Motor
118+
BldcMotor motor_{{
119+
.num_pole_pairs = 7,
120+
.phase_resistance = 5.0f,
121+
.kv_rating = 320,
122+
.current_limit = 1.0f,
123+
.foc_type = espp::detail::FocType::SPACE_VECTOR_PWM,
124+
// create shared_ptr from raw pointer to ensure shared_ptr doesn't delete the object
125+
.driver =
126+
std::shared_ptr<espp::BldcDriver>(std::shared_ptr<espp::BldcDriver>{}, &motor_driver_),
127+
// create shared_ptr from raw pointer to ensure shared_ptr doesn't delete the object
128+
.sensor = std::shared_ptr<Encoder>(std::shared_ptr<Encoder>{}, &encoder_),
129+
.velocity_pid_config =
130+
{
131+
.kp = 0.010f,
132+
.ki = 1.000f,
133+
.kd = 0.000f,
134+
.integrator_min = -1.0f, // same scale as output_min (so same scale as current)
135+
.integrator_max = 1.0f, // same scale as output_max (so same scale as current)
136+
.output_min = -1.0, // velocity pid works on current (if we have phase resistance)
137+
.output_max = 1.0, // velocity pid works on current (if we have phase resistance)
138+
},
139+
.angle_pid_config =
140+
{
141+
.kp = 7.000f,
142+
.ki = 0.300f,
143+
.kd = 0.010f,
144+
.integrator_min = -10.0f, // same scale as output_min (so same scale as velocity)
145+
.integrator_max = 10.0f, // same scale as output_max (so same scale as velocity)
146+
.output_min = -20.0, // angle pid works on velocity (rad/s)
147+
.output_max = 20.0, // angle pid works on velocity (rad/s)
148+
},
149+
.velocity_filter = [this](auto v) { return motor_velocity_filter_(v); },
150+
.angle_filter = [this](auto v) { return motor_angle_filter_(v); },
151+
.auto_init = false, // we have to initialize the SPI first before we can use the encoder
152+
.log_level = get_log_level(),
153+
}};
154+
};
155+
} // namespace espp

main/Kconfig.projbuild

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
menu "Example Configuration"
2+
3+
choice EXAMPLE_HARDWARE
4+
prompt "Hardware"
5+
default EXAMPLE_HARDWARE_MOTORGO_MINI
6+
help
7+
Select the hardware to run this example on.
8+
9+
config EXAMPLE_HARDWARE_MOTORGO_MINI
10+
depends on IDF_TARGET_ESP32S3
11+
bool "MotorGo Mini"
12+
13+
config EXAMPLE_HARDWARE_TEST_STAND
14+
depends on IDF_TARGET_ESP32S3
15+
bool "BLDC Motor Test Stand (TinyS3)"
16+
17+
endchoice
18+
19+
endmenu

main/main.cpp

Lines changed: 20 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
#include <chrono>
22
#include <vector>
33

4-
#include "bldc_driver.hpp"
54
#include "bldc_haptics.hpp"
6-
#include "bldc_motor.hpp"
7-
#include "butterworth_filter.hpp"
85
#include "cli.hpp"
9-
#include "i2c.hpp"
10-
#include "lowpass_filter.hpp"
11-
#include "mt6701.hpp"
12-
#include "task.hpp"
6+
7+
#include "motorgo-mini.hpp"
8+
#include "tinys3_test_stand.hpp"
139

1410
using namespace std::chrono_literals;
1511

@@ -18,86 +14,23 @@ extern "C" void app_main(void) {
1814

1915
logger.info("Bootup");
2016

21-
// make the I2C that we'll use to communicate with the mt6701 (magnetic encoder)
22-
espp::I2c i2c({
23-
// pins for the bldc motor test stand with the TinyS3
24-
.port = I2C_NUM_1,
25-
.sda_io_num = GPIO_NUM_8,
26-
.scl_io_num = GPIO_NUM_9,
27-
.sda_pullup_en = GPIO_PULLUP_ENABLE,
28-
.scl_pullup_en = GPIO_PULLUP_ENABLE,
29-
});
30-
31-
// make the velocity filter
32-
static constexpr float core_update_period = 0.001f; // seconds
33-
34-
// now make the mt6701 which decodes the data
35-
using Encoder = espp::Mt6701<>;
36-
std::shared_ptr<Encoder> mt6701 = std::make_shared<Encoder>(
37-
Encoder::Config{.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1,
38-
std::placeholders::_2, std::placeholders::_3),
39-
.read = std::bind(&espp::I2c::read, &i2c, std::placeholders::_1,
40-
std::placeholders::_2, std::placeholders::_3),
41-
.update_period = std::chrono::duration<float>(core_update_period),
42-
.log_level = espp::Logger::Verbosity::WARN});
43-
44-
// now make the bldc driver
45-
std::shared_ptr<espp::BldcDriver> driver = std::make_shared<espp::BldcDriver>(
46-
espp::BldcDriver::Config{// this pinout is configured for the TinyS3 connected to the
47-
// TMC6300-BOB in the BLDC Motor Test Stand
48-
.gpio_a_h = 1,
49-
.gpio_a_l = 2,
50-
.gpio_b_h = 3,
51-
.gpio_b_l = 4,
52-
.gpio_c_h = 5,
53-
.gpio_c_l = 21,
54-
.gpio_enable = 34, // connected to the VIO/~Stdby pin of TMC6300-BOB
55-
.gpio_fault = 36, // connected to the nFAULT pin of TMC6300-BOB
56-
.power_supply_voltage = 5.0f,
57-
.limit_voltage = 5.0f,
58-
.log_level = espp::Logger::Verbosity::WARN});
59-
60-
// now make the bldc motor
61-
using BldcMotor = espp::BldcMotor<espp::BldcDriver, Encoder>;
62-
auto motor = BldcMotor(BldcMotor::Config{
63-
// measured by setting it into ANGLE_OPENLOOP and then counting how many
64-
// spots you feel when rotating it.
65-
.num_pole_pairs = 7,
66-
.phase_resistance =
67-
5.0f, // tested by running velocity_openloop and seeing if the veloicty is ~correct
68-
.kv_rating =
69-
320, // tested by running velocity_openloop and seeing if the velocity is ~correct
70-
.current_limit = 1.0f, // Amps
71-
.zero_electric_offset = 0.0f, // set to zero to always calibrate
72-
// and it will be logged.
73-
.sensor_direction =
74-
espp::detail::SensorDirection::UNKNOWN, // set to unknown to always calibrate
75-
.foc_type = espp::detail::FocType::SPACE_VECTOR_PWM,
76-
.driver = driver,
77-
.sensor = mt6701,
78-
.velocity_pid_config =
79-
{
80-
.kp = 0.010f,
81-
.ki = 1.000f,
82-
.kd = 0.000f,
83-
.integrator_min = -1.0f, // same scale as output_min (so same scale as current)
84-
.integrator_max = 1.0f, // same scale as output_max (so same scale as current)
85-
.output_min = -1.0, // velocity pid works on current (if we have phase resistance)
86-
.output_max = 1.0, // velocity pid works on current (if we have phase resistance)
87-
},
88-
.angle_pid_config =
89-
{
90-
.kp = 7.000f,
91-
.ki = 0.300f,
92-
.kd = 0.010f,
93-
.integrator_min = -10.0f, // same scale as output_min (so same scale as velocity)
94-
.integrator_max = 10.0f, // same scale as output_max (so same scale as velocity)
95-
.output_min = -20.0, // angle pid works on velocity (rad/s)
96-
.output_max = 20.0, // angle pid works on velocity (rad/s)
97-
},
98-
.log_level = espp::Logger::Verbosity::INFO});
99-
100-
using BldcHaptics = espp::BldcHaptics<BldcMotor>;
17+
#if CONFIG_EXAMPLE_HARDWARE_MOTORGO_MINI
18+
#pragma message("Using MotorGo Mini hardware configuration")
19+
logger.info("Using MotorGo Mini hardware configuration");
20+
// we don't want to init both motors, so we'll pass in auto_init=false
21+
espp::MotorGoMini motorgo_mini({.auto_init = false});
22+
motorgo_mini.init_motor_channel_1();
23+
auto &motor = motorgo_mini.motor1();
24+
using BldcHaptics = espp::BldcHaptics<espp::MotorGoMini::BldcMotor>;
25+
#elif CONFIG_EXAMPLE_HARDWARE_TEST_STAND
26+
#pragma message("Using TinyS3 Test Stand hardware configuration")
27+
logger.info("Using TinyS3 Test Stand hardware configuration");
28+
espp::TinyS3TestStand test_stand(espp::Logger::Verbosity::INFO);
29+
auto &motor = test_stand.motor();
30+
using BldcHaptics = espp::BldcHaptics<espp::TinyS3TestStand::BldcMotor>;
31+
#else
32+
#error "No hardware configuration selected"
33+
#endif
10134

10235
auto haptic_motor = BldcHaptics({.motor = motor,
10336
.kp_factor = 2,

0 commit comments

Comments
 (0)