Guest User

flight_code

a guest
Sep 12th, 2025
55
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 20.14 KB | Source Code | 0 0
  1. #include <Wire.h>
  2. #include <Adafruit_BMP280.h>
  3. #include "driver/ledc.h"
  4.  
  5. // ========== HARDWARE CONFIGURATION ==========
  6. // MPU9250 (IMU) - I2C0
  7. #define MPU_ADDR 0x68
  8. #define AK8963_ADDR 0x0C
  9. #define I2C_SDA 21
  10. #define I2C_SCL 22
  11.  
  12. // MPU9250 registers
  13. #define PWR_MGMT_1 0x6B
  14. #define SMPLRT_DIV 0x19
  15. #define CONFIG_REG 0x1A
  16. #define GYRO_CONFIG 0x1B
  17. #define ACCEL_CONFIG 0x1C
  18. #define ACCEL_CONFIG2 0x1D
  19. #define INT_PIN_CFG 0x37
  20. #define INT_ENABLE 0x38
  21. #define ACCEL_XOUT_H 0x3B
  22. #define TEMP_OUT_H 0x41
  23. #define GYRO_XOUT_H 0x43
  24. #define USER_CTRL 0x6A
  25. #define I2C_MST_CTRL 0x24
  26. #define I2C_SLV0_ADDR 0x25
  27. #define I2C_SLV0_REG 0x26
  28. #define I2C_SLV0_CTRL 0x27
  29. #define I2C_MST_STATUS 0x36
  30. #define I2C_SLV0_DO 0x63
  31. #define WHO_AM_I_MPU 0x75
  32.  
  33. // AK8963 registers
  34. #define AK8963_WHO_AM_I 0x00
  35. #define AK8963_ST1 0x02
  36. #define AK8963_HXL 0x03
  37. #define AK8963_CNTL1 0x0A
  38. #define AK8963_ASAX 0x10
  39.  
  40. // Scale factors
  41. float accelScale = 2.0f / 32768.0f; // g/LSB for ±2g
  42. float gyroScale = 250.0f / 32768.0f; // dps/LSB for ±250 dps
  43. float magAdj[3] = {1.0f, 1.0f, 1.0f}; // ASA adjustment from AK8963 fuse ROM
  44. float magScale = 4912.0f / 32760.0f; // µT/LSB for 16-bit, 0x16 mode
  45.  
  46. // BMP280 (Barometer) - I2C1
  47. #define I2C1_SDA 32
  48. #define I2C1_SCL 33
  49. TwoWire I2C_1 = TwoWire(1);
  50. Adafruit_BMP280 bmp(&I2C_1);
  51.  
  52. // Control pins
  53. #define ARM_SWITCH_PIN 12
  54. #define LED_RED 26
  55. #define LED_GREEN 27
  56. #define CALIB_PIN 0
  57.  
  58. // PWM Configuration
  59. const uint32_t PWM_FREQ_HZ = 50;
  60. const uint8_t PWM_RES_BITS = 16;
  61. const uint32_t PWM_MAX_DUTY = (1UL << PWM_RES_BITS) - 1;
  62. const uint16_t MIN_US = 1000;
  63. const uint16_t MAX_US = 2000;
  64. const uint32_t PERIOD_US = 1000000UL / PWM_FREQ_HZ;
  65.  
  66. // ========== MOTOR COORDINATION FIX - RAISED THROTTLE THRESHOLDS ==========
  67. const uint16_t THROTTLE_DEADBAND = 1200;      // Raised from 1100 to 1200µs
  68. const uint16_t MOTOR_IDLE_SPEED = 1200;       // Minimum motor speed when armed
  69. const uint16_t MOTOR_MIN_THROTTLE = 1200;    // Minimum throttle for motor calculations
  70.  
  71. // ========== STRUCTURES ==========
  72. struct RcChannel {
  73.     uint8_t pin;
  74.     volatile uint32_t riseTime;
  75.     volatile uint16_t pulseWidth;
  76.     volatile bool newValue;
  77.     uint16_t minVal;
  78.     uint16_t maxVal;
  79.     uint16_t centerVal;
  80.     uint32_t lastUpdate;
  81. };
  82.  
  83. // RC CALIBRATION VALUES UPDATED FROM YOUR CALIBRATION
  84. RcChannel channels[6] = {
  85.     // pin, riseTime, pulseWidth (center), newValue, minVal, maxVal, centerVal, lastUpdate
  86.     {34, 0, 1575, false, 1160, 1990, 1575, 0}, // CH1 - Roll
  87.     {35, 0, 1407, false, 1012, 1803, 1407, 0}, // CH2 - Pitch
  88.     {4, 0, 1452, false, 1044, 1861, 1452, 0}, // CH3 - Throttle
  89.     {2, 0, 1455, false, 1063, 1848, 1455, 0}, // CH4 - Yaw
  90.     {15, 0, 1188, false, 1186, 1191, 1188, 0}, // CH5 - Mode
  91.     {14, 0, 1013, false, 1011, 1016, 1013, 0} // CH6 - Aux
  92. };
  93.  
  94. struct EscOut {
  95.     int pin;
  96.     ledc_channel_t channel;
  97.     ledc_timer_t timer;
  98.     ledc_mode_t mode;
  99. };
  100.  
  101. EscOut escOuts[6] = {
  102.     {5, LEDC_CHANNEL_0, LEDC_TIMER_0, LEDC_LOW_SPEED_MODE},
  103.     {18, LEDC_CHANNEL_1, LEDC_TIMER_1, LEDC_LOW_SPEED_MODE},
  104.     {23, LEDC_CHANNEL_2, LEDC_TIMER_2, LEDC_LOW_SPEED_MODE},
  105.     {27, LEDC_CHANNEL_3, LEDC_TIMER_3, LEDC_LOW_SPEED_MODE}, // GPIO27 for consistency
  106.     {19, LEDC_CHANNEL_4, LEDC_TIMER_0, LEDC_HIGH_SPEED_MODE},
  107.     {25, LEDC_CHANNEL_5, LEDC_TIMER_1, LEDC_HIGH_SPEED_MODE}
  108. };
  109.  
  110. enum FlightMode {
  111.     STABILIZE = 0,
  112.     ALTITUDE_HOLD = 1,
  113.     ACRO = 2
  114. };
  115.  
  116. struct FlightState {
  117.     bool armed = false;
  118.     FlightMode mode = STABILIZE;
  119.     float roll = 0, pitch = 0, yaw = 0;
  120.     float rollRate = 0, pitchRate = 0, yawRate = 0;
  121.     float altitude = 0;
  122.     float targetAltitude = 0;
  123.     uint16_t throttle = 1000;
  124.     uint32_t lastUpdate = 0;
  125.     uint32_t lastRcUpdate = 0;
  126.     bool failsafe = false;
  127. } flight;
  128.  
  129. struct PIDController {
  130.     float kp, ki, kd;
  131.     float integral = 0;
  132.     float lastError = 0;
  133.     float lastTime = 0;
  134.     float integralMax = 100;
  135.    
  136.     // Updated throttle threshold for I-term reset
  137.     float calculate(float setpoint, float measured, uint16_t throttle) {
  138.         float now = millis();
  139.         float dt = (now - lastTime) / 1000.0;
  140.         if (dt <= 0 || dt > 0.1) dt = 0.01;
  141.        
  142.         float error = setpoint - measured;
  143.         integral += error * dt;
  144.        
  145.         // If throttle is below new deadband, reset the integral term
  146.         if (throttle < THROTTLE_DEADBAND) {
  147.             integral = 0;
  148.         }
  149.        
  150.         integral = constrain(integral, -integralMax, integralMax);
  151.         float derivative = (error - lastError) / dt;
  152.         float output = kp * error + ki * integral + kd * derivative;
  153.        
  154.         lastError = error;
  155.         lastTime = now;
  156.        
  157.         return output;
  158.     }
  159.    
  160.     // Original calculate function (for Yaw and Altitude which don't need the throttle check)
  161.     float calculate(float setpoint, float measured) {
  162.         return calculate(setpoint, measured, 1500);
  163.     }
  164.    
  165.     void reset() {
  166.         integral = 0;
  167.         lastError = 0;
  168.         lastTime = millis();
  169.     }
  170. };
  171.  
  172. // Conservative PID gains for safer testing
  173. PIDController rollPID = {0.5, 0.02, 0.1};
  174. PIDController pitchPID = {0.5, 0.02, 0.1};
  175. PIDController yawPID = {1.0, 0.05, 0.05};
  176. PIDController rollRatePID = {0.8, 0.2, 0.01};
  177. PIDController pitchRatePID = {0.8, 0.2, 0.01};
  178. PIDController yawRatePID = {1.0, 0.2, 0.01};
  179. PIDController altitudePID = {1.5, 0.1, 0.8};
  180.  
  181. // Sensor variables
  182. float ax, ay, az, gx, gy, gz, tempC, mx, my, mz;
  183.  
  184. // SENSOR OFFSETS UPDATED FROM YOUR CALIBRATION
  185. float axOffset = -0.0492, ayOffset = 0.0747, azOffset = 0.0326;
  186. float gxOffset = 2.7156, gyOffset = 1.3944, gzOffset = -0.1386;
  187.  
  188. // ========== SENSOR FUNCTIONS ==========
  189. void i2cWriteByte(uint8_t addr, uint8_t reg, uint8_t data) {
  190.     Wire.beginTransmission(addr);
  191.     Wire.write(reg);
  192.     Wire.write(data);
  193.     Wire.endTransmission(true);
  194. }
  195.  
  196. void i2cReadBytes(uint8_t addr, uint8_t reg, uint8_t count, uint8_t* dest) {
  197.     Wire.beginTransmission(addr);
  198.     Wire.write(reg);
  199.     Wire.endTransmission(false);
  200.     Wire.requestFrom((int)addr, (int)count, (int)true);
  201.     for (int i = 0; i < count && Wire.available(); i++) {
  202.         dest[i] = Wire.read();
  203.     }
  204. }
  205.  
  206. int16_t read16(uint8_t addr, uint8_t regHigh) {
  207.     uint8_t buf[2];
  208.     i2cReadBytes(addr, regHigh, 2, buf);
  209.     return (int16_t)((buf[0] << 8) | buf[1]);
  210. }
  211.  
  212. void ak8963WriteReg(uint8_t reg, uint8_t val) {
  213.     i2cWriteByte(MPU_ADDR, I2C_SLV0_ADDR, AK8963_ADDR);
  214.     i2cWriteByte(MPU_ADDR, I2C_SLV0_REG, reg);
  215.     i2cWriteByte(MPU_ADDR, I2C_SLV0_DO, val);
  216.     i2cWriteByte(MPU_ADDR, I2C_SLV0_CTRL, 0x81);
  217.     delay(10);
  218. }
  219.  
  220. void mpuSetupI2CMaster() {
  221.     i2cWriteByte(MPU_ADDR, USER_CTRL, 0x20);
  222.     i2cWriteByte(MPU_ADDR, I2C_MST_CTRL, 0x0D);
  223.     delay(10);
  224. }
  225.  
  226. void ak8963Init() {
  227.     ak8963WriteReg(AK8963_CNTL1, 0x00);
  228.     delay(20);
  229.     ak8963WriteReg(AK8963_CNTL1, 0x0F);
  230.     delay(20);
  231.    
  232.     i2cWriteByte(MPU_ADDR, I2C_SLV0_ADDR, 0x80 | AK8963_ADDR);
  233.     i2cWriteByte(MPU_ADDR, I2C_SLV0_REG, AK8963_ASAX);
  234.     i2cWriteByte(MPU_ADDR, I2C_SLV0_CTRL, 0x83);
  235.     delay(20);
  236.    
  237.     uint8_t ext[3] = {0};
  238.     i2cReadBytes(MPU_ADDR, 0x49, 3, ext);
  239.    
  240.     for (int i = 0; i < 3; i++) {
  241.         magAdj[i] = ((float)ext[i] - 128.0f) / 256.0f + 1.0f;
  242.     }
  243.    
  244.     ak8963WriteReg(AK8963_CNTL1, 0x00);
  245.     delay(20);
  246.     ak8963WriteReg(AK8963_CNTL1, 0x16);
  247.     delay(20);
  248.    
  249.     i2cWriteByte(MPU_ADDR, I2C_SLV0_ADDR, 0x80 | AK8963_ADDR);
  250.     i2cWriteByte(MPU_ADDR, I2C_SLV0_REG, AK8963_HXL);
  251.     i2cWriteByte(MPU_ADDR, I2C_SLV0_CTRL, 0x87);
  252. }
  253.  
  254. bool mpuInit() {
  255.     i2cWriteByte(MPU_ADDR, PWR_MGMT_1, 0x01);
  256.     delay(100);
  257.    
  258.     i2cWriteByte(MPU_ADDR, CONFIG_REG, 0x03);
  259.     i2cWriteByte(MPU_ADDR, SMPLRT_DIV, 9);
  260.     i2cWriteByte(MPU_ADDR, GYRO_CONFIG, 0x00);
  261.     i2cWriteByte(MPU_ADDR, ACCEL_CONFIG, 0x00);
  262.     i2cWriteByte(MPU_ADDR, ACCEL_CONFIG2, 0x03);
  263.    
  264.     mpuSetupI2CMaster();
  265.     ak8963Init();
  266.    
  267.     uint8_t who = 0;
  268.     i2cReadBytes(MPU_ADDR, WHO_AM_I_MPU, 1, &who);
  269.     Serial.print("MPU WHO_AM_I: 0x"); Serial.println(who, HEX);
  270.    
  271.     return true;
  272. }
  273.  
  274. void readAccelGyro(float& ax, float& ay, float& az, float& gx, float& gy, float& gz, float& tempC) {
  275.     uint8_t buf[14];
  276.     i2cReadBytes(MPU_ADDR, ACCEL_XOUT_H, 14, buf);
  277.    
  278.     int16_t axraw = (int16_t)((buf[0] << 8) | buf[1]);
  279.     int16_t ayraw = (int16_t)((buf[2] << 8) | buf[3]);
  280.     int16_t azraw = (int16_t)((buf[4] << 8) | buf[5]);
  281.     int16_t traw = (int16_t)((buf[6] << 8) | buf[7]);
  282.     int16_t gxraw = (int16_t)((buf[8] << 8) | buf[9]);
  283.     int16_t gyraw = (int16_t)((buf[10] << 8) | buf[11]);
  284.     int16_t gzraw = (int16_t)((buf[12] << 8) | buf[13]);
  285.    
  286.     ax = axraw * accelScale;
  287.     ay = ayraw * accelScale;
  288.     az = azraw * accelScale;
  289.     gx = gxraw * gyroScale;
  290.     gy = gyraw * gyroScale;
  291.     gz = gzraw * gyroScale;
  292.     tempC = ((float)traw) / 333.87f + 21.0f;
  293. }
  294.  
  295. bool readMag(float& mx, float& my, float& mz) {
  296.     uint8_t ext[8] = {0};
  297.     i2cReadBytes(MPU_ADDR, 0x49, 8, ext);
  298.    
  299.     if (!(ext[0] & 0x01)) return false;
  300.    
  301.     int16_t mxraw = (int16_t)((ext[2] << 8) | ext[1]);
  302.     int16_t myraw = (int16_t)((ext[4] << 8) | ext[3]);
  303.     int16_t mzraw = (int16_t)((ext[6] << 8) | ext[5]);
  304.    
  305.     mx = (float)mxraw * magAdj[0] * magScale;
  306.     my = (float)myraw * magAdj[1] * magScale;
  307.     mz = (float)mzraw * magAdj[2] * magScale;
  308.    
  309.     return true;
  310. }
  311.  
  312. // ========== RC INPUT HANDLING ==========
  313. void IRAM_ATTR onEdge(RcChannel &ch) {
  314.     int level = digitalRead(ch.pin);
  315.     uint32_t now = micros();
  316.    
  317.     if (level) {
  318.         ch.riseTime = now;
  319.     } else {
  320.         uint32_t width = now - ch.riseTime;
  321.         if (width >= 800 && width <= 2500) {
  322.             ch.pulseWidth = (uint16_t)width;
  323.             ch.newValue = true;
  324.             ch.lastUpdate = millis();
  325.         }
  326.     }
  327. }
  328.  
  329. void IRAM_ATTR handleChange0() { onEdge(channels[0]); }
  330. void IRAM_ATTR handleChange1() { onEdge(channels[1]); }
  331. void IRAM_ATTR handleChange2() { onEdge(channels[2]); }
  332. void IRAM_ATTR handleChange3() { onEdge(channels[3]); }
  333. void IRAM_ATTR handleChange4() { onEdge(channels[4]); }
  334. void IRAM_ATTR handleChange5() { onEdge(channels[5]); }
  335.  
  336. typedef void (*isr_t)();
  337. isr_t isrList[6] = {handleChange0, handleChange1, handleChange2, handleChange3, handleChange4, handleChange5};
  338.  
  339. void setupChannelPin(uint8_t idx) {
  340.     pinMode(channels[idx].pin, INPUT);
  341.     attachInterrupt(digitalPinToInterrupt(channels[idx].pin), isrList[idx], CHANGE);
  342. }
  343.  
  344. // ========== ESC/PWM FUNCTIONS ==========
  345. uint32_t usToDuty(uint16_t us) {
  346.     if (us < MIN_US) us = MIN_US;
  347.     if (us > MAX_US) us = MAX_US;
  348.     return (uint32_t)((uint64_t)us * PWM_MAX_DUTY / PERIOD_US);
  349. }
  350.  
  351. void setupPwmOutputs() {
  352.     bool timerConfigured[2][4] = {};
  353.    
  354.     for (int i = 0; i < 6; i++) {
  355.         int modeIdx = (escOuts[i].mode == LEDC_LOW_SPEED_MODE) ? 0 : 1;
  356.        
  357.         if (!timerConfigured[modeIdx][escOuts[i].timer]) {
  358.             ledc_timer_config_t tcfg = {
  359.                 .speed_mode = escOuts[i].mode,
  360.                 .duty_resolution = (ledc_timer_bit_t)PWM_RES_BITS,
  361.                 .timer_num = escOuts[i].timer,
  362.                 .freq_hz = PWM_FREQ_HZ,
  363.                 .clk_cfg = LEDC_AUTO_CLK
  364.             };
  365.             ledc_timer_config(&tcfg);
  366.             timerConfigured[modeIdx][escOuts[i].timer] = true;
  367.         }
  368.        
  369.         pinMode(escOuts[i].pin, OUTPUT);
  370.         ledc_channel_config_t ccfg = {
  371.             .gpio_num = escOuts[i].pin,
  372.             .speed_mode = escOuts[i].mode,
  373.             .channel = escOuts[i].channel,
  374.             .intr_type = LEDC_INTR_DISABLE,
  375.             .timer_sel = escOuts[i].timer,
  376.             .duty = usToDuty(MIN_US),
  377.             .hpoint = 0
  378.         };
  379.         ledc_channel_config(&ccfg);
  380.     }
  381. }
  382.  
  383. void writeEscPulse(int motorIndex, uint16_t us) {
  384.     if (motorIndex < 0 || motorIndex >= 6) return;
  385.     uint32_t duty = usToDuty(us);
  386.     ledc_set_duty(escOuts[motorIndex].mode, escOuts[motorIndex].channel, duty);
  387.     ledc_update_duty(escOuts[motorIndex].mode, escOuts[motorIndex].channel);
  388. }
  389.  
  390. void writeEscPulseAll(uint16_t us) {
  391.     for (int i = 0; i < 6; i++) {
  392.         writeEscPulse(i, us);
  393.     }
  394. }
  395.  
  396. // ========== FLIGHT CONTROL FUNCTIONS ==========
  397. // Updated motor mixing with higher minimum throttle and idle speed
  398. void mixMotors(uint16_t throttle, float rollCorrection, float pitchCorrection, float yawCorrection, uint16_t motors[6]) {
  399.     // CRITICAL SAFETY: If throttle below raised deadband, force all motors to minimum
  400.     if (throttle < THROTTLE_DEADBAND) {
  401.         for (int i = 0; i < 6; i++) {
  402.             motors[i] = 1000;
  403.         }
  404.         return;
  405.     }
  406.    
  407.     // Map throttle range to start at higher minimum
  408.     // Input throttle range: 1200-2000µs
  409.     // Output motor range: 1200-1750µs (75% max for safety)
  410.     uint16_t mappedThrottle = map(throttle, THROTTLE_DEADBAND, 2000, MOTOR_IDLE_SPEED, 1750);
  411.     mappedThrottle = constrain(mappedThrottle, MOTOR_IDLE_SPEED, 1750);
  412.    
  413.     // Hexacopter motor mixing matrix (+ configuration)
  414.     float mix[6][4] = {
  415.         {1.0, 0.0, 1.0, 1.0}, // Motor 1 (Front)
  416.         {1.0, 0.866, -0.5, -1.0}, // Motor 2 (Front Right)
  417.         {1.0, 0.866, -0.5, 1.0}, // Motor 3 (Back Right)
  418.         {1.0, 0.0, -1.0, -1.0}, // Motor 4 (Back)
  419.         {1.0, -0.866, -0.5, 1.0}, // Motor 5 (Back Left)
  420.         {1.0, -0.866, 0.5, -1.0} // Motor 6 (Front Left)
  421.     };
  422.    
  423.     for (int i = 0; i < 6; i++) {
  424.         float motorValue = mappedThrottle * mix[i][0] +
  425.                            rollCorrection * mix[i][1] +
  426.                            pitchCorrection * mix[i][2] +
  427.                            yawCorrection * mix[i][3];
  428.         // Ensure motors never go below idle speed when armed
  429.         motors[i] = constrain(motorValue, MOTOR_IDLE_SPEED, 1750);
  430.     }
  431. }
  432.  
  433. bool checkRcSignal() {
  434.     uint32_t now = millis();
  435.     for (int i = 0; i < 4; i++) {
  436.         if (now - channels[i].lastUpdate > 500) {
  437.             return false;
  438.         }
  439.         if (channels[i].pulseWidth < 800 || channels[i].pulseWidth > 2200) {
  440.             return false;
  441.         }
  442.     }
  443.     return true;
  444. }
  445.  
  446. // Updated arming conditions for new throttle deadband
  447. bool checkArmingConditions() {
  448.     if (channels[2].pulseWidth > THROTTLE_DEADBAND) return false;  // Use new deadband
  449.     if (!checkRcSignal()) return false;
  450.     if (abs(flight.rollRate) > 10 || abs(flight.pitchRate) > 10) return false;
  451.     return true;
  452. }
  453.  
  454. void handleArming() {
  455.     static uint32_t armingStart = 0;
  456.     static bool armingSequence = false;
  457.    
  458.     if (!checkRcSignal()) {
  459.         if (flight.armed) {
  460.             flight.failsafe = true;
  461.             Serial.println("FAILSAFE: RC signal lost!");
  462.         }
  463.         return;
  464.     } else {
  465.         flight.failsafe = false;
  466.     }
  467.    
  468.     if (!flight.armed) {
  469.         // Updated arming sequence for new throttle deadband
  470.         if (channels[2].pulseWidth < THROTTLE_DEADBAND && channels[3].pulseWidth > 1800) {
  471.             if (!armingSequence) {
  472.                 armingSequence = true;
  473.                 armingStart = millis();
  474.                 Serial.println("Arming sequence started...");
  475.             } else if (millis() - armingStart > 3000) {
  476.                 if (checkArmingConditions()) {
  477.                     flight.armed = true;
  478.                     digitalWrite(LED_RED, HIGH);
  479.                     rollPID.reset();
  480.                     pitchPID.reset();
  481.                     yawPID.reset();
  482.                     rollRatePID.reset();
  483.                     pitchRatePID.reset();
  484.                     yawRatePID.reset();
  485.                     altitudePID.reset();
  486.                     flight.targetAltitude = flight.altitude;
  487.                     Serial.println("🚁 ARMED! Motors at idle speed.");
  488.                 } else {
  489.                     Serial.println("❌ Arming failed - conditions not met");
  490.                 }
  491.                 armingSequence = false;
  492.             }
  493.         } else {
  494.             armingSequence = false;
  495.         }
  496.     } else {
  497.         // Updated disarming sequence for new throttle deadband
  498.         if (channels[2].pulseWidth < THROTTLE_DEADBAND && channels[3].pulseWidth < 1200) {
  499.             if (!armingSequence) {
  500.                 armingSequence = true;
  501.                 armingStart = millis();
  502.                 Serial.println("Disarming sequence started...");
  503.             } else if (millis() - armingStart > 2000) {
  504.                 flight.armed = false;
  505.                 flight.failsafe = false;
  506.                 digitalWrite(LED_RED, LOW);
  507.                 Serial.println("🛑 DISARMED!");
  508.                 armingSequence = false;
  509.             }
  510.         } else {
  511.             armingSequence = false;
  512.         }
  513.     }
  514. }
  515.  
  516. void updateFlightMode() {
  517.     uint16_t modeSwitch = channels[4].pulseWidth;
  518.     if (modeSwitch < 1300) {
  519.         flight.mode = STABILIZE;
  520.         digitalWrite(LED_GREEN, LOW);
  521.     } else if (modeSwitch < 1700) {
  522.         flight.mode = ALTITUDE_HOLD;
  523.         digitalWrite(LED_GREEN, HIGH);
  524.     } else {
  525.         flight.mode = ACRO;
  526.         digitalWrite(LED_GREEN, !digitalRead(LED_GREEN));
  527.     }
  528. }
  529.  
  530. void calculateAttitude() {
  531.     static float alpha = 0.98;
  532.     static uint32_t lastTime = micros();
  533.     uint32_t now = micros();
  534.     float dt = (now - lastTime) / 1000000.0;
  535.     if (dt > 0.1) dt = 0.01;
  536.    
  537.     flight.rollRate = gx - gxOffset;
  538.     flight.pitchRate = gy - gyOffset;
  539.     flight.yawRate = gz - gzOffset;
  540.    
  541.     flight.roll += flight.rollRate * dt;
  542.     flight.pitch += flight.pitchRate * dt;
  543.     flight.yaw += flight.yawRate * dt;
  544.    
  545.     if (flight.yaw > 180) flight.yaw -= 360;
  546.     if (flight.yaw < -180) flight.yaw += 360;
  547.    
  548.     float accRoll = atan2(ay - ayOffset, az - azOffset) * 180.0 / PI;
  549.     float accPitch = atan2(-(ax - axOffset), sqrt(pow(ay - ayOffset, 2) + pow(az - azOffset, 2))) * 180.0 / PI;
  550.    
  551.     flight.roll = alpha * flight.roll + (1.0 - alpha) * accRoll;
  552.     flight.pitch = alpha * flight.pitch + (1.0 - alpha) * accPitch;
  553.    
  554.     lastTime = now;
  555. }
  556.  
  557. // Enhanced flight control loop with higher throttle deadband
  558. void flightControlLoop() {
  559.     readAccelGyro(ax, ay, az, gx, gy, gz, tempC);
  560.     readMag(mx, my, mz);
  561.     flight.altitude = bmp.readAltitude(1013.25);
  562.    
  563.     calculateAttitude();
  564.     handleArming();
  565.     updateFlightMode();
  566.    
  567.     float desiredRoll = map(channels[0].pulseWidth, channels[0].minVal, channels[0].maxVal, -30, 30);
  568.     float desiredPitch = map(channels[1].pulseWidth, channels[1].minVal, channels[1].maxVal, -30, 30);
  569.     float desiredYawRate = map(channels[3].pulseWidth, channels[3].minVal, channels[3].maxVal, -100, 100);
  570.     uint16_t throttleInput = constrain(channels[2].pulseWidth, 1000, 2000);
  571.    
  572.     desiredRoll = constrain(desiredRoll, -45, 45);
  573.     desiredPitch = constrain(desiredPitch, -45, 45);
  574.     desiredYawRate = constrain(desiredYawRate, -180, 180);
  575.    
  576.     // Critical safety check with new deadband - motors maintain idle speed when armed
  577.     if (!flight.armed || flight.failsafe || throttleInput < THROTTLE_DEADBAND) {
  578.         writeEscPulseAll(1000);  // Stop all motors when disarmed or in failsafe
  579.         if (!flight.armed) {
  580.             rollPID.reset();
  581.             pitchPID.reset();
  582.             yawPID.reset();
  583.             rollRatePID.reset();
  584.             pitchRatePID.reset();
  585.             yawRatePID.reset();
  586.             altitudePID.reset();
  587.         }
  588.         flight.lastUpdate = millis();
  589.         return;
  590.     }
  591.    
  592.     float rollCorrection = 0, pitchCorrection = 0, yawCorrection = 0;
  593.     float altitudeCorrection = 0;
  594.    
  595.     switch (flight.mode) {
  596.         case STABILIZE:
  597.             rollCorrection = rollPID.calculate(desiredRoll, flight.roll, throttleInput);
  598.             pitchCorrection = pitchPID.calculate(desiredPitch, flight.pitch, throttleInput);
  599.             yawCorrection = yawRatePID.calculate(desiredYawRate, flight.yawRate);
  600.             break;
  601.            
  602.         case ALTITUDE_HOLD:
  603.             if (throttleInput > 1600) {
  604.                 flight.targetAltitude += 0.5;
  605.             } else if (throttleInput < 1400) {
  606.                 flight.targetAltitude -= 0.5;
  607.             }
  608.            
  609.             altitudeCorrection = altitudePID.calculate(flight.targetAltitude, flight.altitude
Advertisement
Add Comment
Please, Sign In to add comment