Fix doc path
[opentx.git] / radio / src / bluetooth.cpp
blob50bc769bd76001ea7708b5b856756ec0ae0764dd
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
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.
21 #include "opentx.h"
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"
27 #else
28 #define BLUETOOTH_COMMAND_NAME "TTM:REN-"
29 #define BLUETOOTH_ANSWER_NAME "TTM:REN"
30 #define BLUETOOTH_COMMAND_BAUD_115200 "TTM:BPS-115200"
31 #endif
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)
48 TRACE_NOCRLF("BT>");
49 for (int i=0; i<length; i++) {
50 TRACE_NOCRLF(" %02X", data[i]);
51 btTxFifo.push(data[i]);
53 TRACE_NOCRLF("\r\n");
54 bluetoothWriteWakeup();
57 void bluetoothWriteString(const char * str)
59 TRACE("BT> %s", str);
60 while (*str != 0) {
61 btTxFifo.push(*str++);
63 bluetoothWriteWakeup();
66 char * bluetoothReadline(bool error_reset)
68 uint8_t byte;
70 while (1) {
71 if (!btRxFifo.pop(byte)) {
72 #if defined(PCBX9E) && !defined(USEHORUSBT) // X9E BT module can get unresponsive
73 TRACE("NO RESPONSE FROM BT MODULE");
74 #endif
75 return NULL;
77 TRACE_NOCRLF("%02X ", byte);
78 if (byte == '\n') {
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
85 TRACE("BT error...");
86 #else
87 TRACE("BT Reset...");
88 bluetoothDone();
89 bluetoothState = BLUETOOTH_STATE_OFF;
90 bluetoothWakeupTime = get_tmr10ms() + 100; /* 1s */
91 #endif
92 return NULL;
94 else {
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;
102 else {
103 bluetoothBufferIndex = 0;
106 else {
107 bluetoothBuffer[bluetoothBufferIndex++] = byte;
108 bluetoothBufferIndex &= (BLUETOOTH_LINE_LENGTH-1);
113 void bluetoothProcessTrainerFrame(const uint8_t * bluetoothBuffer)
115 TRACE("");
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)
131 if (data == '\n') {
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;
146 switch (dataState) {
147 case STATE_DATA_START:
148 if (data == START_STOP) {
149 dataState = STATE_DATA_IN_FRAME ;
150 bluetoothBufferIndex = 0;
152 else {
153 bluetoothAppendTrainerByte(data);
155 break;
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;
165 else {
166 bluetoothAppendTrainerByte(data);
168 break;
170 case STATE_DATA_XOR:
171 bluetoothAppendTrainerByte(data ^ STUFF_MASK);
172 dataState = STATE_DATA_IN_FRAME;
173 break;
175 case STATE_DATA_IDLE:
176 if (data == START_STOP) {
177 bluetoothBufferIndex = 0;
178 dataState = STATE_DATA_START;
180 else {
181 bluetoothAppendTrainerByte(data);
183 break;
186 if (bluetoothBufferIndex >= BLUETOOTH_PACKET_SIZE) {
187 uint8_t crc = 0x00;
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;
206 byte ^= STUFF_MASK;
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;
220 bluetoothCrc = 0x00;
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()
249 uint8_t byte;
251 while (1) {
252 if (!btRxFifo.pop(byte)) {
253 return;
256 TRACE_NOCRLF("%02X ", byte);
258 bluetoothProcessTrainerByte(byte);
262 #if defined(PCBX9E) && !defined(USEHORUSBT)
263 void bluetoothWakeup(void)
265 #if !defined(SIMU)
266 if (!g_eeGeneral.bluetoothMode) {
267 if (bluetoothState != BLUETOOTH_INIT) {
268 bluetoothDone();
269 bluetoothState = BLUETOOTH_INIT;
272 else {
273 static tmr10ms_t waitEnd = 0;
274 if (bluetoothState != BLUETOOTH_STATE_IDLE) {
276 if (bluetoothState == BLUETOOTH_INIT) {
277 bluetoothInit(BLUETOOTH_DEFAULT_BAUDRATE);
278 char command[32];
279 char * cur = strAppend(command, BLUETOOTH_COMMAND_NAME);
280 uint8_t len = ZLEN(g_eeGeneral.bluetoothName);
281 if (len > 0) {
282 for (int i = 0; i < len; i++) {
283 *cur++ = idx2char(g_eeGeneral.bluetoothName[i]);
286 else {
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;
300 else {
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();
321 #endif
323 #else // PCBX9E
324 void bluetoothWakeup()
326 tmr10ms_t now = get_tmr10ms();
328 if (now < bluetoothWakeupTime)
329 return;
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) {
335 bluetoothDone();
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()) {
348 return;
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();
367 else {
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"
375 else {
376 char * line = bluetoothReadline();
377 if (bluetoothState == BLUETOOTH_STATE_BAUDRATE_INIT) {
378 char command[32];
379 char * cur = strAppend(command, BLUETOOTH_COMMAND_NAME);
380 uint8_t len = ZLEN(g_eeGeneral.bluetoothName);
381 if (len > 0) {
382 for (int i = 0; i < len; i++) {
383 *cur++ = idx2char(g_eeGeneral.bluetoothName[i]);
386 else {
387 #if defined(PCBHORUS)
388 cur = strAppend(cur, "Horus");
389 #else
390 cur = strAppend(cur, "Taranis");
391 #endif
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");
404 else
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) {
425 char command[32];
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) {
438 char command[32];
439 strAppend(strAppend(strAppend(command, "AT+CON"), bluetoothDistantAddr), "\r\n");
440 bluetoothWriteString(command);
441 bluetoothWakeupTime = now + 200; /* 2s */
445 #endif