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.
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"
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!
73 * | STM32 |--UART TX-->[Bi-directional @ 115200 baud]<--| IBUS RX |
74 * | uC |--UART RX--x[not connected] \---------/
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:
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:
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:
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:
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:
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)
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;
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
);
204 changeTypeIbusTelemetry(3, IBUS_MEAS_TYPE_S85
, IBUS_MEAS_VALUE_STATUS
);
206 if (sensors(SENSOR_PITOT
)) changeTypeIbusTelemetry(11,IBUS_MEAS_TYPE_VSPEED
, IBUS_MEAS_VALUE_VSPEED
);
209 changeTypeIbusTelemetry(11,IBUS_MEAS_TYPE_PRES
, IBUS_MEAS_VALUE_PRES
);
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
);
235 if (sensors(SENSOR_PITOT
)) changeTypeIbusTelemetry(9,IBUS_MEAS_TYPE1_VERTICAL_SPEED
, IBUS_MEAS_VALUE_VSPEED
);
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
) {
270 while (serialRxBytesWaiting(ibusSerialPort
) > 0) {
271 uint8_t c
= serialRead(ibusSerialPort
);
272 if (outboundBytesToIgnoreOnRxCount
) {
273 outboundBytesToIgnoreOnRxCount
--;
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
) {
290 if (newTelemetryEnabledValue
) {
291 rescheduleTask(TASK_TELEMETRY
, IBUS_TASK_PERIOD_US
);
292 configureIbusTelemetryPort();
294 freeIbusTelemetryPort();
300 void configureIbusTelemetryPort(void) {
301 if (!ibusSerialPortConfig
) {
304 if (isSerialPortShared(ibusSerialPortConfig
, FUNCTION_RX_SERIAL
, FUNCTION_TELEMETRY_IBUS
)) {
305 // serialRx will open port and handle telemetry
308 ibusSerialPort
= openSerialPort(ibusSerialPortConfig
->identifier
, FUNCTION_TELEMETRY_IBUS
, NULL
, NULL
, IBUS_BAUDRATE
, MODE_RXTX
, SERIAL_BIDIR
| SERIAL_NOT_INVERTED
);
309 if (!ibusSerialPort
) {
312 initSharedIbusTelemetry(ibusSerialPort
);
313 ibusTelemetryEnabled
= true;
314 outboundBytesToIgnoreOnRxCount
= 0;
317 void freeIbusTelemetryPort(void) {
318 closeSerialPort(ibusSerialPort
);
319 ibusSerialPort
= NULL
;
321 ibusTelemetryEnabled
= false;