-
Notifications
You must be signed in to change notification settings - Fork 12
/
arduino_DJI_03_RC_ARM.ino
346 lines (288 loc) · 11.3 KB
/
arduino_DJI_03_RC_ARM.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/************************************************************************************
@file arduino_DJI_03_RC_ARM.ino
@brief Send DJI Arm using XIAO Seeeduino over MSP to keep power level at full.
@author Richard Amiss
Release Note:
Complete rework for XIAO Seeeduino and MSP libraries. RC input is no longer used in
this version. The AU will simply arm a few seconds after being turned on, as long
as the goggles are on.
Code: Richard Amiss
Version: 1.1.0
Date: 06/24/23
Credits:
This software is based on and uses software published by 2022 David Payne:
QLiteOSD, which is based on work by Paul Kurucz (pkuruz):opentelem_to_bst_bridge
as well as software from d3ngit : djihdfpv_mavlink_to_msp_V2 and
crashsalot : VOT_to_DJIFPV
THIS SOFTWARE IS PROVIDED IN AN "AS IS" CONDITION. NO WARRANTIES, WHETHER EXPRESS,
IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE
COMPANY SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
************************************************************************************/
#include "MSP.h"
#include "MSP_OSD.h"
#include "OSD_positions_config.h"
#define ANALOG_IN A0 // Voltage Read pin (notice this is now Pin 0, instead of Pin 1)
#define VOLT_DIVIDER 48 // Set to 1024/full scale voltage
//#define DEBUG //uncomment to see diagnostics from USB serial
#define FC_FIRMWARE_NAME "Betaflight"
#define FC_FIRMWARE_IDENTIFIER "BTFL"
HardwareSerial &mspSerial = Serial1;
MSP msp;
// Arm Logic
uint32_t unarmedMillis = 3000; // number of milliseconds to wait before arming, after AU is initialized. Recommend at least 3000 (3 seconds).
//Voltage and Battery Reading
float averageVoltage = 0;
int sampleVoltageCount = 0;
int lastCount = 0;
boolean lightOn = true;
uint8_t *messageID;
void *payload;
uint8_t maxSize;
uint8_t *recvSize;
//Other
char fcVariant[5] = "BTFL";
char craftname[15] = "Craft";
uint32_t previousMillis_MSP = 0;
uint32_t activityDetectedMillis_MSP = 0;
bool activityDetected = false;
const uint32_t next_interval_MSP = 100;
uint32_t general_counter = next_interval_MSP;
uint32_t custom_mode = 0; //flight mode
uint8_t vbat = 0;
uint32_t flightModeFlags = 0x00000002;
uint8_t batteryCellCount = 3;
uint32_t previousFlightMode = custom_mode;
msp_battery_state_t battery_state = { 0 };
msp_name_t name = { 0 };
msp_fc_version_t fc_version = { 0 };
msp_fc_variant_t fc_variant = { 0 };
//msp_status_BF_t status_BF = {0};
msp_status_DJI_t status_DJI = { 0 };
msp_analog_t analog = { 0 };
void setup() {
#ifdef DEBUG
SerialUSB.begin(115200);
//while (!SerialUSB);
#endif
Serial1.begin(115200);
while (!Serial1);
analogReadResolution(12); // SAMD21 12 bit resolution 0 - 4096 range on Analog pin
msp.begin(mspSerial);
pinMode(LED_BUILTIN, OUTPUT);
delay(1000);
status_DJI.cycleTime = 0x0080;
status_DJI.i2cErrorCounter = 0;
status_DJI.sensor = 0x23;
status_DJI.configProfileIndex = 0;
status_DJI.averageSystemLoadPercent = 7;
status_DJI.accCalibrationAxisFlags = 0;
status_DJI.DJI_ARMING_DISABLE_FLAGS_COUNT = 20;
status_DJI.djiPackArmingDisabledFlags = (1 << 24);
flightModeFlags = 0x00000002;
#ifdef DEBUG
Serial.println("Initialized");
#endif
}
void loop() {
if (!activityDetected) {
#ifdef DEBUG
Serial.println("Waiting for AU...");
#endif
digitalWrite(LED_BUILTIN, LOW);
// Wait for Air Unit to send data
while(!msp.activityDetected()) {
};
activityDetected = true;
activityDetectedMillis_MSP = millis();
#ifdef DEBUG
Serial.println("AU Detected, waiting (unarmedMillis) time till arm");
#endif
}
digitalWrite(LED_BUILTIN, HIGH);
uint32_t currentMillis_MSP = millis();
if ((uint32_t)(currentMillis_MSP - previousMillis_MSP) >= next_interval_MSP) {
previousMillis_MSP = currentMillis_MSP;
if (general_counter % 300 == 0) { // update the altitude and voltage values every 300ms
getVoltageSample();
if (lightOn) {
digitalWrite(LED_BUILTIN, LOW);
} else {
digitalWrite(LED_BUILTIN, HIGH);
}
lightOn = !lightOn;
}
if (currentMillis_MSP < (activityDetectedMillis_MSP + unarmedMillis)) {
set_flight_mode_flags(false);
} else {
set_flight_mode_flags(true);
}
#ifdef DEBUG
//debugPrint();
#endif
send_msp_to_airunit();
general_counter += next_interval_MSP;
}
if (custom_mode != previousFlightMode) {
previousFlightMode = custom_mode;
display_flight_mode();
}
if (batteryCellCount == 0 && vbat > 0) {
set_battery_cells_number();
}
//display flight mode every 10s
if (general_counter % 10000 == 0) {
display_flight_mode();
}
}
msp_osd_config_t msp_osd_config = { 0 };
void send_osd_config() {
#ifdef IMPERIAL_UNITS
msp_osd_config.units = 0;
#else
msp_osd_config.units = 1;
#endif
msp_osd_config.osd_item_count = 56;
msp_osd_config.osd_stat_count = 24;
msp_osd_config.osd_timer_count = 2;
msp_osd_config.osd_warning_count = 16; // 16
msp_osd_config.osd_profile_count = 1; // 1
msp_osd_config.osdprofileindex = 1; // 1
msp_osd_config.overlay_radio_mode = 0; // 0
msp_osd_config.osd_rssi_value_pos = osd_rssi_value_pos;
msp_osd_config.osd_main_batt_voltage_pos = osd_main_batt_voltage_pos;
msp_osd_config.osd_crosshairs_pos = osd_crosshairs_pos;
msp_osd_config.osd_artificial_horizon_pos = osd_artificial_horizon_pos;
msp_osd_config.osd_horizon_sidebars_pos = osd_horizon_sidebars_pos;
msp_osd_config.osd_item_timer_1_pos = osd_item_timer_1_pos;
msp_osd_config.osd_item_timer_2_pos = osd_item_timer_2_pos;
msp_osd_config.osd_flymode_pos = osd_flymode_pos;
msp_osd_config.osd_craft_name_pos = osd_craft_name_pos;
msp_osd_config.osd_throttle_pos_pos = osd_throttle_pos_pos;
msp_osd_config.osd_vtx_channel_pos = osd_vtx_channel_pos;
msp_osd_config.osd_current_draw_pos = osd_current_draw_pos;
msp_osd_config.osd_mah_drawn_pos = osd_mah_drawn_pos;
msp_osd_config.osd_gps_speed_pos = osd_gps_speed_pos;
msp_osd_config.osd_gps_sats_pos = osd_gps_sats_pos;
msp_osd_config.osd_altitude_pos = osd_altitude_pos;
msp_osd_config.osd_roll_pids_pos = osd_roll_pids_pos;
msp_osd_config.osd_pitch_pids_pos = osd_pitch_pids_pos;
msp_osd_config.osd_yaw_pids_pos = osd_yaw_pids_pos;
msp_osd_config.osd_power_pos = osd_power_pos;
msp_osd_config.osd_pidrate_profile_pos = osd_pidrate_profile_pos;
msp_osd_config.osd_warnings_pos = osd_warnings_pos;
msp_osd_config.osd_avg_cell_voltage_pos = osd_avg_cell_voltage_pos;
msp_osd_config.osd_gps_lon_pos = osd_gps_lon_pos;
msp_osd_config.osd_gps_lat_pos = osd_gps_lat_pos;
msp_osd_config.osd_debug_pos = osd_debug_pos;
msp_osd_config.osd_pitch_angle_pos = osd_pitch_angle_pos;
msp_osd_config.osd_roll_angle_pos = osd_roll_angle_pos;
msp_osd_config.osd_main_batt_usage_pos = osd_main_batt_usage_pos;
msp_osd_config.osd_disarmed_pos = osd_disarmed_pos;
msp_osd_config.osd_home_dir_pos = osd_home_dir_pos;
msp_osd_config.osd_home_dist_pos = osd_home_dist_pos;
msp_osd_config.osd_numerical_heading_pos = osd_numerical_heading_pos;
msp_osd_config.osd_numerical_vario_pos = osd_numerical_vario_pos;
msp_osd_config.osd_compass_bar_pos = osd_compass_bar_pos;
msp_osd_config.osd_esc_tmp_pos = osd_esc_tmp_pos;
msp_osd_config.osd_esc_rpm_pos = osd_esc_rpm_pos;
msp_osd_config.osd_remaining_time_estimate_pos = osd_remaining_time_estimate_pos;
msp_osd_config.osd_rtc_datetime_pos = osd_rtc_datetime_pos;
msp_osd_config.osd_adjustment_range_pos = osd_adjustment_range_pos;
msp_osd_config.osd_core_temperature_pos = osd_core_temperature_pos;
msp_osd_config.osd_anti_gravity_pos = osd_anti_gravity_pos;
msp_osd_config.osd_g_force_pos = osd_g_force_pos;
msp_osd_config.osd_motor_diag_pos = osd_motor_diag_pos;
msp_osd_config.osd_log_status_pos = osd_log_status_pos;
msp_osd_config.osd_flip_arrow_pos = osd_flip_arrow_pos;
msp_osd_config.osd_link_quality_pos = osd_link_quality_pos;
msp_osd_config.osd_flight_dist_pos = osd_flight_dist_pos;
msp_osd_config.osd_stick_overlay_left_pos = osd_stick_overlay_left_pos;
msp_osd_config.osd_stick_overlay_right_pos = osd_stick_overlay_right_pos;
msp_osd_config.osd_display_name_pos = osd_display_name_pos;
msp_osd_config.osd_esc_rpm_freq_pos = osd_esc_rpm_freq_pos;
msp_osd_config.osd_rate_profile_name_pos = osd_rate_profile_name_pos;
msp_osd_config.osd_pid_profile_name_pos = osd_pid_profile_name_pos;
msp_osd_config.osd_profile_name_pos = osd_profile_name_pos;
msp_osd_config.osd_rssi_dbm_value_pos = osd_rssi_dbm_value_pos;
msp_osd_config.osd_rc_channels_pos = osd_rc_channels_pos;
msp.send(MSP_OSD_CONFIG, &msp_osd_config, sizeof(msp_osd_config));
}
void invert_pos(uint16_t *pos1, uint16_t *pos2) {
uint16_t tmp_pos = *pos1;
*pos1 = *pos2;
*pos2 = tmp_pos;
}
void set_flight_mode_flags(bool arm) {
if ((flightModeFlags == 0x00000002) && arm) {
flightModeFlags = 0x00000003; // arm
#ifdef DEBUG
Serial.println("ARMING");
#endif
} else if ((flightModeFlags == 0x00000003) && !arm) {
flightModeFlags = 0x00000002; // disarm
#ifdef DEBUG
Serial.println("DISARMING");
#endif
}
}
void display_flight_mode() {
show_text(&craftname);
}
void send_msp_to_airunit() {
//MSP_FC_VARIANT
memcpy(fc_variant.flightControlIdentifier, fcVariant, sizeof(fcVariant));
msp.send(MSP_FC_VARIANT, &fc_variant, sizeof(fc_variant));
//MSP_FC_VERSION
fc_version.versionMajor = 4;
fc_version.versionMinor = 1;
fc_version.versionPatchLevel = 1;
msp.send(MSP_FC_VERSION, &fc_version, sizeof(fc_version));
//MSP_NAME
memcpy(name.craft_name, craftname, sizeof(craftname));
msp.send(MSP_NAME, &name, sizeof(name));
//MSP_STATUS
status_DJI.flightModeFlags = flightModeFlags;
status_DJI.armingFlags = 0x0303;
msp.send(MSP_STATUS_EX, &status_DJI, sizeof(status_DJI));
status_DJI.armingFlags = 0x0000;
msp.send(MSP_STATUS, &status_DJI, sizeof(status_DJI));
//MSP_BATTERY_STATE
battery_state.amperage = 0;
battery_state.batteryVoltage = vbat * 10;
battery_state.mAhDrawn = 0;
battery_state.batteryCellCount = batteryCellCount;
battery_state.batteryCapacity = 0;
battery_state.batteryState = 0;
battery_state.legacyBatteryVoltage = vbat;
msp.send(MSP_BATTERY_STATE, &battery_state, sizeof(battery_state));
//MSP_OSD_CONFIG
send_osd_config();
}
void show_text(char (*text)[15]) {
memcpy(craftname, *text, sizeof(craftname));
}
void set_battery_cells_number() {
if (vbat < 43) batteryCellCount = 1;
else if (vbat < 85) batteryCellCount = 2;
else if (vbat < 127) batteryCellCount = 3;
else if (vbat < 169) batteryCellCount = 4;
else if (vbat < 211) batteryCellCount = 5;
else if (vbat < 255) batteryCellCount = 6;
}
void getVoltageSample() {
vbat = analogRead(ANALOG_IN)*10/VOLT_DIVIDER;
}
//*** USED ONLY FOR DEBUG ***
void debugPrint() {
SerialUSB.println("**********************************");
SerialUSB.print("Flight Mode: ");
SerialUSB.println(flightModeFlags);
SerialUSB.print("Voltage: ");
SerialUSB.println(((double)vbat / 10), 1);
SerialUSB.print("Sample Count / transmit: ");
SerialUSB.println(lastCount);
SerialUSB.print("Battery Cell Count: ");
SerialUSB.println(batteryCellCount);
}