Skip to content

Commit 6c8a230

Browse files
committed
v2
1 parent cc6306b commit 6c8a230

File tree

1,578 files changed

+104457
-116488
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,578 files changed

+104457
-116488
lines changed

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ The source code and [website](https://madflight.com/) have extensive documentati
1313

1414
<img src="extras/img/madflight RP2040 flight controller.jpeg" title="madflight RP2040 flight controller" width="38%" /> <img src="extras/img/madflight drone.jpeg" title="madflight drone" width="30%" /> <img src="extras/img/madflight ESP32 flight controller.jpeg" title="madflight ESP32 flight controller" width="29%" />
1515

16+
## Version 2.0.0-DEV
17+
18+
If you clone/download this repository you get version 2.0.0-DEV, which is BLEEDING EDGE - not flight tested at all, might not even compile, and will completely change in the next hour...
19+
20+
Use [release version 1.3.3](https://github.com/qqqlab/madflight/releases) if you want something that actually has logged flight hours.
21+
1622
## Required Hardware
1723

1824
- [Development board](https://madflight.com/Controller-Boards/):
@@ -44,10 +50,10 @@ The source code and [website](https://madflight.com/) have extensive documentati
4450
3. Open *Examples for custom libraries->madflight->Quadcopter.ino* in the Arduino IDE.
4551
4. Edit the HARDWARE section in madflight_config.h to enable the connected peripherals.
4652
5. If you're not using the default pinout then setup your board pinout in the CUSTOM PINS section.
47-
6. Compile Quadcopter.ino and upload it to your board. Connect the Serial Monitor at 115200 baud and check the messages. Type `help` to see the available CLI commands.
53+
6. Compile Quadcopter.ino and upload it to your board. Connect the Serial Monitor at 115200 baud and check the startup messages. Type `help` to see the available CLI commands.
4854
7. Type `calradio` and follow the prompts to setup your RC radio receiver.
4955
8. IMPORTANT: Use CLI `calimu` and `calmag` to calibate the sensors.
50-
9. Use CLI commands `pimu`, `pahrs`, `pradio`, `pmot`, etc. and check that IMU sensor, AHRS and RC Receiver are working correctly.
56+
9. Use CLI commands `pimu`, `pahr`, `prcl`, `pmot`, etc. and check that IMU sensor, AHRS and RC Receiver are working correctly.
5157
10. Connect motors (no props) and battery and check that motors are spinning correctly.
5258
11. Mount props, go to an wide open space, and FLY!
5359

@@ -88,7 +94,7 @@ By default **madflight** has these safety features enabled:
8894
- `rcin` RC INput, retrieves RC receiver data
8995
- `veh` Vehicle information
9096
- Most modules are interfaced through a global object, for example the `imu` object has property `imu.gx` which is the current gyro x-axis rate in degrees per second for the selected IMU chip.
91-
- The module implementations are in subdirectories of the `src/madflight` directory. Here you find the module header file, e.g. `src/madflight/imu/imu.h`, and the interface declaration `src/madflight/imu/imu_interface.h`
97+
- The module implementations are in subdirectories of the `src/madflight` directory. Here you find the module header file, e.g. `src/madflight/imu/imu.h`, and the interface declaration `src/madflight/imu/imu.h`
9298
- The module files are usually header only, that is, the header also includes the implemention.
9399

94100
## Disclaimer

examples/01.Quadcopter/01.Quadcopter.ino

Lines changed: 60 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@ Minimal quadcopter demo program for madflight Arduino ESP32-S3 / ESP32 / RP2350
66
77
See http://madflight.com for detailed description
88
9-
Needs:
10-
- IMU sensor (SPI or I2C)
11-
- RC receiver with 5 channels (CRSF/ELRS preferred)
12-
- 4 brushless motors with ESCs (or brushed motors with MOSFETs)
9+
Required Hardware
1310
14-
Connecting:
15-
IMU: connect IMU_EXTI, IMU_CS, SPI_MISO, SPI_MOSI, SPI_CLK for SPI (or IMU_EXTI, I2C_SDA, I2C_SCL for I2C)
16-
RC receiver: connect RCIN_RX to receiver TX pin
17-
ESCs: PWM1-4 to the ESC inputs
11+
IMU sensor (SPI or I2C)
12+
RC receiver with 5 channels (CRSF/ELRS preferred)
13+
4 brushless motors with ESCs
14+
15+
Connecting Hardware
16+
17+
SPI IMU: connect pin_imu_int, pin_imu_cs, pin_spi0_miso, pin_spi0_mosi, pin_spi0_sclk
18+
or for I2C IMU: connect pin_imu_int, pin_i2c1_scl, pin_i2c1_sda
19+
RC receiver: connect pin_ser0_rx to receiver TX pin
20+
ESCs: pin_out0 ... pin_out3 to the ESC inputs of motor1 ... motor4
1821
1922
Arming: Set throttle low, then flip arm switch from DISARMED to ARMED.
2023
Disarming: Flip arm switch from ARMED to DISARMED, at any throttle position. "Kill switch".
@@ -80,23 +83,26 @@ void setup() {
8083
//setup madflight components: Serial.begin(115200), imu, rcin, led, etc. See src/madflight/interface.h for full interface description of each component.
8184
madflight_setup();
8285

83-
//setup 4 motors for the quadcopter
84-
for(int i=0;i<4;i++) {
85-
//uncomment one line - sets pin, frequency (Hz), minimum (us), maximum (us)
86-
out.setupMotor(i, HW_PIN_OUT[i], 400, 950, 2000); //Standard PWM: 400Hz, 950-2000 us
87-
//out.setupMotor(i, (HW_PIN_OUT[i], 2000, 125, 250); //Oneshot125: 2000Hz, 125-250 us
88-
}
86+
// Uncomment ONE line - select output type
87+
int out_hz = 400; int min_us = 950; int max_us = 2000; //Standard PWM: 400Hz, 950-2000 us
88+
//int out_hz = 2000; int min_us = 125; int max_us = 250; //Oneshot125: 2000Hz, 125-250 us
89+
90+
// Setup 4 motors for the quadcopter
91+
out.setupMotor(0, cfg.pin_out0, out_hz, min_us, max_us);
92+
out.setupMotor(1, cfg.pin_out1, out_hz, min_us, max_us);
93+
out.setupMotor(2, cfg.pin_out2, out_hz, min_us, max_us);
94+
out.setupMotor(3, cfg.pin_out3, out_hz, min_us, max_us);
8995

9096
//set initial desired yaw
91-
yaw_desired = ahrs.yaw;
97+
yaw_desired = ahr.yaw;
9298
}
9399

94100
//========================================================================================================================//
95101
// LOOP() //
96102
//========================================================================================================================//
97103

98104
void loop() {
99-
cli.loop(); //process CLI commands
105+
cli.update(); //process CLI commands
100106
}
101107

102108
//========================================================================================================================//
@@ -108,17 +114,17 @@ void imu_loop() {
108114
//Blink LED
109115
led_Blink();
110116

111-
//Sensor fusion: update ahrs.roll, ahrs.pitch, and ahrs.yaw angle estimates (degrees) from IMU data
112-
ahrs.update();
117+
//Sensor fusion: update ahr.roll, ahr.pitch, and ahr.yaw angle estimates (degrees) from IMU data
118+
ahr.update();
113119

114120
//Get radio commands - Note: don't do this in loop() because loop() is a lower priority task than imu_loop(), so in worst case loop() will not get any processor time.
115-
rcin.update();
121+
rcl.update();
116122

117123
//PID Controller RATE or ANGLE
118124
#ifdef FLIGHTMODE_ANGLE
119-
control_Angle(rcin.throttle == 0); //Stabilize on pitch/roll angle setpoint, stabilize yaw on rate setpoint //control_Angle2(rcin_thro_is_low); //Stabilize on pitch/roll setpoint using cascaded method. Rate controller must be tuned well first!
125+
control_Angle(rcl.throttle == 0); //Stabilize on pitch/roll angle setpoint, stabilize yaw on rate setpoint //control_Angle2(rcin_thro_is_low); //Stabilize on pitch/roll setpoint using cascaded method. Rate controller must be tuned well first!
120126
#else
121-
control_Rate(rcin.throttle == 0); //Stabilize on rate setpoint
127+
control_Rate(rcl.throttle == 0); //Stabilize on rate setpoint
122128
#endif
123129

124130
//Updates out.arm, the output armed flag
@@ -133,13 +139,11 @@ void imu_loop() {
133139
//========================================================================================================================//
134140

135141
void led_Blink() {
136-
//Blink LED once per second, if LED blinks slower then the loop takes too much time, use print_loop_Rate() to investigate.
142+
//Blink LED once per second, if LED blinks slower then the loop takes too much time, use CLI 'pimu' to investigate.
137143
//DISARMED: long off, short on, ARMED: long on, short off
138-
if(imu.update_cnt % imu.getSampleRate() <= imu.getSampleRate() / 10) {
139-
led.set(!out.armed); //short interval
140-
}else{
141-
led.set(out.armed); //long interval
142-
}
144+
uint32_t modulus = imu.update_cnt % imu.getSampleRate();
145+
if( modulus == 0) led.set(!out.armed); //start of pulse
146+
if( modulus == imu.getSampleRate() / 10) led.set(out.armed); //end of pulse
143147
}
144148

145149
//returns angle in range -180 to 180
@@ -156,7 +160,7 @@ void control_Angle(bool zero_integrators) {
156160
//DESCRIPTION: Computes control commands based on angle error
157161
/*
158162
* Basic PID control to stablize on angle setpoint based on desired states roll_des, pitch_des, and yaw_des. Error
159-
* is simply the desired state minus the actual state (ex. roll_des - ahrs.roll). Two safety features
163+
* is simply the desired state minus the actual state (ex. roll_des - ahr.roll). Two safety features
160164
* are implimented here regarding the I terms. The I terms are saturated within specified limits on startup to prevent
161165
* excessive buildup. This can be seen by holding the vehicle at an angle and seeing the motors ramp up on one side until
162166
* they've maxed out throttle... saturating I to a specified limit fixes this. The second feature defaults the I terms to 0
@@ -169,9 +173,9 @@ void control_Angle(bool zero_integrators) {
169173
//outputs: PIDroll.PID, PIDpitch.PID, PIDyaw.PID
170174

171175
//desired values
172-
float roll_des = rcin.roll * maxRoll; //Between -maxRoll and +maxRoll
173-
float pitch_des = rcin.pitch * maxPitch; //Between -maxPitch and +maxPitch
174-
float yawRate_des = rcin.yaw * maxYawRate; //Between -maxYawRate roll_PIDand +maxYawRate
176+
float roll_des = rcl.roll * maxRoll; //Between -maxRoll and +maxRoll
177+
float pitch_des = rcl.pitch * maxPitch; //Between -maxPitch and +maxPitch
178+
float yawRate_des = rcl.yaw * maxYawRate; //Between -maxYawRate roll_PIDand +maxYawRate
175179

176180
//state vars
177181
static float integral_roll, integral_pitch, error_yawRate_prev, integral_yawRate;
@@ -184,35 +188,35 @@ void control_Angle(bool zero_integrators) {
184188
}
185189

186190
//Roll PID
187-
float error_roll = roll_des - ahrs.roll;
191+
float error_roll = roll_des - ahr.roll;
188192
integral_roll += error_roll * imu.dt;
189193
integral_roll = constrain(integral_roll, -i_limit, i_limit); //Saturate integrator to prevent unsafe buildup
190-
float derivative_roll = ahrs.gx;
194+
float derivative_roll = ahr.gx;
191195
PIDroll.PID = 0.01 * (Kp_ro_pi_angle*error_roll + Ki_ro_pi_angle*integral_roll - Kd_ro_pi_angle*derivative_roll); //Scaled by .01 to bring within -1 to 1 range
192196

193197
//Pitch PID
194-
float error_pitch = pitch_des - ahrs.pitch;
198+
float error_pitch = pitch_des - ahr.pitch;
195199
integral_pitch += error_pitch * imu.dt;
196200
integral_pitch = constrain(integral_pitch, -i_limit, i_limit); //Saturate integrator to prevent unsafe buildup
197-
float derivative_pitch = ahrs.gy;
201+
float derivative_pitch = ahr.gy;
198202
PIDpitch.PID = 0.01 * (Kp_ro_pi_angle*error_pitch + Ki_ro_pi_angle*integral_pitch - Kd_ro_pi_angle*derivative_pitch); //Scaled by .01 to bring within -1 to 1 range
199203

200204
//Yaw PID
201-
if(-0.02 < rcin.yaw && rcin.yaw < 0.02) {
205+
if(-0.02 < rcl.yaw && rcl.yaw < 0.02) {
202206
//on reset, set desired yaw to current yaw
203-
if(zero_integrators) yaw_desired = ahrs.yaw;
207+
if(zero_integrators) yaw_desired = ahr.yaw;
204208

205209
//Yaw stick centered: hold yaw_desired
206-
float error_yaw = degreeModulus(yaw_desired - ahrs.yaw);
210+
float error_yaw = degreeModulus(yaw_desired - ahr.yaw);
207211
float desired_yawRate = error_yaw / 0.5; //set desired yawRate such that it gets us to desired yaw in 0.5 second
208-
float derivative_yaw = desired_yawRate - ahrs.gz;
212+
float derivative_yaw = desired_yawRate - ahr.gz;
209213
PIDyaw.PID = 0.01 * (Kp_yaw_angle*error_yaw + Kd_yaw_angle*derivative_yaw); //Scaled by .01 to bring within -1 to 1 range
210214

211215
//update yaw rate controller
212216
error_yawRate_prev = 0;
213217
}else{
214218
//Yaw stick not centered: stablize on rate from GyroZ
215-
float error_yawRate = yawRate_des - ahrs.gz;
219+
float error_yawRate = yawRate_des - ahr.gz;
216220
integral_yawRate += error_yawRate * imu.dt;
217221
integral_yawRate = constrain(integral_yawRate, -i_limit, i_limit); //Saturate integrator to prevent unsafe buildup
218222
float derivative_yawRate = (error_yawRate - error_yawRate_prev) / imu.dt;
@@ -222,7 +226,7 @@ void control_Angle(bool zero_integrators) {
222226
error_yawRate_prev = error_yawRate;
223227

224228
//update yaw controller:
225-
yaw_desired = ahrs.yaw; //set desired yaw to current yaw, the yaw angle controller will hold this value
229+
yaw_desired = ahr.yaw; //set desired yaw to current yaw, the yaw angle controller will hold this value
226230
}
227231
}
228232

@@ -234,9 +238,9 @@ void control_Rate(bool zero_integrators) {
234238
//outputs: PIDroll.PID, PIDpitch.PID, PIDyaw.PID
235239

236240
//desired values
237-
float rollRate_des = rcin.roll * maxRollRate; //Between -maxRoll and +maxRoll
238-
float pitchRate_des = rcin.pitch * maxPitchRate; //Between -maxPitch and +maxPitch
239-
float yawRate_des = rcin.yaw * maxYawRate; //Between -maxYawRate and +maxYawRate
241+
float rollRate_des = rcl.roll * maxRollRate; //Between -maxRoll and +maxRoll
242+
float pitchRate_des = rcl.pitch * maxPitchRate; //Between -maxPitch and +maxPitch
243+
float yawRate_des = rcl.yaw * maxYawRate; //Between -maxYawRate and +maxYawRate
240244

241245
//state vars
242246
static float integral_roll, error_roll_prev;
@@ -251,21 +255,21 @@ void control_Rate(bool zero_integrators) {
251255
}
252256

253257
//Roll
254-
float error_roll = rollRate_des - ahrs.gx;
258+
float error_roll = rollRate_des - ahr.gx;
255259
integral_roll += error_roll * imu.dt;
256260
integral_roll = constrain(integral_roll, -i_limit, i_limit); //Saturate integrator to prevent unsafe buildup
257261
float derivative_roll = (error_roll - error_roll_prev) / imu.dt;
258262
PIDroll.PID = 0.01 * (Kp_ro_pi_rate*error_roll + Ki_ro_pi_rate*integral_roll + Kd_ro_pi_rate*derivative_roll); //Scaled by .01 to bring within -1 to 1 range
259263

260264
//Pitch
261-
float error_pitch = pitchRate_des - ahrs.gy;
265+
float error_pitch = pitchRate_des - ahr.gy;
262266
integral_pitch += error_pitch * imu.dt;
263267
integral_pitch = constrain(integral_pitch, -i_limit, i_limit); //Saturate integrator to prevent unsafe buildup
264268
float derivative_pitch = (error_pitch - error_pitch_prev) / imu.dt;
265269
PIDpitch.PID = 0.01 * (Kp_ro_pi_rate*error_pitch + Ki_ro_pi_rate*integral_pitch + Kd_ro_pi_rate*derivative_pitch); //Scaled by .01 to bring within -1 to 1 range
266270

267271
//Yaw, stablize on rate from GyroZ
268-
float error_yaw = yawRate_des - ahrs.gz;
272+
float error_yaw = yawRate_des - ahr.gz;
269273
integral_yaw += error_yaw * imu.dt;
270274
integral_yaw = constrain(integral_yaw, -i_limit, i_limit); //Saturate integrator to prevent unsafe buildup
271275
float derivative_yaw = (error_yaw - error_yaw_prev) / imu.dt;
@@ -281,25 +285,22 @@ void out_KillSwitchAndFailsafe() {
281285
static bool rcin_arm_prev = true; //initial value is true: forces out.armed false on startup even if arm switch is ON
282286

283287
//Change to ARMED when throttle is zero and radio armed switch was flipped from disamed to armed position
284-
if (!out.armed && rcin.throttle == 0 && rcin.arm && !rcin_arm_prev) {
288+
if (!out.armed && rcl.throttle == 0 && rcl.arm && !rcin_arm_prev) {
285289
out.armed = true;
286290
Serial.println("OUT: ARMED");
287-
bb.start(); //start blackbox logging
288291
}
289292

290293
//Change to DISARMED when radio armed switch is in disarmed position, or if radio lost connection
291-
if (out.armed && (!rcin.arm || !rcin.connected())) {
294+
if (out.armed && (!rcl.arm || !rcl.connected())) {
292295
out.armed = false;
293-
if(!rcin.arm) {
296+
if(!rcl.arm) {
294297
Serial.println("OUT: DISARMED");
295-
bb.stop(); //stop blackbox logging
296298
}else{
297299
Serial.println("OUT: DISARMED due to lost radio connection");
298-
//keep on logging to document the crash...
299300
}
300301
}
301302

302-
rcin_arm_prev = rcin.arm;
303+
rcin_arm_prev = rcl.arm;
303304
}
304305

305306
void out_Mixer() {
@@ -308,13 +309,13 @@ void out_Mixer() {
308309
* Takes PIDroll.PID, PIDpitch.PID, and PIDyaw.PID computed from the PID controller and appropriately mixes them for the desired
309310
* vehicle configuration. For example on a quadcopter, the left two motors should have +PIDroll.PID while the right two motors
310311
* should have -PIDroll.PID. Front two should have +PIDpitch.PID and the back two should have -PIDpitch.PID etc... every motor has
311-
* normalized (0 to 1) rcin.throttle command for throttle control. Can also apply direct unstabilized commands from the transmitter with
312-
* rcin.xxx variables are to be sent to the motor ESCs and servos.
312+
* normalized (0 to 1) rcl.throttle command for throttle control. Can also apply direct unstabilized commands from the transmitter with
313+
* rcl.xxx variables are to be sent to the motor ESCs and servos.
313314
*
314315
*Relevant variables:
315-
*rcin.throtle - direct thottle control
316+
*rcl.throtle - direct thottle control
316317
*PIDroll.PID, PIDpitch.PID, PIDyaw.PID - stabilized axis variables
317-
*rcin.roll, rcin.pitch, rcin.yaw - direct unstabilized command passthrough
318+
*rcl.roll, rcl.pitch, rcl.yaw - direct unstabilized command passthrough
318319
*/
319320
/*
320321
Motor order diagram (Betaflight order)
@@ -336,7 +337,7 @@ Yaw right (CCW+ CW-) -++-
336337

337338
// IMPORTANT: This is a safety feature to remind the pilot to disarm.
338339
// Set throttle to at least armed_min_throttle, to keep at least one prop spinning when armed. The [out] module will disable motors when out.armed == false
339-
float thr = armed_min_throttle + (1 - armed_min_throttle) * rcin.throttle; //shift throttle range from [0.0 .. 1.0] to [armed_min_throttle .. 1.0]
340+
float thr = armed_min_throttle + (1 - armed_min_throttle) * rcl.throttle; //shift throttle range from [0.0 .. 1.0] to [armed_min_throttle .. 1.0]
340341

341342
//Quad mixing
342343
out.set(0, thr - PIDpitch.PID - PIDroll.PID - PIDyaw.PID); //M1 Back Right CW

0 commit comments

Comments
 (0)