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(PCBX7) || defined(PCBHORUS) || 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)
265 if (!g_eeGeneral
.bluetoothMode
) {
266 if (bluetoothState
!= BLUETOOTH_INIT
) {
268 bluetoothState
= BLUETOOTH_INIT
;
272 static tmr10ms_t waitEnd
= 0;
273 if (bluetoothState
!= BLUETOOTH_STATE_IDLE
) {
275 if (bluetoothState
== BLUETOOTH_INIT
) {
276 bluetoothInit(BLUETOOTH_DEFAULT_BAUDRATE
);
278 char * cur
= strAppend(command
, BLUETOOTH_COMMAND_NAME
);
279 uint8_t len
= ZLEN(g_eeGeneral
.bluetoothName
);
281 for (int i
= 0; i
< len
; i
++) {
282 *cur
++ = idx2char(g_eeGeneral
.bluetoothName
[i
]);
286 cur
= strAppend(cur
, "Taranis-X9E");
288 strAppend(cur
, "\r\n");
289 bluetoothWriteString(command
);
290 bluetoothState
= BLUETOOTH_WAIT_TTM
;
291 waitEnd
= get_tmr10ms() + 25; // 250ms
293 else if (bluetoothState
== BLUETOOTH_WAIT_TTM
) {
294 if (get_tmr10ms() > waitEnd
) {
295 char * line
= bluetoothReadline();
296 if (strncmp(line
, "OK+", 3)) {
297 bluetoothState
= BLUETOOTH_STATE_IDLE
;
300 bluetoothInit(BLUETOOTH_FACTORY_BAUDRATE
);
301 const char btMessage
[] = "TTM:BPS-115200";
302 bluetoothWriteString(btMessage
);
303 bluetoothState
= BLUETOOTH_WAIT_BAUDRATE_CHANGE
;
304 waitEnd
= get_tmr10ms() + 250; // 2.5s
308 else if (bluetoothState
== BLUETOOTH_WAIT_BAUDRATE_CHANGE
) {
309 if (get_tmr10ms() > waitEnd
) {
310 bluetoothState
= BLUETOOTH_INIT
;
314 else if (IS_BLUETOOTH_TRAINER()){
315 bluetoothState
= BLUETOOTH_STATE_CONNECTED
;
316 bluetoothWriteWakeup();
317 bluetoothSendTrainer();
322 void bluetoothWakeup()
324 tmr10ms_t now
= get_tmr10ms();
326 if (now
< bluetoothWakeupTime
)
329 bluetoothWakeupTime
= now
+ 5; /* 50ms default */
331 if (g_eeGeneral
.bluetoothMode
== BLUETOOTH_OFF
|| (g_eeGeneral
.bluetoothMode
== BLUETOOTH_TRAINER
&& !IS_BLUETOOTH_TRAINER())) {
332 if (bluetoothState
!= BLUETOOTH_STATE_OFF
) {
334 bluetoothState
= BLUETOOTH_STATE_OFF
;
336 bluetoothWakeupTime
= now
+ 10; /* 100ms */
338 else if (bluetoothState
== BLUETOOTH_STATE_OFF
) {
339 bluetoothInit(BLUETOOTH_FACTORY_BAUDRATE
);
340 bluetoothState
= BLUETOOTH_STATE_FACTORY_BAUDRATE_INIT
;
343 if (bluetoothState
!= BLUETOOTH_STATE_OFF
) {
344 bluetoothWriteWakeup();
345 if (bluetoothIsWriting()) {
350 if (bluetoothState
== BLUETOOTH_STATE_FACTORY_BAUDRATE_INIT
) {
351 bluetoothWriteString("AT+BAUD4\r\n");
352 bluetoothState
= BLUETOOTH_STATE_BAUDRATE_SENT
;
353 bluetoothWakeupTime
= now
+ 10; /* 100ms */
355 else if (bluetoothState
== BLUETOOTH_STATE_BAUDRATE_SENT
) {
356 bluetoothInit(BLUETOOTH_DEFAULT_BAUDRATE
);
357 bluetoothState
= BLUETOOTH_STATE_BAUDRATE_INIT
;
358 bluetoothReadline(false);
359 bluetoothWakeupTime
= now
+ 10; /* 100ms */
361 else if (bluetoothState
== BLUETOOTH_STATE_CONNECTED
) {
362 if (g_eeGeneral
.bluetoothMode
== BLUETOOTH_TRAINER
&& g_model
.trainerMode
== TRAINER_MODE_MASTER_BLUETOOTH
) {
363 bluetoothReceiveTrainer();
366 if (g_eeGeneral
.bluetoothMode
== BLUETOOTH_TRAINER
&& g_model
.trainerMode
== TRAINER_MODE_SLAVE_BLUETOOTH
) {
367 bluetoothSendTrainer();
368 bluetoothWakeupTime
= now
+ 2; /* 20ms */
370 bluetoothReadline(); // to deal with "ERROR"
374 char * line
= bluetoothReadline();
375 if (bluetoothState
== BLUETOOTH_STATE_BAUDRATE_INIT
) {
377 char * cur
= strAppend(command
, BLUETOOTH_COMMAND_NAME
);
378 uint8_t len
= ZLEN(g_eeGeneral
.bluetoothName
);
380 for (int i
= 0; i
< len
; i
++) {
381 *cur
++ = idx2char(g_eeGeneral
.bluetoothName
[i
]);
385 #if defined(PCBHORUS)
386 cur
= strAppend(cur
, "Horus");
388 cur
= strAppend(cur
, "Taranis");
391 strAppend(cur
, "\r\n");
392 bluetoothWriteString(command
);
393 bluetoothState
= BLUETOOTH_STATE_NAME_SENT
;
395 else if (bluetoothState
== BLUETOOTH_STATE_NAME_SENT
&& (!strncmp(line
, "OK+", 3) || !strncmp(line
, "Central:", 8) || !strncmp(line
, "Peripheral:", 11))) {
396 bluetoothWriteString("AT+TXPW3\r\n");
397 bluetoothState
= BLUETOOTH_STATE_POWER_SENT
;
399 else if (bluetoothState
== BLUETOOTH_STATE_POWER_SENT
&& (!strncmp(line
, "Central:", 8) || !strncmp(line
, "Peripheral:", 11))) {
400 if (g_eeGeneral
.bluetoothMode
== BLUETOOTH_TRAINER
&& g_model
.trainerMode
== TRAINER_MODE_MASTER_BLUETOOTH
)
401 bluetoothWriteString("AT+ROLE1\r\n");
403 bluetoothWriteString("AT+ROLE0\r\n");
404 bluetoothState
= BLUETOOTH_STATE_ROLE_SENT
;
406 else if (bluetoothState
== BLUETOOTH_STATE_ROLE_SENT
&& (!strncmp(line
, "Central:", 8) || !strncmp(line
, "Peripheral:", 11))) {
407 bluetoothState
= BLUETOOTH_STATE_IDLE
;
409 else if (bluetoothState
== BLUETOOTH_STATE_DISCOVER_REQUESTED
) {
410 bluetoothWriteString("AT+DISC?\r\n");
411 bluetoothState
= BLUETOOTH_STATE_DISCOVER_SENT
;
413 else if (bluetoothState
== BLUETOOTH_STATE_DISCOVER_SENT
&& !strcmp(line
, "OK+DISCS")) {
414 bluetoothState
= BLUETOOTH_STATE_DISCOVER_START
;
416 else if (bluetoothState
== BLUETOOTH_STATE_DISCOVER_START
&& !strncmp(line
, "OK+DISC:", 8)) {
417 strcpy(bluetoothDistantAddr
, &line
[8]); // TODO quick & dirty
419 else if (bluetoothState
== BLUETOOTH_STATE_DISCOVER_START
&& !strcmp(line
, "OK+DISCE")) {
420 bluetoothState
= BLUETOOTH_STATE_DISCOVER_END
;
422 else if (bluetoothState
== BLUETOOTH_STATE_BIND_REQUESTED
) {
424 strAppend(strAppend(strAppend(command
, "AT+CON"), bluetoothDistantAddr
), "\r\n");
425 bluetoothWriteString(command
);
426 bluetoothState
= BLUETOOTH_STATE_CONNECT_SENT
;
428 else if ((bluetoothState
== BLUETOOTH_STATE_IDLE
|| bluetoothState
== BLUETOOTH_STATE_DISCONNECTED
|| bluetoothState
== BLUETOOTH_STATE_CONNECT_SENT
) && !strncmp(line
, "Connected:", 10)) {
429 strcpy(bluetoothDistantAddr
, &line
[10]); // TODO quick & dirty
430 bluetoothState
= BLUETOOTH_STATE_CONNECTED
;
431 if (g_model
.trainerMode
== TRAINER_MODE_SLAVE_BLUETOOTH
) {
432 bluetoothWakeupTime
+= 500; // it seems a 5s delay is needed before sending the 1st frame
435 else if (bluetoothState
== BLUETOOTH_STATE_DISCONNECTED
&& !line
) {
437 strAppend(strAppend(strAppend(command
, "AT+CON"), bluetoothDistantAddr
), "\r\n");
438 bluetoothWriteString(command
);
439 bluetoothWakeupTime
= now
+ 200; /* 2s */