5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
23 #if defined(PCBHORUS) || defined(PCBX7) || defined(PCBXLITE) || defined(USEHORUSBT)
24 #define BLUETOOTH_COMMAND_NAME "AT+NAME"
25 #define BLUETOOTH_ANSWER_NAME "OK+"
26 #define BLUETOOTH_COMMAND_BAUD_115200 "AT+BAUD115200"
28 #define BLUETOOTH_COMMAND_NAME "TTM:REN-"
29 #define BLUETOOTH_ANSWER_NAME "TTM:REN"
30 #define BLUETOOTH_COMMAND_BAUD_115200 "TTM:BPS-115200"
33 #define BLUETOOTH_PACKET_SIZE 14
34 #define BLUETOOTH_LINE_LENGTH 32
36 extern Fifo
<uint8_t, 64> btTxFifo
;
37 extern Fifo
<uint8_t, 64> btRxFifo
;
39 volatile uint8_t bluetoothState
;
40 char bluetoothLocalAddr
[LEN_BLUETOOTH_ADDR
+1];
41 char bluetoothDistantAddr
[LEN_BLUETOOTH_ADDR
+1];
42 uint8_t bluetoothBuffer
[BLUETOOTH_LINE_LENGTH
+1];
43 uint8_t bluetoothBufferIndex
= 0;
44 tmr10ms_t bluetoothWakeupTime
= 0;
46 void bluetoothWrite(const uint8_t * data
, uint8_t length
)
49 for (int i
=0; i
<length
; i
++) {
50 TRACE_NOCRLF(" %02X", data
[i
]);
51 btTxFifo
.push(data
[i
]);
54 bluetoothWriteWakeup();
57 void bluetoothWriteString(const char * str
)
61 btTxFifo
.push(*str
++);
63 bluetoothWriteWakeup();
66 char * bluetoothReadline(bool error_reset
)
71 if (!btRxFifo
.pop(byte
)) {
72 #if defined(PCBX9E) && !defined(USEHORUSBT) // X9E BT module can get unresponsive
73 TRACE("NO RESPONSE FROM BT MODULE");
77 TRACE_NOCRLF("%02X ", byte
);
79 if (bluetoothBufferIndex
> 2 && bluetoothBuffer
[bluetoothBufferIndex
-1] == '\r') {
80 bluetoothBuffer
[bluetoothBufferIndex
-1] = '\0';
81 bluetoothBufferIndex
= 0;
82 TRACE("BT< %s", bluetoothBuffer
);
83 if (error_reset
&& !strcmp((char *)bluetoothBuffer
, "ERROR")) {
84 #if defined(PCBX9E) // X9E enter BT reset loop if following code is implemented
89 bluetoothState
= BLUETOOTH_STATE_OFF
;
90 bluetoothWakeupTime
= get_tmr10ms() + 100; /* 1s */
95 if (!strncmp((char *)bluetoothBuffer
, "Central:", 8))
96 strcpy(bluetoothLocalAddr
, (char *)bluetoothBuffer
+8);
97 else if (!strncmp((char *)bluetoothBuffer
, "Peripheral:", 11))
98 strcpy(bluetoothLocalAddr
, (char *)bluetoothBuffer
+11);
99 return (char *)bluetoothBuffer
;
103 bluetoothBufferIndex
= 0;
107 bluetoothBuffer
[bluetoothBufferIndex
++] = byte
;
108 bluetoothBufferIndex
&= (BLUETOOTH_LINE_LENGTH
-1);
113 void bluetoothProcessTrainerFrame(const uint8_t * bluetoothBuffer
)
117 for (uint8_t channel
=0, i
=1; channel
<8; channel
+=2, i
+=3) {
118 // +-500 != 512, but close enough.
119 ppmInput
[channel
] = bluetoothBuffer
[i
] + ((bluetoothBuffer
[i
+1] & 0xf0) << 4) - 1500;
120 ppmInput
[channel
+1] = ((bluetoothBuffer
[i
+1] & 0x0f) << 4) + ((bluetoothBuffer
[i
+2] & 0xf0) >> 4) + ((bluetoothBuffer
[i
+2] & 0x0f) << 8) - 1500;
123 ppmInputValidityTimer
= PPM_IN_VALID_TIMEOUT
;
126 void bluetoothAppendTrainerByte(uint8_t data
)
128 if (bluetoothBufferIndex
< BLUETOOTH_LINE_LENGTH
) {
129 bluetoothBuffer
[bluetoothBufferIndex
++] = data
;
130 // we check for "DisConnected", but the first byte could be altered (if received in state STATE_DATA_XOR)
132 if (!strncmp((char *)&bluetoothBuffer
[bluetoothBufferIndex
-13], "isConnected", 11)) {
133 TRACE("BT< DisConnected");
134 bluetoothState
= BLUETOOTH_STATE_DISCONNECTED
;
135 bluetoothBufferIndex
= 0;
136 bluetoothWakeupTime
+= 200; // 1s
142 void bluetoothProcessTrainerByte(uint8_t data
)
144 static uint8_t dataState
= STATE_DATA_IDLE
;
147 case STATE_DATA_START
:
148 if (data
== START_STOP
) {
149 dataState
= STATE_DATA_IN_FRAME
;
150 bluetoothBufferIndex
= 0;
153 bluetoothAppendTrainerByte(data
);
157 case STATE_DATA_IN_FRAME
:
158 if (data
== BYTESTUFF
) {
159 dataState
= STATE_DATA_XOR
; // XOR next byte
161 else if (data
== START_STOP
) {
162 dataState
= STATE_DATA_IN_FRAME
;
163 bluetoothBufferIndex
= 0;
166 bluetoothAppendTrainerByte(data
);
171 bluetoothAppendTrainerByte(data
^ STUFF_MASK
);
172 dataState
= STATE_DATA_IN_FRAME
;
175 case STATE_DATA_IDLE
:
176 if (data
== START_STOP
) {
177 bluetoothBufferIndex
= 0;
178 dataState
= STATE_DATA_START
;
181 bluetoothAppendTrainerByte(data
);
186 if (bluetoothBufferIndex
>= BLUETOOTH_PACKET_SIZE
) {
188 for (int i
=0; i
<13; i
++) {
189 crc
^= bluetoothBuffer
[i
];
191 if (crc
== bluetoothBuffer
[13]) {
192 if (bluetoothBuffer
[0] == 0x80) {
193 bluetoothProcessTrainerFrame(bluetoothBuffer
);
196 dataState
= STATE_DATA_IDLE
;
200 uint8_t bluetoothCrc
;
201 void bluetoothPushByte(uint8_t byte
)
203 bluetoothCrc
^= byte
;
204 if (byte
== START_STOP
|| byte
== BYTESTUFF
) {
205 bluetoothBuffer
[bluetoothBufferIndex
++] = 0x7d;
208 bluetoothBuffer
[bluetoothBufferIndex
++] = byte
;
211 void bluetoothSendTrainer()
213 int16_t PPM_range
= g_model
.extendedLimits
? 640*2 : 512*2;
215 int firstCh
= g_model
.moduleData
[TRAINER_MODULE
].channelsStart
;
216 int lastCh
= firstCh
+ 8;
218 uint8_t * cur
= bluetoothBuffer
;
219 bluetoothBufferIndex
= 0;
222 bluetoothBuffer
[bluetoothBufferIndex
++] = START_STOP
; // start byte
223 bluetoothPushByte(0x80); // trainer frame type?
224 for (int channel
=0; channel
<lastCh
; channel
+=2, cur
+=3) {
225 uint16_t channelValue1
= PPM_CH_CENTER(channel
) + limit((int16_t)-PPM_range
, channelOutputs
[channel
], (int16_t)PPM_range
) / 2;
226 uint16_t channelValue2
= PPM_CH_CENTER(channel
+1) + limit((int16_t)-PPM_range
, channelOutputs
[channel
+1], (int16_t)PPM_range
) / 2;
227 bluetoothPushByte(channelValue1
& 0x00ff);
228 bluetoothPushByte(((channelValue1
& 0x0f00) >> 4) + ((channelValue2
& 0x00f0) >> 4));
229 bluetoothPushByte(((channelValue2
& 0x000f) << 4) + ((channelValue2
& 0x0f00) >> 8));
231 bluetoothBuffer
[bluetoothBufferIndex
++] = bluetoothCrc
;
232 bluetoothBuffer
[bluetoothBufferIndex
++] = START_STOP
; // end byte
234 bluetoothWrite(bluetoothBuffer
, bluetoothBufferIndex
);
235 bluetoothBufferIndex
= 0;
238 void bluetoothForwardTelemetry(uint8_t data
)
240 bluetoothBuffer
[bluetoothBufferIndex
++] = data
;
241 if (data
== START_STOP
&& bluetoothBufferIndex
>= 2*FRSKY_SPORT_PACKET_SIZE
) {
242 bluetoothWrite(bluetoothBuffer
, bluetoothBufferIndex
);
243 bluetoothBufferIndex
= 0;
247 void bluetoothReceiveTrainer()
252 if (!btRxFifo
.pop(byte
)) {
256 TRACE_NOCRLF("%02X ", byte
);
258 bluetoothProcessTrainerByte(byte
);
262 #if defined(PCBX9E) && !defined(USEHORUSBT)
263 void bluetoothWakeup(void)
266 if (!g_eeGeneral
.bluetoothMode
) {
267 if (bluetoothState
!= BLUETOOTH_INIT
) {
269 bluetoothState
= BLUETOOTH_INIT
;
273 static tmr10ms_t waitEnd
= 0;
274 if (bluetoothState
!= BLUETOOTH_STATE_IDLE
) {
276 if (bluetoothState
== BLUETOOTH_INIT
) {
277 bluetoothInit(BLUETOOTH_DEFAULT_BAUDRATE
);
279 char * cur
= strAppend(command
, BLUETOOTH_COMMAND_NAME
);
280 uint8_t len
= ZLEN(g_eeGeneral
.bluetoothName
);
282 for (int i
= 0; i
< len
; i
++) {
283 *cur
++ = idx2char(g_eeGeneral
.bluetoothName
[i
]);
287 cur
= strAppend(cur
, "Taranis-X9E");
289 strAppend(cur
, "\r\n");
290 bluetoothWriteString(command
);
291 bluetoothState
= BLUETOOTH_WAIT_TTM
;
292 waitEnd
= get_tmr10ms() + 25; // 250ms
294 else if (bluetoothState
== BLUETOOTH_WAIT_TTM
) {
295 if (get_tmr10ms() > waitEnd
) {
296 char * line
= bluetoothReadline();
297 if (strncmp(line
, "OK+", 3)) {
298 bluetoothState
= BLUETOOTH_STATE_IDLE
;
301 bluetoothInit(BLUETOOTH_FACTORY_BAUDRATE
);
302 const char btMessage
[] = "TTM:BPS-115200";
303 bluetoothWriteString(btMessage
);
304 bluetoothState
= BLUETOOTH_WAIT_BAUDRATE_CHANGE
;
305 waitEnd
= get_tmr10ms() + 250; // 2.5s
309 else if (bluetoothState
== BLUETOOTH_WAIT_BAUDRATE_CHANGE
) {
310 if (get_tmr10ms() > waitEnd
) {
311 bluetoothState
= BLUETOOTH_INIT
;
315 else if (IS_BLUETOOTH_TRAINER()){
316 bluetoothState
= BLUETOOTH_STATE_CONNECTED
;
317 bluetoothWriteWakeup();
318 bluetoothSendTrainer();
324 void bluetoothWakeup()
326 tmr10ms_t now
= get_tmr10ms();
328 if (now
< bluetoothWakeupTime
)
331 bluetoothWakeupTime
= now
+ 5; /* 50ms default */
333 if (g_eeGeneral
.bluetoothMode
== BLUETOOTH_OFF
|| (g_eeGeneral
.bluetoothMode
== BLUETOOTH_TRAINER
&& !IS_BLUETOOTH_TRAINER())) {
334 if (bluetoothState
!= BLUETOOTH_STATE_OFF
) {
336 bluetoothState
= BLUETOOTH_STATE_OFF
;
338 bluetoothWakeupTime
= now
+ 10; /* 100ms */
340 else if (bluetoothState
== BLUETOOTH_STATE_OFF
) {
341 bluetoothInit(BLUETOOTH_FACTORY_BAUDRATE
);
342 bluetoothState
= BLUETOOTH_STATE_FACTORY_BAUDRATE_INIT
;
345 if (bluetoothState
!= BLUETOOTH_STATE_OFF
) {
346 bluetoothWriteWakeup();
347 if (bluetoothIsWriting()) {
352 if (bluetoothState
== BLUETOOTH_STATE_FACTORY_BAUDRATE_INIT
) {
353 bluetoothWriteString("AT+BAUD4\r\n");
354 bluetoothState
= BLUETOOTH_STATE_BAUDRATE_SENT
;
355 bluetoothWakeupTime
= now
+ 10; /* 100ms */
357 else if (bluetoothState
== BLUETOOTH_STATE_BAUDRATE_SENT
) {
358 bluetoothInit(BLUETOOTH_DEFAULT_BAUDRATE
);
359 bluetoothState
= BLUETOOTH_STATE_BAUDRATE_INIT
;
360 bluetoothReadline(false);
361 bluetoothWakeupTime
= now
+ 10; /* 100ms */
363 else if (bluetoothState
== BLUETOOTH_STATE_CONNECTED
) {
364 if (g_eeGeneral
.bluetoothMode
== BLUETOOTH_TRAINER
&& g_model
.trainerMode
== TRAINER_MODE_MASTER_BLUETOOTH
) {
365 bluetoothReceiveTrainer();
368 if (g_eeGeneral
.bluetoothMode
== BLUETOOTH_TRAINER
&& g_model
.trainerMode
== TRAINER_MODE_SLAVE_BLUETOOTH
) {
369 bluetoothSendTrainer();
370 bluetoothWakeupTime
= now
+ 2; /* 20ms */
372 bluetoothReadline(); // to deal with "ERROR"
376 char * line
= bluetoothReadline();
377 if (bluetoothState
== BLUETOOTH_STATE_BAUDRATE_INIT
) {
379 char * cur
= strAppend(command
, BLUETOOTH_COMMAND_NAME
);
380 uint8_t len
= ZLEN(g_eeGeneral
.bluetoothName
);
382 for (int i
= 0; i
< len
; i
++) {
383 *cur
++ = idx2char(g_eeGeneral
.bluetoothName
[i
]);
387 #if defined(PCBHORUS)
388 cur
= strAppend(cur
, "Horus");
390 cur
= strAppend(cur
, "Taranis");
393 strAppend(cur
, "\r\n");
394 bluetoothWriteString(command
);
395 bluetoothState
= BLUETOOTH_STATE_NAME_SENT
;
397 else if (bluetoothState
== BLUETOOTH_STATE_NAME_SENT
&& (!strncmp(line
, "OK+", 3) || !strncmp(line
, "Central:", 8) || !strncmp(line
, "Peripheral:", 11))) {
398 bluetoothWriteString("AT+TXPW3\r\n");
399 bluetoothState
= BLUETOOTH_STATE_POWER_SENT
;
401 else if (bluetoothState
== BLUETOOTH_STATE_POWER_SENT
&& (!strncmp(line
, "Central:", 8) || !strncmp(line
, "Peripheral:", 11))) {
402 if (g_eeGeneral
.bluetoothMode
== BLUETOOTH_TRAINER
&& g_model
.trainerMode
== TRAINER_MODE_MASTER_BLUETOOTH
)
403 bluetoothWriteString("AT+ROLE1\r\n");
405 bluetoothWriteString("AT+ROLE0\r\n");
406 bluetoothState
= BLUETOOTH_STATE_ROLE_SENT
;
408 else if (bluetoothState
== BLUETOOTH_STATE_ROLE_SENT
&& (!strncmp(line
, "Central:", 8) || !strncmp(line
, "Peripheral:", 11))) {
409 bluetoothState
= BLUETOOTH_STATE_IDLE
;
411 else if (bluetoothState
== BLUETOOTH_STATE_DISCOVER_REQUESTED
) {
412 bluetoothWriteString("AT+DISC?\r\n");
413 bluetoothState
= BLUETOOTH_STATE_DISCOVER_SENT
;
415 else if (bluetoothState
== BLUETOOTH_STATE_DISCOVER_SENT
&& !strcmp(line
, "OK+DISCS")) {
416 bluetoothState
= BLUETOOTH_STATE_DISCOVER_START
;
418 else if (bluetoothState
== BLUETOOTH_STATE_DISCOVER_START
&& !strncmp(line
, "OK+DISC:", 8)) {
419 strcpy(bluetoothDistantAddr
, &line
[8]); // TODO quick & dirty
421 else if (bluetoothState
== BLUETOOTH_STATE_DISCOVER_START
&& !strcmp(line
, "OK+DISCE")) {
422 bluetoothState
= BLUETOOTH_STATE_DISCOVER_END
;
424 else if (bluetoothState
== BLUETOOTH_STATE_BIND_REQUESTED
) {
426 strAppend(strAppend(strAppend(command
, "AT+CON"), bluetoothDistantAddr
), "\r\n");
427 bluetoothWriteString(command
);
428 bluetoothState
= BLUETOOTH_STATE_CONNECT_SENT
;
430 else if ((bluetoothState
== BLUETOOTH_STATE_IDLE
|| bluetoothState
== BLUETOOTH_STATE_DISCONNECTED
|| bluetoothState
== BLUETOOTH_STATE_CONNECT_SENT
) && !strncmp(line
, "Connected:", 10)) {
431 strcpy(bluetoothDistantAddr
, &line
[10]); // TODO quick & dirty
432 bluetoothState
= BLUETOOTH_STATE_CONNECTED
;
433 if (g_model
.trainerMode
== TRAINER_MODE_SLAVE_BLUETOOTH
) {
434 bluetoothWakeupTime
+= 500; // it seems a 5s delay is needed before sending the 1st frame
437 else if (bluetoothState
== BLUETOOTH_STATE_DISCONNECTED
&& !line
) {
439 strAppend(strAppend(strAppend(command
, "AT+CON"), bluetoothDistantAddr
), "\r\n");
440 bluetoothWriteString(command
);
441 bluetoothWakeupTime
= now
+ 200; /* 2s */