2 * SmartPort Telemetry implementation by frank26080115
3 * see https://github.com/frank26080115/cleanflight/wiki/Using-Smart-Port
14 #include "common/axis.h"
15 #include "common/color.h"
16 #include "common/maths.h"
18 #include "drivers/system.h"
19 #include "drivers/sensor.h"
20 #include "drivers/accgyro.h"
21 #include "drivers/compass.h"
22 #include "drivers/serial.h"
23 #include "drivers/bus_i2c.h"
24 #include "drivers/gpio.h"
25 #include "drivers/timer.h"
26 #include "drivers/pwm_rx.h"
27 #include "drivers/adc.h"
28 #include "drivers/light_led.h"
33 #include "io/escservo.h"
34 #include "io/rc_controls.h"
36 #include "io/gimbal.h"
37 #include "io/serial.h"
38 #include "io/ledstrip.h"
40 #include "sensors/boardalignment.h"
41 #include "sensors/sensors.h"
42 #include "sensors/battery.h"
43 #include "sensors/acceleration.h"
44 #include "sensors/barometer.h"
45 #include "sensors/compass.h"
46 #include "sensors/gyro.h"
48 #include "flight/flight.h"
49 #include "flight/imu.h"
50 #include "flight/mixer.h"
51 #include "flight/failsafe.h"
52 #include "flight/navigation.h"
54 #include "telemetry/telemetry.h"
55 #include "telemetry/smartport.h"
57 #include "config/runtime_config.h"
58 #include "config/config.h"
59 #include "config/config_profile.h"
60 #include "config/config_master.h"
64 SPSTATE_UNINITIALIZED
,
68 SPSTATE_DEINITIALIZED
,
73 FSSP_START_STOP
= 0x7E,
74 FSSP_DATA_FRAME
= 0x10,
76 // ID of sensor. Must be something that is polled by FrSky RX
77 FSSP_SENSOR_ID1
= 0x1B,
78 FSSP_SENSOR_ID2
= 0x0D,
79 FSSP_SENSOR_ID3
= 0x34,
80 FSSP_SENSOR_ID4
= 0x67,
81 // there are 32 ID's polled by smartport master
82 // remaining 3 bits are crc (according to comments in openTx code)
85 // these data identifiers are obtained from http://diydrones.com/forum/topics/amp-to-frsky-x8r-sport-converter
88 FSSP_DATAID_SPEED
= 0x0830 ,
89 FSSP_DATAID_VFAS
= 0x0210 ,
90 FSSP_DATAID_CURRENT
= 0x0200 ,
91 FSSP_DATAID_RPM
= 0x050F ,
92 FSSP_DATAID_ALTITUDE
= 0x0100 ,
93 FSSP_DATAID_FUEL
= 0x0600 ,
94 FSSP_DATAID_ADC1
= 0xF102 ,
95 FSSP_DATAID_ADC2
= 0xF103 ,
96 FSSP_DATAID_LATLONG
= 0x0800 ,
97 FSSP_DATAID_CAP_USED
= 0x0600 ,
98 FSSP_DATAID_VARIO
= 0x0110 ,
99 FSSP_DATAID_CELLS
= 0x0300 ,
100 FSSP_DATAID_CELLS_LAST
= 0x030F ,
101 FSSP_DATAID_HEADING
= 0x0840 ,
102 FSSP_DATAID_ACCX
= 0x0700 ,
103 FSSP_DATAID_ACCY
= 0x0710 ,
104 FSSP_DATAID_ACCZ
= 0x0720 ,
105 FSSP_DATAID_T1
= 0x0400 ,
106 FSSP_DATAID_T2
= 0x0410 ,
107 FSSP_DATAID_GPS_ALT
= 0x0820 ,
110 const uint16_t frSkyDataIdTable
[] = {
113 FSSP_DATAID_CURRENT
,
115 FSSP_DATAID_ALTITUDE
,
119 FSSP_DATAID_LATLONG
,
120 FSSP_DATAID_LATLONG
, // twice
121 //FSSP_DATAID_CAP_USED ,
123 //FSSP_DATAID_CELLS ,
124 //FSSP_DATAID_CELLS_LAST,
125 FSSP_DATAID_HEADING
,
131 FSSP_DATAID_GPS_ALT
,
135 #define __USE_C99_MATH // for roundf()
136 #define SMARTPORT_BAUD 57600
137 #define SMARTPORT_UART_MODE MODE_BIDIR
138 #define SMARTPORT_SERVICE_DELAY_MS 5 // telemetry requests comes in at roughly 12 ms intervals, keep this under that
139 #define SMARTPORT_NOT_CONNECTED_TIMEOUT_MS 7000
141 static serialPort_t
*smartPortSerialPort
; // The 'SmartPort'(tm) Port.
142 static telemetryConfig_t
*telemetryConfig
;
143 static portMode_t previousPortMode
;
144 static uint32_t previousBaudRate
;
145 extern void serialInit(serialConfig_t
*); // from main.c // FIXME remove this dependency
147 char smartPortState
= SPSTATE_UNINITIALIZED
;
148 static uint8_t smartPortHasRequest
= 0;
149 static uint8_t smartPortIdCnt
= 0;
150 static uint32_t smartPortLastRequestTime
= 0;
151 static uint32_t smartPortLastServiceTime
= 0;
153 static void smartPortDataReceive(uint16_t c
)
155 uint32_t now
= millis();
157 // look for a valid request sequence
158 static uint8_t lastChar
;
159 if (lastChar
== FSSP_START_STOP
) {
160 smartPortState
= SPSTATE_WORKING
;
161 smartPortLastRequestTime
= now
;
162 if ((c
== FSSP_SENSOR_ID1
) ||
163 (c
== FSSP_SENSOR_ID2
) ||
164 (c
== FSSP_SENSOR_ID3
) ||
165 (c
== FSSP_SENSOR_ID4
)) {
166 smartPortHasRequest
= 1;
167 // we only responde to these IDs
168 // the X4R-SB does send other IDs, we ignore them, but take note of the time
174 static void smartPortSendByte(uint8_t c
, uint16_t *crcp
)
176 // smart port escape sequence
177 if (c
== 0x7D || c
== 0x7E) {
178 serialWrite(smartPortSerialPort
, 0x7D);
182 serialWrite(smartPortSerialPort
, c
);
187 uint16_t crc
= *crcp
;
194 static void smartPortSendPackage(uint16_t id
, uint32_t val
)
197 smartPortSendByte(FSSP_DATA_FRAME
, &crc
);
198 uint8_t *u8p
= (uint8_t*)&id
;
199 smartPortSendByte(u8p
[0], &crc
);
200 smartPortSendByte(u8p
[1], &crc
);
201 u8p
= (uint8_t*)&val
;
202 smartPortSendByte(u8p
[0], &crc
);
203 smartPortSendByte(u8p
[1], &crc
);
204 smartPortSendByte(u8p
[2], &crc
);
205 smartPortSendByte(u8p
[3], &crc
);
206 smartPortSendByte(0xFF - (uint8_t)crc
, NULL
);
208 smartPortLastServiceTime
= millis();
211 void initSmartPortTelemetry(telemetryConfig_t
*initialTelemetryConfig
)
213 telemetryConfig
= initialTelemetryConfig
;
216 void freeSmartPortTelemetryPort(void)
218 if (smartPortState
== SPSTATE_UNINITIALIZED
)
220 if (smartPortState
== SPSTATE_DEINITIALIZED
)
223 if (isTelemetryPortShared()) {
224 endSerialPortFunction(smartPortSerialPort
, FUNCTION_SMARTPORT_TELEMETRY
);
225 smartPortState
= SPSTATE_DEINITIALIZED
;
226 serialInit(&masterConfig
.serialConfig
);
229 serialSetMode(smartPortSerialPort
, previousPortMode
);
230 serialSetBaudRate(smartPortSerialPort
, previousBaudRate
);
231 endSerialPortFunction(smartPortSerialPort
, FUNCTION_SMARTPORT_TELEMETRY
);
232 smartPortState
= SPSTATE_DEINITIALIZED
;
234 smartPortSerialPort
= NULL
;
237 void configureSmartPortTelemetryPort(void)
239 if (smartPortState
!= SPSTATE_UNINITIALIZED
) {
240 // do not allow reinitialization
244 smartPortSerialPort
= findOpenSerialPort(FUNCTION_SMARTPORT_TELEMETRY
);
245 if (smartPortSerialPort
) {
246 previousPortMode
= smartPortSerialPort
->mode
;
247 previousBaudRate
= smartPortSerialPort
->baudRate
;
249 //waitForSerialPortToFinishTransmitting(smartPortPort); // FIXME locks up the system
251 serialSetBaudRate(smartPortSerialPort
, SMARTPORT_BAUD
);
252 serialSetMode(smartPortSerialPort
, SMARTPORT_UART_MODE
);
253 beginSerialPortFunction(smartPortSerialPort
, FUNCTION_SMARTPORT_TELEMETRY
);
255 smartPortSerialPort
= openSerialPort(FUNCTION_SMARTPORT_TELEMETRY
, NULL
, SMARTPORT_BAUD
, SMARTPORT_UART_MODE
, telemetryConfig
->telemetry_inversion
);
257 if (smartPortSerialPort
) {
258 smartPortState
= SPSTATE_INITIALIZED
;
259 previousPortMode
= smartPortSerialPort
->mode
;
260 previousBaudRate
= smartPortSerialPort
->baudRate
;
263 // failed, resume MSP and CLI
264 if (isTelemetryPortShared()) {
265 smartPortState
= SPSTATE_DEINITIALIZED
;
266 serialInit(&masterConfig
.serialConfig
);
272 bool canSendSmartPortTelemetry(void)
274 return smartPortState
== SPSTATE_INITIALIZED
|| smartPortState
== SPSTATE_WORKING
;
277 bool canSmartPortAllowOtherSerial(void)
279 return smartPortState
== SPSTATE_DEINITIALIZED
;
282 bool isSmartPortTimedOut(void)
284 return smartPortState
>= SPSTATE_TIMEDOUT
;
287 uint32_t getSmartPortTelemetryProviderBaudRate(void)
289 return SMARTPORT_BAUD
;
292 void handleSmartPortTelemetry(void)
294 if (!canSendSmartPortTelemetry())
297 while (serialTotalBytesWaiting(smartPortSerialPort
) > 0) {
298 uint8_t c
= serialRead(smartPortSerialPort
);
299 smartPortDataReceive(c
);
302 uint32_t now
= millis();
304 // if timed out, reconfigure the UART back to normal so the GUI or CLI works
305 if ((now
- smartPortLastRequestTime
) > SMARTPORT_NOT_CONNECTED_TIMEOUT_MS
) {
306 smartPortState
= SPSTATE_TIMEDOUT
;
310 // limit the rate at which we send responses, we don't want to affect flight characteristics
311 if ((now
- smartPortLastServiceTime
) < SMARTPORT_SERVICE_DELAY_MS
)
314 if (smartPortHasRequest
) {
315 // we can send back any data we want, our table keeps track of the order and frequency of each data type we send
316 uint16_t id
= frSkyDataIdTable
[smartPortIdCnt
];
317 if (id
== 0) { // end of table reached, loop back
319 id
= frSkyDataIdTable
[smartPortIdCnt
];
325 static uint8_t t1Cnt
= 0;
328 case FSSP_DATAID_SPEED
:
329 if (sensors(SENSOR_GPS
) && STATE(GPS_FIX
)) {
330 tmpui
= (GPS_speed
* 36 + 36 / 2) / 100;
331 smartPortSendPackage(id
, tmpui
); // given in 0.1 m/s, provide in KM/H
332 smartPortHasRequest
= 0;
335 case FSSP_DATAID_VFAS
:
336 smartPortSendPackage(id
, vbat
* 83); // supposedly given in 0.1V, unknown requested unit
337 // multiplying by 83 seems to make Taranis read correctly
338 smartPortHasRequest
= 0;
340 case FSSP_DATAID_CURRENT
:
341 smartPortSendPackage(id
, amperage
); // given in 10mA steps, unknown requested unit
342 smartPortHasRequest
= 0;
344 //case FSSP_DATAID_RPM :
345 case FSSP_DATAID_ALTITUDE
:
346 smartPortSendPackage(id
, BaroAlt
); // unknown given unit, requested 100 = 1 meter
347 smartPortHasRequest
= 0;
349 case FSSP_DATAID_FUEL
:
350 smartPortSendPackage(id
, mAhDrawn
); // given in mAh, unknown requested unit
351 smartPortHasRequest
= 0;
353 //case FSSP_DATAID_ADC1 :
354 //case FSSP_DATAID_ADC2 :
355 case FSSP_DATAID_LATLONG
:
356 if (sensors(SENSOR_GPS
) && STATE(GPS_FIX
)) {
358 // the same ID is sent twice, one for longitude, one for latitude
359 // the MSB of the sent uint32_t helps FrSky keep track
360 // the even/odd bit of our counter helps us keep track
361 if (smartPortIdCnt
& 1) {
362 tmpui
= tmpi
= GPS_coord
[LON
];
370 tmpui
= tmpi
= GPS_coord
[LAT
];
376 smartPortSendPackage(id
, tmpui
);
377 smartPortHasRequest
= 0;
380 //case FSSP_DATAID_CAP_USED :
381 case FSSP_DATAID_VARIO
:
382 smartPortSendPackage(id
, vario
); // unknown given unit but requested in 100 = 1m/s
383 smartPortHasRequest
= 0;
385 case FSSP_DATAID_HEADING
:
386 smartPortSendPackage(id
, heading
* 100); // given in deg, requested in 10000 = 100 deg
387 smartPortHasRequest
= 0;
389 case FSSP_DATAID_ACCX
:
390 smartPortSendPackage(id
, accSmooth
[X
] / 44);
391 // unknown input and unknown output unit
392 // we can only show 00.00 format, another digit won't display right on Taranis
393 // dividing by roughly 44 will give acceleration in G units
394 smartPortHasRequest
= 0;
396 case FSSP_DATAID_ACCY
:
397 smartPortSendPackage(id
, accSmooth
[Y
] / 44);
398 smartPortHasRequest
= 0;
400 case FSSP_DATAID_ACCZ
:
401 smartPortSendPackage(id
, accSmooth
[Z
] / 44);
402 smartPortHasRequest
= 0;
404 case FSSP_DATAID_T1
:
405 // we send all the flags as decimal digits for easy reading
407 // the t1Cnt simply allows the telemetry view to show at least some changes
412 tmpi
= t1Cnt
* 10000; // start off with at least one digit so the most significant 0 won't be cut off
413 // the Taranis seems to be able to fit 5 digits on the screen
414 // the Taranis seems to consider this number a signed 16 bit integer
416 if (ARMING_FLAG(OK_TO_ARM
))
418 if (ARMING_FLAG(PREVENT_ARMING
))
420 if (ARMING_FLAG(ARMED
))
423 if (FLIGHT_MODE(ANGLE_MODE
))
425 if (FLIGHT_MODE(HORIZON_MODE
))
427 if (FLIGHT_MODE(AUTOTUNE_MODE
))
429 if (FLIGHT_MODE(PASSTHRU_MODE
))
432 if (FLIGHT_MODE(MAG_MODE
))
434 if (FLIGHT_MODE(BARO_MODE
))
436 if (FLIGHT_MODE(SONAR_MODE
))
439 if (FLIGHT_MODE(GPS_HOLD_MODE
))
441 if (FLIGHT_MODE(GPS_HOME_MODE
))
443 if (FLIGHT_MODE(HEADFREE_MODE
))
446 smartPortSendPackage(id
, (uint32_t)tmpi
);
447 smartPortHasRequest
= 0;
449 case FSSP_DATAID_T2
:
450 if (sensors(SENSOR_GPS
)) {
451 // provide GPS lock status
452 smartPortSendPackage(id
, (STATE(GPS_FIX
) ? 1000 : 0) + (STATE(GPS_FIX_HOME
) ? 2000 : 0) + GPS_numSat
);
453 smartPortHasRequest
= 0;
456 smartPortSendPackage(id
, 0);
457 smartPortHasRequest
= 0;
460 case FSSP_DATAID_GPS_ALT
:
461 if (sensors(SENSOR_GPS
) && STATE(GPS_FIX
)) {
462 smartPortSendPackage(id
, GPS_altitude
* 1000); // given in 0.1m , requested in 100 = 1m
463 smartPortHasRequest
= 0;
468 // if nothing is sent, smartPortHasRequest isn't cleared, we already incremented the counter, just wait for the next loop