Bluetooth SPI MBED

Dependencies:   mbed-rtos mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "mbed.h"
00002 #include "rtos.h"
00003 #include "sdep.h "
00004 #include "Adafruit_FIFO.h "
00005 #include <stdio.h>
00006 
00007 #define BLUEFRUIT_MODE_COMMAND   HIGH
00008 #define BLUEFRUIT_MODE_DATA      LOW
00009 #define BLE_BUFSIZE              4*SDEP_MAX_PACKETSIZE
00010 
00011 
00012 #define SPI_IGNORED_BYTE          0xFEu /**< SPI default character. Character clocked out in case of an ignored transaction. */
00013 #define SPI_OVERREAD_BYTE         0xFFu /**< SPI over-read character. Character clocked out after an over-read of the transmit buffer. */
00014 #define SPI_DEFAULT_DELAY_US      50
00015 
00016 DigitalOut myled(LED1);
00017 
00018 Serial pc(USBTX,USBRX);
00019 
00020 SPI spi(p5, p6, p7); // mosi, miso, sclk
00021 
00022 DigitalOut cs(p21);
00023 DigitalIn m_irq_pin(p22);
00024 DigitalOut ble_reset(p23);
00025 
00026 void enable_spi() {
00027     cs = 0;
00028 }
00029 
00030 void disable_spi() {
00031     cs = 1;
00032 }
00033 
00034 // TX
00035 uint8_t         m_tx_buffer[SDEP_MAX_PACKETSIZE] = {0};
00036 uint8_t         m_tx_count = 0;
00037 
00038 // RX
00039 uint8_t         m_rx_buffer[BLE_BUFSIZE * 2] = {0};
00040 Adafruit_FIFO   m_rx_fifo(m_rx_buffer, sizeof(m_rx_buffer), 1, true);
00041 
00042 enum BLE_MODE {
00043     COMMAND = 0,
00044     DATA
00045 };
00046 
00047 BLE_MODE bleMode = COMMAND;
00048 
00049 
00050 // prototypes
00051 void spixfer(void *buff, size_t len);
00052 uint8_t spixfer(uint8_t x);
00053 bool bleGetResponse(void);
00054 bool sendPacket(uint16_t command, const uint8_t* buf, uint8_t count, uint8_t more_data);
00055 size_t bleWriteChar(uint8_t c);
00056 void bleWrite(char *cmd);
00057 
00058 
00059 volatile unsigned long _millis = 0;
00060 unsigned long millis(void) {
00061     return _millis;
00062 }
00063 
00064 
00065 class TimeoutTimer
00066 {
00067   private:
00068     uint32_t start;
00069     uint32_t interval;
00070 
00071   public:
00072     TimeoutTimer()              { start = millis(); interval = 0; }
00073     TimeoutTimer(uint32_t msec) { set(msec); }
00074 
00075     void set(uint32_t msec)     { start = millis(); interval = msec; }
00076     bool expired(void)  const   { return (millis() - start) >= interval; }
00077     void restart(void)          { start = millis(); }
00078     void reset(void)            { start += interval; } // used for periodic invoke to prevent drift
00079 };
00080 
00081 
00082 uint16_t word(uint8_t h, uint8_t l) {
00083     uint16_t res = h;
00084     res <<= 8;
00085     res |= l;
00086     return res;
00087 }
00088 
00089 
00090 uint32_t _timeout = 250;
00091 
00092 
00093 bool bleGetPacket(sdepMsgResponse_t * p_response)
00094 {
00095   // Wait until IRQ is asserted, double timeout since some commands take long time to start responding
00096   TimeoutTimer tt(2*_timeout);
00097   
00098   while ( !m_irq_pin ) {
00099     if (tt.expired()) return false;
00100   }
00101   
00102   sdepMsgHeader_t * p_header = &p_response->header;
00103   
00104   enable_spi();
00105 
00106   tt.set(_timeout);
00107 
00108   do {
00109     if ( tt.expired() ) break;
00110 
00111     p_header->msg_type = spixfer(0xff);
00112 
00113     if (p_header->msg_type == SPI_IGNORED_BYTE)
00114     {
00115       // Bluefruit may not be ready
00116       // Disable & Re-enable CS with a bit of delay for Bluefruit to ready itself
00117       disable_spi();
00118       wait_us(50);
00119       enable_spi();
00120     }
00121     else if (p_header->msg_type == SPI_OVERREAD_BYTE)
00122     {
00123       // IRQ may not be pulled down by Bluefruit when returning all data in previous transfer.
00124       // This could happen when Arduino MCU is running at fast rate comparing to Bluefruit's MCU,
00125       // causing an SPI_OVERREAD_BYTE to be returned at stage.
00126       //
00127       // Walkaround: Disable & Re-enable CS with a bit of delay and keep waiting
00128       // TODO IRQ is supposed to be OFF then ON, it is better to use GPIO trigger interrupt.
00129 
00130       disable_spi();
00131       // wait for the clock to be enabled..
00132 //      while (!digitalRead(m_irq_pin)) {
00133 //        if ( tt.expired() ) break;
00134 //      }
00135 //      if (!digitalRead(m_irq_pin)) break;
00136       wait_us(50);
00137       enable_spi();
00138     }
00139   }  while (p_header->msg_type == SPI_IGNORED_BYTE || p_header->msg_type == SPI_OVERREAD_BYTE);
00140 
00141   bool result=false;
00142 
00143   // Not a loop, just a way to avoid goto with error handling
00144   do
00145   {
00146     // Look for the header
00147     // note that we should always get the right header at this point, and not doing so will really mess up things.
00148     while ( p_header->msg_type != SDEP_MSGTYPE_RESPONSE && p_header->msg_type != SDEP_MSGTYPE_ERROR && !tt.expired() )
00149     {
00150       p_header->msg_type = spixfer(0xff);
00151     }
00152     
00153     if ( tt.expired() ) break;
00154     
00155     memset( (&p_header->msg_type)+1, 0xff, sizeof(sdepMsgHeader_t ) - 1);
00156     spixfer((&p_header->msg_type)+1, sizeof(sdepMsgHeader_t ) - 1);
00157 
00158     // Command is 16-bit at odd address, may have alignment issue with 32-bit chip
00159     uint16_t cmd_id = word(p_header->cmd_id_high, p_header->cmd_id_low);
00160 
00161     // Error Message Response
00162     if ( p_header->msg_type == SDEP_MSGTYPE_ERROR ) break;
00163 
00164     // Invalid command
00165     if (!(cmd_id == SDEP_CMDTYPE_AT_WRAPPER ||
00166           cmd_id == SDEP_CMDTYPE_BLE_UARTTX ||
00167           cmd_id == SDEP_CMDTYPE_BLE_UARTRX) )
00168     {
00169       break;
00170     }
00171 
00172     // Invalid length
00173     if(p_header->length > SDEP_MAX_PACKETSIZE) break;
00174 
00175     // read payload
00176     memset(p_response->payload, 0xff, p_header->length);
00177     spixfer(p_response->payload, p_header->length);
00178 
00179     result = true;
00180   }while(0);
00181 
00182   disable_spi();
00183 
00184   return result;
00185 }
00186 
00187 
00188 /******************************************************************************/
00189 /*!
00190 
00191 */
00192 /******************************************************************************/
00193 void spixfer(void *buff, size_t len) {
00194   uint8_t *p = (uint8_t *)buff;
00195   while (len--) {
00196     p[0] = spixfer(p[0]);
00197     p++;
00198   }
00199 }
00200 
00201 /******************************************************************************/
00202 /*!
00203 
00204 */
00205 /******************************************************************************/
00206 uint8_t spixfer(uint8_t x) {
00207   return spi.write(x);
00208 }
00209 
00210 
00211 /******************************************************************************/
00212 /*!
00213     @brief  Try to perform an full AT response transfer from Bluefruit, or execute
00214             as many SPI transaction as internal FIFO can hold up.
00215 
00216     @note   If verbose is enabled, all the received data will be print to Serial
00217 
00218     @return
00219       - true  : if succeeded
00220       - false : if failed
00221 */
00222 /******************************************************************************/
00223 bool bleGetResponse(void)
00224 {
00225   // Try to read data from Bluefruit if there is enough room in the fifo
00226   while ( m_rx_fifo.remaining() >= SDEP_MAX_PACKETSIZE )
00227   {
00228     // Get a SDEP packet
00229     sdepMsgResponse_t  msg_response;
00230     memset(&msg_response, 0, sizeof(sdepMsgResponse_t ));
00231 
00232     if ( !bleGetPacket(&msg_response) ) return false;
00233 
00234     // Write to fifo
00235     if ( msg_response.header.length > 0)
00236     {
00237       m_rx_fifo.write_n(msg_response.payload, msg_response.header.length);
00238     }
00239 
00240     // No more packet data
00241     if ( !msg_response.header.more_data ) break;
00242 
00243     // It takes a bit since all Data received to IRQ to get LOW
00244     // May need to delay a bit for it to be stable before the next try
00245     wait_us(50);
00246   }
00247 
00248   return true;
00249 }
00250 
00251 
00252 void bleWrite(char *cmd) {
00253     while (*cmd != '\0') {
00254         bleWriteChar((uint8_t) *cmd);
00255         cmd += 1;
00256     }
00257 }
00258 
00259 
00260 /******************************************************************************/
00261 /*!
00262     @brief Check if the response from the previous command is ready
00263 
00264     @return 'true' if a response is ready, otherwise 'false'
00265 */
00266 /******************************************************************************/
00267 int bleAvailable(void)
00268 {
00269   if (! m_rx_fifo.empty() ) {
00270     return m_rx_fifo.count();
00271   }
00272 
00273   if ( bleMode == DATA )
00274   {
00275     // DATA Mode: query for BLE UART data
00276     sendPacket(SDEP_CMDTYPE_BLE_UARTRX, NULL, 0, 0);
00277 
00278     // Waiting to get response from Bluefruit
00279     bleGetResponse();
00280 
00281     return m_rx_fifo.count();
00282   }else{
00283     return m_irq_pin;
00284   }
00285 }
00286 
00287 /******************************************************************************/
00288 /*!
00289     @brief Get a byte from response data, perform SPI transaction if needed
00290 
00291     @return -1 if no data is available
00292 */
00293 /******************************************************************************/
00294 int bleRead(void)
00295 {
00296   uint8_t ch;
00297 
00298   // try to grab from buffer first...
00299   if (!m_rx_fifo.empty()) {
00300     m_rx_fifo.read(&ch);
00301     return (int)ch;
00302   }
00303 
00304   if ( bleMode == DATA )
00305   {
00306     // DATA Mode: query for BLE UART data
00307     sendPacket(SDEP_CMDTYPE_BLE_UARTRX, NULL, 0, 0);
00308 
00309     // Waiting to get response from Bluefruit
00310     bleGetResponse();
00311   }else
00312   {
00313     // COMMAND Mode: Only read data from Bluefruit if IRQ is raised
00314     if ( m_irq_pin ) bleGetResponse();
00315   }
00316 
00317   return m_rx_fifo.read(&ch) ? ((int) ch) : EOF;
00318 
00319 }
00320 
00321 
00322 uint16_t bleReadLine(char * buf, uint16_t bufsize)
00323 {
00324   uint16_t timeout = 1000 * 10;
00325   bool multiline = false;
00326   uint16_t replyidx = 0;
00327 
00328   while (timeout--) {
00329     while(bleAvailable()) {
00330       //pc.printf("SPI: Available\n");
00331       char c = bleRead();
00332       //pc.printf("SPI: %d\n", (int) c);
00333       //SerialDebug.println(c);
00334 
00335       if (c == '\r') continue;
00336 
00337       if (c == '\n') {
00338         // the first '\n' is ignored
00339         if (replyidx == 0) continue;
00340 
00341         if (!multiline) {
00342           timeout = 0;
00343           break;
00344         }
00345       }
00346       buf[replyidx] = c;
00347       replyidx++;
00348 
00349       // Buffer is full
00350       if (replyidx >= bufsize) {
00351         //if (_verbose) { SerialDebug.println("*overflow*"); }  // for my debuggin' only!
00352         timeout = 0;
00353         break;
00354       }
00355     }
00356 
00357     // delay if needed
00358     if (timeout) {
00359         Thread::wait(1);
00360     }
00361   }
00362 
00363   buf[replyidx] = 0;  // null term
00364 
00365   return replyidx;
00366 }
00367 
00368 bool waitForOK(void)
00369 {
00370     // Use temp buffer to avoid overwrite returned result if any
00371     char tempbuf[BLE_BUFSIZE + 1];
00372 
00373     while (bleReadLine(tempbuf, BLE_BUFSIZE)) {
00374         // pc.printf("SPI: %s\n", tempbuf);
00375         if (strcmp(tempbuf, "OK") == 0) return true;
00376         if (strcmp(tempbuf, "ERROR") == 0) return false;
00377     }
00378     return false;
00379 }
00380 
00381 bool sendATCommand(char *cmd) {
00382     bleMode = COMMAND;
00383     bleWrite(cmd);
00384     bleWrite("\r\n");
00385     bool result = waitForOK();
00386     bleMode = DATA;
00387     return result;
00388 }
00389 
00390 bool sendCommandCheckOK(char *cmd) {
00391     return sendATCommand(cmd);
00392 }
00393 
00394 // =============================================================================
00395 
00396 void flush(void)
00397 {
00398     m_rx_fifo.clear();
00399 }
00400 
00401 uint8_t highByte(uint16_t x) {
00402     return (uint8_t) (x >> 8);
00403 }
00404 
00405 uint8_t lowByte(uint16_t x) {
00406     return (uint8_t) (x & 0xff);
00407 }
00408 
00409 bool sendPacket(uint16_t command, const uint8_t* buf, uint8_t count, uint8_t more_data) {
00410   // flush old response before sending the new command
00411   if (more_data == 0) flush();
00412 
00413   sdepMsgCommand_t  msgCmd;
00414 
00415   msgCmd.header.msg_type    = SDEP_MSGTYPE_COMMAND;
00416   msgCmd.header.cmd_id_high = highByte(command);
00417   msgCmd.header.cmd_id_low  = lowByte(command);
00418   msgCmd.header.length      = count;
00419   msgCmd.header.more_data   = (count == SDEP_MAX_PACKETSIZE) ? more_data : 0;
00420 
00421   // Copy payload
00422   if ( buf != NULL && count > 0) memcpy(msgCmd.payload, buf, count);
00423 
00424   enable_spi();
00425 
00426   TimeoutTimer tt(_timeout);
00427 
00428   // Bluefruit may not be ready
00429   while ( ( spixfer(msgCmd.header.msg_type) == SPI_IGNORED_BYTE ) && !tt.expired() )
00430   {
00431     // Disable & Re-enable CS with a bit of delay for Bluefruit to ready itself
00432     disable_spi();
00433     wait_us(SPI_DEFAULT_DELAY_US);
00434     enable_spi();
00435   }
00436 
00437   bool result = !tt.expired();
00438   if ( result )
00439   {
00440     // transfer the rest of the data
00441     spixfer((void*) (((uint8_t*)&msgCmd) +1), sizeof(sdepMsgHeader_t )+count-1);
00442   }
00443 
00444   disable_spi();
00445 
00446   return result;
00447 }
00448 
00449 size_t bleWriteChar(uint8_t c) {
00450   if (bleMode == DATA)
00451   {
00452     sendPacket(SDEP_CMDTYPE_BLE_UARTTX, &c, 1, 0);
00453     bleGetResponse();
00454     return 1;
00455   }
00456 
00457   // Following code handle BLUEFRUIT_MODE_COMMAND
00458 
00459   // Final packet due to \r or \n terminator
00460   if (c == '\r' || c == '\n')
00461   {
00462     if (m_tx_count > 0)
00463     {
00464       bool result = sendPacket(SDEP_CMDTYPE_AT_WRAPPER, m_tx_buffer, m_tx_count, 0);
00465       m_tx_count = 0;
00466       if (!result) {
00467         return 0;
00468       }
00469     }
00470   }
00471   // More than max packet buffered --> send with more_data = 1
00472   else if (m_tx_count == SDEP_MAX_PACKETSIZE)
00473   {
00474     bool result = sendPacket(SDEP_CMDTYPE_AT_WRAPPER, m_tx_buffer, m_tx_count, 1);
00475 
00476     m_tx_buffer[0] = c;
00477     m_tx_count = 1;
00478     if (!result) {
00479       return 0;
00480     }
00481   }
00482   // Not enough data, continue to buffer
00483   else
00484   {
00485     m_tx_buffer[m_tx_count++] = c;
00486   }
00487   return 1;
00488 }
00489 
00490 int32_t readline_parseInt(void)
00491 {
00492   char buffer[101] = { 0 };
00493   uint16_t len = bleReadLine(buffer, 100);
00494   if (len == 0) return 0;
00495 
00496   // also parsed hex number e.g 0xADAF
00497   int32_t val = strtol(buffer, NULL, 0);
00498 
00499   return val;
00500 }
00501 
00502 bool send_arg_get_resp(int32_t *reply) {
00503     bleWrite("\r\n"); // execute command
00504 
00505     // parse integer response if required
00506     if (reply)
00507     {
00508         (*reply) = readline_parseInt();
00509     }
00510 
00511     // check OK or ERROR status
00512     return waitForOK();
00513 }
00514 
00515 int32_t sendATCommandIntReply(char *cmd) {
00516     int32_t reply = 0;
00517     BLE_MODE current_mode = bleMode;
00518 
00519     // switch mode if necessary to execute command
00520     bleMode = COMMAND;
00521 
00522     // Execute command with parameter and get response
00523     bleWrite(cmd);
00524     send_arg_get_resp(&reply);
00525 
00526     // switch back if necessary
00527     if ( current_mode == DATA ) bleMode = DATA;
00528     return reply;
00529 }
00530 
00531 bool sendInitializePattern() {
00532     return sendPacket(SDEP_CMDTYPE_INITIALIZE, NULL, 0, 0);
00533 }
00534 
00535 bool isBLEConnected() {
00536     int32_t connected = 0;
00537     connected = sendATCommandIntReply("AT+GAPGETCONN");
00538     return connected;
00539 }
00540 
00541  
00542 void timer_function_millis(void const *n) {
00543     _millis += 1;
00544 }
00545 
00546 int main() {
00547     // timer to bump _millis
00548     RtosTimer millis_timer(timer_function_millis, osTimerPeriodic, (void *)0);
00549     millis_timer.start(1);
00550     
00551     pc.baud(115200);
00552     spi.format(8, 0);
00553     spi.frequency(4000000);
00554     
00555     pc.printf("Prepare to initiaize BLE\n");
00556     
00557     // initialize BLE
00558     cs = 1;
00559     if (!sendInitializePattern()) pc.printf("BLE Init Failed\n");
00560     ble_reset = 1;
00561     ble_reset = 0;
00562     Thread::wait(10);
00563     ble_reset = 1;
00564     Thread::wait(500);
00565     cs = 1;
00566     
00567     pc.printf("Set BLE Name\n");
00568     
00569     // change name
00570     if (!sendCommandCheckOK("AT+GAPDEVNAME=AdafruitDJ")) {
00571         pc.printf("Could not set device name.\n");
00572     }
00573     
00574     // wait until connected
00575     while (!isBLEConnected()) {
00576         Thread::wait(100);
00577     }
00578     
00579     pc.printf("Ready\n");
00580     
00581     Thread::wait(1000);
00582     
00583     int i = 0;
00584     while (true) {
00585         bleMode = DATA;
00586         // send character
00587         char str[40] = { 0 };
00588         sprintf(str, "Hello DJ %d\r\n", i);
00589         bleWrite(str);
00590         Thread::wait(1000);
00591         pc.printf("Send\n");
00592         i += 1;
00593     }
00594     
00595     Thread::wait(osWaitForever);
00596 }