Merge pull request #10558 from iNavFlight/MrD_Correct-comments-on-OSD-symbols
[inav.git] / src / main / telemetry / ibus.c
blobc230b551d8fd78dad07c2ddc87883bfa06eb303b
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
17 * FlySky iBus telemetry implementation by CraigJPerry.
19 * Many thanks to Dave Borthwick's iBus telemetry dongle converter for
20 * PIC 12F1572 (also distributed under GPLv3) which was referenced to
21 * clarify the protocol.
25 #include <stdbool.h>
26 #include <stdint.h>
27 #include <stdlib.h>
29 #include "platform.h"
31 #if defined(USE_TELEMETRY) && defined(USE_TELEMETRY_IBUS)
33 #include "common/maths.h"
34 #include "common/axis.h"
36 #include "drivers/serial.h"
37 #include "drivers/time.h"
39 #include "fc/fc_core.h"
40 #include "fc/rc_controls.h"
41 #include "fc/runtime_config.h"
42 #include "scheduler/scheduler.h"
44 #include "io/serial.h"
46 #include "sensors/barometer.h"
47 #include "sensors/acceleration.h"
48 #include "sensors/battery.h"
49 #include "sensors/sensors.h"
50 #include "io/gps.h"
52 #include "flight/imu.h"
53 #include "flight/failsafe.h"
54 #include "flight/mixer.h"
56 #include "navigation/navigation.h"
58 #include "telemetry/ibus.h"
59 #include "telemetry/ibus_shared.h"
60 #include "telemetry/telemetry.h"
61 #include "fc/config.h"
62 #include "config/feature.h"
65 * iBus Telemetry is a half-duplex serial protocol. It shares 1 line for
66 * both TX and RX. It runs at a fixed baud rate of 115200. Queries are sent
67 * every 7ms by the iBus receiver. Multiple sensors can be daisy chained with
68 * ibus but not with this implementation, only because i don't have one of the
69 * sensors to test with!
71 * _______
72 * / \ /---------\
73 * | STM32 |--UART TX-->[Bi-directional @ 115200 baud]<--| IBUS RX |
74 * | uC |--UART RX--x[not connected] \---------/
75 * \_______/
78 * The protocol is driven by the iBus receiver, currently either an IA6B or
79 * IA10. All iBus traffic is little endian. It begins with the iBus rx
80 * querying for a sensor on the iBus:
83 * /---------\
84 * | IBUS RX | > Hello sensor at address 1, are you there?
85 * \---------/ [ 0x04, 0x81, 0x7A, 0xFF ]
87 * 0x04 - Packet Length
88 * 0x81 - bits 7-4 Command (1000 = discover sensor)
89 * bits 3-0 Address (0001 = address 1)
90 * 0x7A, 0xFF - Checksum, 0xFFFF - (0x04 + 0x81)
93 * Due to the daisy-chaining, this hello also serves to inform the sensor
94 * of its address (position in the chain). There are 16 possible addresses
95 * in iBus, however address 0 is reserved for the rx's internally measured
96 * voltage leaving 0x1 to 0xF remaining.
98 * Having learned it's address, the sensor simply echos the message back:
101 * /--------\
102 * Yes, i'm here, hello! < | Sensor |
103 * [ 0x04, 0x81, 0x7A, 0xFF ] \--------/
105 * 0x04, 0x81, 0x7A, 0xFF - Echo back received packet
108 * On receipt of a response, the iBus rx next requests the sensor's type:
111 * /---------\
112 * | IBUS RX | > Sensor at address 1, what type are you?
113 * \---------/ [ 0x04, 0x91, 0x6A, 0xFF ]
115 * 0x04 - Packet Length
116 * 0x91 - bits 7-4 Command (1001 = request sensor type)
117 * bits 3-0 Address (0001 = address 1)
118 * 0x6A, 0xFF - Checksum, 0xFFFF - (0x04 + 0x91)
121 * To which the sensor responds with its details:
124 * /--------\
125 * Yes, i'm here, hello! < | Sensor |
126 * [ 0x06 0x91 0x00 0x02 0x66 0xFF ] \--------/
128 * 0x06 - Packet Length
129 * 0x91 - bits 7-4 Command (1001 = request sensor type)
130 * bits 3-0 Address (0001 = address 1)
131 * 0x00 - Measurement type (0 = internal voltage)
132 * 0x02 - Unknown, always 0x02
133 * 0x66, 0xFF - Checksum, 0xFFFF - (0x06 + 0x91 + 0x00 + 0x02)
136 * The iBus rx continues the discovery process by querying the next
137 * address. Discovery stops at the first address which does not respond.
139 * The iBus rx then begins a continual loop, requesting measurements from
140 * each sensor discovered:
143 * /---------\
144 * | IBUS RX | > Sensor at address 1, please send your measurement
145 * \---------/ [ 0x04, 0xA1, 0x5A, 0xFF ]
147 * 0x04 - Packet Length
148 * 0xA1 - bits 7-4 Command (1010 = request measurement)
149 * bits 3-0 Address (0001 = address 1)
150 * 0x5A, 0xFF - Checksum, 0xFFFF - (0x04 + 0xA1)
153 * /--------\
154 * I'm reading 0 volts < | Sensor |
155 * [ 0x06 0xA1 0x00 0x00 0x5E 0xFF ] \--------/
157 * 0x06 - Packet Length
158 * 0xA1 - bits 7-4 Command (1010 = request measurement)
159 * bits 3-0 Address (0001 = address 1)
160 * 0x00, 0x00 - The measurement
161 * 0x5E, 0xFF - Checksum, 0xFF - (0x06 + 0xA1 + 0x00 + 0x00)
164 * Due to the limited telemetry data types possible with ibus, we
165 * simply send everything which can be represented. Currently this
166 * is voltage and temperature.
170 static serialPort_t *ibusSerialPort = NULL;
171 static serialPortConfig_t *ibusSerialPortConfig;
172 static uint8_t outboundBytesToIgnoreOnRxCount = 0;
173 static bool ibusTelemetryEnabled = false;
174 static portSharing_e ibusPortSharing;
176 static uint8_t ibusReceiveBuffer[IBUS_RX_BUF_LEN] = { 0x0 };
178 static void pushOntoTail(uint8_t buffer[IBUS_MIN_LEN], size_t bufferLength, uint8_t value) {
179 for (size_t i = 0; i < bufferLength - 1; i++) {
180 buffer[i] = buffer[i + 1];
182 ibusReceiveBuffer[bufferLength - 1] = value;
185 void initIbusTelemetry(void) {
186 ibusSerialPortConfig = findSerialPortConfig(FUNCTION_TELEMETRY_IBUS);
187 uint8_t type = telemetryConfig()->ibusTelemetryType;
188 uint8_t speed = type & 0x80;
189 type = type & 0x7F;
190 if (type == 3) {
191 changeTypeIbusTelemetry(3, IBUS_MEAS_TYPE_S85, IBUS_MEAS_VALUE_STATUS);
192 changeTypeIbusTelemetry(4, IBUS_MEAS_TYPE_ACC_Z, IBUS_MEAS_VALUE_ACC_Z);
193 changeTypeIbusTelemetry(5, IBUS_MEAS_TYPE_CURRENT, IBUS_MEAS_VALUE_CURRENT);
194 changeTypeIbusTelemetry(6, IBUS_MEAS_TYPE_ALT, IBUS_MEAS_VALUE_ALT);
195 changeTypeIbusTelemetry(7, IBUS_MEAS_TYPE_HEADING, IBUS_MEAS_VALUE_HEADING);
196 changeTypeIbusTelemetry(8, IBUS_MEAS_TYPE_DIST, IBUS_MEAS_VALUE_DIST);
197 changeTypeIbusTelemetry(9, IBUS_MEAS_TYPE_COG, IBUS_MEAS_VALUE_COG);
198 changeTypeIbusTelemetry(11,IBUS_MEAS_TYPE_GPS_LON, IBUS_MEAS_VALUE_GPS_LON2);
199 changeTypeIbusTelemetry(12,IBUS_MEAS_TYPE_GPS_LAT, IBUS_MEAS_VALUE_GPS_LAT2);
200 changeTypeIbusTelemetry(13,IBUS_MEAS_TYPE_ACC_X, IBUS_MEAS_VALUE_GPS_LON1);
201 changeTypeIbusTelemetry(14,IBUS_MEAS_TYPE_ACC_Y, IBUS_MEAS_VALUE_GPS_LAT1);
203 if (type == 4) {
204 changeTypeIbusTelemetry(3, IBUS_MEAS_TYPE_S85, IBUS_MEAS_VALUE_STATUS);
205 #ifdef USE_PITOT
206 if (sensors(SENSOR_PITOT)) changeTypeIbusTelemetry(11,IBUS_MEAS_TYPE_VSPEED, IBUS_MEAS_VALUE_VSPEED);
207 else
208 #endif
209 changeTypeIbusTelemetry(11,IBUS_MEAS_TYPE_PRES, IBUS_MEAS_VALUE_PRES);
211 if (type == 5) {
212 changeTypeIbusTelemetry(2, IBUS_MEAS_TYPE_ARMED, IBUS_MEAS_VALUE_ARMED);
213 changeTypeIbusTelemetry(3, IBUS_MEAS_TYPE_MODE, IBUS_MEAS_VALUE_MODE);
214 changeTypeIbusTelemetry(11,IBUS_MEAS_TYPE_CLIMB, IBUS_MEAS_VALUE_CLIMB);
216 if (type == 4 || type == 5) {
217 changeTypeIbusTelemetry(4, IBUS_MEAS_TYPE_ACC_Z, IBUS_MEAS_VALUE_ACC_Z);
218 changeTypeIbusTelemetry(5, IBUS_MEAS_TYPE_CURRENT, IBUS_MEAS_VALUE_CURRENT);
219 changeTypeIbusTelemetry(6, IBUS_MEAS_TYPE_S8A, IBUS_MEAS_VALUE_ALT4);
220 changeTypeIbusTelemetry(7, IBUS_MEAS_TYPE_HEADING, IBUS_MEAS_VALUE_HEADING);
221 changeTypeIbusTelemetry(8, IBUS_MEAS_TYPE_DIST, IBUS_MEAS_VALUE_DIST);
222 changeTypeIbusTelemetry(9, IBUS_MEAS_TYPE_COG, IBUS_MEAS_VALUE_COG);
223 changeTypeIbusTelemetry(12,IBUS_MEAS_TYPE_GPS_STATUS, IBUS_MEAS_VALUE_GPS_STATUS);
224 changeTypeIbusTelemetry(13,IBUS_MEAS_TYPE_S88, IBUS_MEAS_VALUE_GPS_LON);
225 changeTypeIbusTelemetry(14,IBUS_MEAS_TYPE_S89, IBUS_MEAS_VALUE_GPS_LAT);
227 if (type == 2 || type == 3 || type == 4 || type == 5)
228 changeTypeIbusTelemetry(10, IBUS_MEAS_TYPE_GALT, IBUS_MEAS_VALUE_GALT);
229 if (type == 1 || type == 2 || type == 3 || type == 4 || type == 5)
230 changeTypeIbusTelemetry(15, IBUS_MEAS_TYPE_SPE, IBUS_MEAS_VALUE_SPE);
231 if ((type == 3 || type == 4 || type == 5) && speed)
232 changeTypeIbusTelemetry(15,IBUS_MEAS_TYPE_SPEED, IBUS_MEAS_VALUE_SPEED);
233 if (type == 6) {
234 #ifdef USE_PITOT
235 if (sensors(SENSOR_PITOT)) changeTypeIbusTelemetry(9,IBUS_MEAS_TYPE1_VERTICAL_SPEED, IBUS_MEAS_VALUE_VSPEED);
236 else
237 #endif
238 changeTypeIbusTelemetry(9, IBUS_MEAS_TYPE1_PRES, IBUS_MEAS_VALUE_PRES);
239 changeTypeIbusTelemetry(15, IBUS_MEAS_TYPE1_S85, IBUS_MEAS_VALUE_STATUS);
241 if (type == 7 || type == 8) {
242 changeTypeIbusTelemetry(2, IBUS_MEAS_TYPE1_GPS_STATUS, IBUS_MEAS_VALUE_GPS_STATUS);
243 changeTypeIbusTelemetry(9, IBUS_MEAS_TYPE1_ARMED, IBUS_MEAS_VALUE_ARMED);
244 changeTypeIbusTelemetry(15,IBUS_MEAS_TYPE1_FLIGHT_MODE, IBUS_MEAS_VALUE_MODE);
246 if (type == 6 || type == 7 || type == 8) {
247 if (batteryMetersConfig()->current.type == CURRENT_SENSOR_VIRTUAL)
248 changeTypeIbusTelemetry(3, IBUS_MEAS_TYPE1_FUEL, IBUS_MEAS_VALUE_FUEL);
249 else changeTypeIbusTelemetry(3, IBUS_MEAS_TYPE1_BAT_CURR, IBUS_MEAS_VALUE_CURRENT);
250 changeTypeIbusTelemetry(4, IBUS_MEAS_TYPE1_CMP_HEAD, IBUS_MEAS_VALUE_HEADING);
251 changeTypeIbusTelemetry(6, IBUS_MEAS_TYPE1_CLIMB_RATE, IBUS_MEAS_VALUE_CLIMB);
252 changeTypeIbusTelemetry(5, IBUS_MEAS_TYPE1_COG, IBUS_MEAS_VALUE_COG);
253 changeTypeIbusTelemetry(7, IBUS_MEAS_TYPE1_YAW, IBUS_MEAS_VALUE_ACC_Z);
254 changeTypeIbusTelemetry(8, IBUS_MEAS_TYPE1_GPS_DIST, IBUS_MEAS_VALUE_DIST);
255 if (speed) changeTypeIbusTelemetry(10,IBUS_MEAS_TYPE1_GROUND_SPEED, IBUS_MEAS_VALUE_SPEED);
256 else changeTypeIbusTelemetry(10, IBUS_MEAS_TYPE1_SPE, IBUS_MEAS_VALUE_SPE);
257 changeTypeIbusTelemetry(11,IBUS_MEAS_TYPE1_GPS_LAT, IBUS_MEAS_VALUE_GPS_LAT);
258 changeTypeIbusTelemetry(12,IBUS_MEAS_TYPE1_GPS_LON, IBUS_MEAS_VALUE_GPS_LON);
259 changeTypeIbusTelemetry(13,IBUS_MEAS_TYPE1_GPS_ALT, IBUS_MEAS_VALUE_GALT4);
260 changeTypeIbusTelemetry(14,IBUS_MEAS_TYPE1_ALT, IBUS_MEAS_VALUE_ALT4);
262 ibusPortSharing = determinePortSharing(ibusSerialPortConfig, FUNCTION_TELEMETRY_IBUS);
263 ibusTelemetryEnabled = false;
266 void handleIbusTelemetry(void) {
267 if (!ibusTelemetryEnabled) {
268 return;
270 while (serialRxBytesWaiting(ibusSerialPort) > 0) {
271 uint8_t c = serialRead(ibusSerialPort);
272 if (outboundBytesToIgnoreOnRxCount) {
273 outboundBytesToIgnoreOnRxCount--;
274 continue;
276 pushOntoTail(ibusReceiveBuffer, IBUS_RX_BUF_LEN, c);
277 if (ibusIsChecksumOkIa6b(ibusReceiveBuffer, IBUS_RX_BUF_LEN)) {
278 outboundBytesToIgnoreOnRxCount += respondToIbusRequest(ibusReceiveBuffer);
283 bool checkIbusTelemetryState(void) {
284 bool newTelemetryEnabledValue = telemetryDetermineEnabledState(ibusPortSharing);
286 if (newTelemetryEnabledValue == ibusTelemetryEnabled) {
287 return false;
290 if (newTelemetryEnabledValue) {
291 rescheduleTask(TASK_TELEMETRY, IBUS_TASK_PERIOD_US);
292 configureIbusTelemetryPort();
293 } else {
294 freeIbusTelemetryPort();
297 return true;
300 void configureIbusTelemetryPort(void) {
301 if (!ibusSerialPortConfig) {
302 return;
304 if (isSerialPortShared(ibusSerialPortConfig, FUNCTION_RX_SERIAL, FUNCTION_TELEMETRY_IBUS)) {
305 // serialRx will open port and handle telemetry
306 return;
308 ibusSerialPort = openSerialPort(ibusSerialPortConfig->identifier, FUNCTION_TELEMETRY_IBUS, NULL, NULL, IBUS_BAUDRATE, MODE_RXTX, SERIAL_BIDIR | SERIAL_NOT_INVERTED);
309 if (!ibusSerialPort) {
310 return;
312 initSharedIbusTelemetry(ibusSerialPort);
313 ibusTelemetryEnabled = true;
314 outboundBytesToIgnoreOnRxCount = 0;
317 void freeIbusTelemetryPort(void) {
318 closeSerialPort(ibusSerialPort);
319 ibusSerialPort = NULL;
321 ibusTelemetryEnabled = false;
324 #endif