Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: main.cpp
- Revision:
- 0:39b7f3158ebe
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Thu Apr 05 21:26:10 2018 +0000
@@ -0,0 +1,596 @@
+#include "mbed.h"
+#include "rtos.h"
+#include "sdep.h"
+#include "Adafruit_FIFO.h"
+#include <stdio.h>
+
+#define BLUEFRUIT_MODE_COMMAND HIGH
+#define BLUEFRUIT_MODE_DATA LOW
+#define BLE_BUFSIZE 4*SDEP_MAX_PACKETSIZE
+
+
+#define SPI_IGNORED_BYTE 0xFEu /**< SPI default character. Character clocked out in case of an ignored transaction. */
+#define SPI_OVERREAD_BYTE 0xFFu /**< SPI over-read character. Character clocked out after an over-read of the transmit buffer. */
+#define SPI_DEFAULT_DELAY_US 50
+
+DigitalOut myled(LED1);
+
+Serial pc(USBTX,USBRX);
+
+SPI spi(p5, p6, p7); // mosi, miso, sclk
+
+DigitalOut cs(p21);
+DigitalIn m_irq_pin(p22);
+DigitalOut ble_reset(p23);
+
+void enable_spi() {
+ cs = 0;
+}
+
+void disable_spi() {
+ cs = 1;
+}
+
+// TX
+uint8_t m_tx_buffer[SDEP_MAX_PACKETSIZE] = {0};
+uint8_t m_tx_count = 0;
+
+// RX
+uint8_t m_rx_buffer[BLE_BUFSIZE * 2] = {0};
+Adafruit_FIFO m_rx_fifo(m_rx_buffer, sizeof(m_rx_buffer), 1, true);
+
+enum BLE_MODE {
+ COMMAND = 0,
+ DATA
+};
+
+BLE_MODE bleMode = COMMAND;
+
+
+// prototypes
+void spixfer(void *buff, size_t len);
+uint8_t spixfer(uint8_t x);
+bool bleGetResponse(void);
+bool sendPacket(uint16_t command, const uint8_t* buf, uint8_t count, uint8_t more_data);
+size_t bleWriteChar(uint8_t c);
+void bleWrite(char *cmd);
+
+
+volatile unsigned long _millis = 0;
+unsigned long millis(void) {
+ return _millis;
+}
+
+
+class TimeoutTimer
+{
+ private:
+ uint32_t start;
+ uint32_t interval;
+
+ public:
+ TimeoutTimer() { start = millis(); interval = 0; }
+ TimeoutTimer(uint32_t msec) { set(msec); }
+
+ void set(uint32_t msec) { start = millis(); interval = msec; }
+ bool expired(void) const { return (millis() - start) >= interval; }
+ void restart(void) { start = millis(); }
+ void reset(void) { start += interval; } // used for periodic invoke to prevent drift
+};
+
+
+uint16_t word(uint8_t h, uint8_t l) {
+ uint16_t res = h;
+ res <<= 8;
+ res |= l;
+ return res;
+}
+
+
+uint32_t _timeout = 250;
+
+
+bool bleGetPacket(sdepMsgResponse_t* p_response)
+{
+ // Wait until IRQ is asserted, double timeout since some commands take long time to start responding
+ TimeoutTimer tt(2*_timeout);
+
+ while ( !m_irq_pin ) {
+ if (tt.expired()) return false;
+ }
+
+ sdepMsgHeader_t* p_header = &p_response->header;
+
+ enable_spi();
+
+ tt.set(_timeout);
+
+ do {
+ if ( tt.expired() ) break;
+
+ p_header->msg_type = spixfer(0xff);
+
+ if (p_header->msg_type == SPI_IGNORED_BYTE)
+ {
+ // Bluefruit may not be ready
+ // Disable & Re-enable CS with a bit of delay for Bluefruit to ready itself
+ disable_spi();
+ wait_us(50);
+ enable_spi();
+ }
+ else if (p_header->msg_type == SPI_OVERREAD_BYTE)
+ {
+ // IRQ may not be pulled down by Bluefruit when returning all data in previous transfer.
+ // This could happen when Arduino MCU is running at fast rate comparing to Bluefruit's MCU,
+ // causing an SPI_OVERREAD_BYTE to be returned at stage.
+ //
+ // Walkaround: Disable & Re-enable CS with a bit of delay and keep waiting
+ // TODO IRQ is supposed to be OFF then ON, it is better to use GPIO trigger interrupt.
+
+ disable_spi();
+ // wait for the clock to be enabled..
+// while (!digitalRead(m_irq_pin)) {
+// if ( tt.expired() ) break;
+// }
+// if (!digitalRead(m_irq_pin)) break;
+ wait_us(50);
+ enable_spi();
+ }
+ } while (p_header->msg_type == SPI_IGNORED_BYTE || p_header->msg_type == SPI_OVERREAD_BYTE);
+
+ bool result=false;
+
+ // Not a loop, just a way to avoid goto with error handling
+ do
+ {
+ // Look for the header
+ // note that we should always get the right header at this point, and not doing so will really mess up things.
+ while ( p_header->msg_type != SDEP_MSGTYPE_RESPONSE && p_header->msg_type != SDEP_MSGTYPE_ERROR && !tt.expired() )
+ {
+ p_header->msg_type = spixfer(0xff);
+ }
+
+ if ( tt.expired() ) break;
+
+ memset( (&p_header->msg_type)+1, 0xff, sizeof(sdepMsgHeader_t) - 1);
+ spixfer((&p_header->msg_type)+1, sizeof(sdepMsgHeader_t) - 1);
+
+ // Command is 16-bit at odd address, may have alignment issue with 32-bit chip
+ uint16_t cmd_id = word(p_header->cmd_id_high, p_header->cmd_id_low);
+
+ // Error Message Response
+ if ( p_header->msg_type == SDEP_MSGTYPE_ERROR ) break;
+
+ // Invalid command
+ if (!(cmd_id == SDEP_CMDTYPE_AT_WRAPPER ||
+ cmd_id == SDEP_CMDTYPE_BLE_UARTTX ||
+ cmd_id == SDEP_CMDTYPE_BLE_UARTRX) )
+ {
+ break;
+ }
+
+ // Invalid length
+ if(p_header->length > SDEP_MAX_PACKETSIZE) break;
+
+ // read payload
+ memset(p_response->payload, 0xff, p_header->length);
+ spixfer(p_response->payload, p_header->length);
+
+ result = true;
+ }while(0);
+
+ disable_spi();
+
+ return result;
+}
+
+
+/******************************************************************************/
+/*!
+
+*/
+/******************************************************************************/
+void spixfer(void *buff, size_t len) {
+ uint8_t *p = (uint8_t *)buff;
+ while (len--) {
+ p[0] = spixfer(p[0]);
+ p++;
+ }
+}
+
+/******************************************************************************/
+/*!
+
+*/
+/******************************************************************************/
+uint8_t spixfer(uint8_t x) {
+ return spi.write(x);
+}
+
+
+/******************************************************************************/
+/*!
+ @brief Try to perform an full AT response transfer from Bluefruit, or execute
+ as many SPI transaction as internal FIFO can hold up.
+
+ @note If verbose is enabled, all the received data will be print to Serial
+
+ @return
+ - true : if succeeded
+ - false : if failed
+*/
+/******************************************************************************/
+bool bleGetResponse(void)
+{
+ // Try to read data from Bluefruit if there is enough room in the fifo
+ while ( m_rx_fifo.remaining() >= SDEP_MAX_PACKETSIZE )
+ {
+ // Get a SDEP packet
+ sdepMsgResponse_t msg_response;
+ memset(&msg_response, 0, sizeof(sdepMsgResponse_t));
+
+ if ( !bleGetPacket(&msg_response) ) return false;
+
+ // Write to fifo
+ if ( msg_response.header.length > 0)
+ {
+ m_rx_fifo.write_n(msg_response.payload, msg_response.header.length);
+ }
+
+ // No more packet data
+ if ( !msg_response.header.more_data ) break;
+
+ // It takes a bit since all Data received to IRQ to get LOW
+ // May need to delay a bit for it to be stable before the next try
+ wait_us(50);
+ }
+
+ return true;
+}
+
+
+void bleWrite(char *cmd) {
+ while (*cmd != '\0') {
+ bleWriteChar((uint8_t) *cmd);
+ cmd += 1;
+ }
+}
+
+
+/******************************************************************************/
+/*!
+ @brief Check if the response from the previous command is ready
+
+ @return 'true' if a response is ready, otherwise 'false'
+*/
+/******************************************************************************/
+int bleAvailable(void)
+{
+ if (! m_rx_fifo.empty() ) {
+ return m_rx_fifo.count();
+ }
+
+ if ( bleMode == DATA )
+ {
+ // DATA Mode: query for BLE UART data
+ sendPacket(SDEP_CMDTYPE_BLE_UARTRX, NULL, 0, 0);
+
+ // Waiting to get response from Bluefruit
+ bleGetResponse();
+
+ return m_rx_fifo.count();
+ }else{
+ return m_irq_pin;
+ }
+}
+
+/******************************************************************************/
+/*!
+ @brief Get a byte from response data, perform SPI transaction if needed
+
+ @return -1 if no data is available
+*/
+/******************************************************************************/
+int bleRead(void)
+{
+ uint8_t ch;
+
+ // try to grab from buffer first...
+ if (!m_rx_fifo.empty()) {
+ m_rx_fifo.read(&ch);
+ return (int)ch;
+ }
+
+ if ( bleMode == DATA )
+ {
+ // DATA Mode: query for BLE UART data
+ sendPacket(SDEP_CMDTYPE_BLE_UARTRX, NULL, 0, 0);
+
+ // Waiting to get response from Bluefruit
+ bleGetResponse();
+ }else
+ {
+ // COMMAND Mode: Only read data from Bluefruit if IRQ is raised
+ if ( m_irq_pin ) bleGetResponse();
+ }
+
+ return m_rx_fifo.read(&ch) ? ((int) ch) : EOF;
+
+}
+
+
+uint16_t bleReadLine(char * buf, uint16_t bufsize)
+{
+ uint16_t timeout = 1000 * 10;
+ bool multiline = false;
+ uint16_t replyidx = 0;
+
+ while (timeout--) {
+ while(bleAvailable()) {
+ //pc.printf("SPI: Available\n");
+ char c = bleRead();
+ //pc.printf("SPI: %d\n", (int) c);
+ //SerialDebug.println(c);
+
+ if (c == '\r') continue;
+
+ if (c == '\n') {
+ // the first '\n' is ignored
+ if (replyidx == 0) continue;
+
+ if (!multiline) {
+ timeout = 0;
+ break;
+ }
+ }
+ buf[replyidx] = c;
+ replyidx++;
+
+ // Buffer is full
+ if (replyidx >= bufsize) {
+ //if (_verbose) { SerialDebug.println("*overflow*"); } // for my debuggin' only!
+ timeout = 0;
+ break;
+ }
+ }
+
+ // delay if needed
+ if (timeout) {
+ Thread::wait(1);
+ }
+ }
+
+ buf[replyidx] = 0; // null term
+
+ return replyidx;
+}
+
+bool waitForOK(void)
+{
+ // Use temp buffer to avoid overwrite returned result if any
+ char tempbuf[BLE_BUFSIZE + 1];
+
+ while (bleReadLine(tempbuf, BLE_BUFSIZE)) {
+ // pc.printf("SPI: %s\n", tempbuf);
+ if (strcmp(tempbuf, "OK") == 0) return true;
+ if (strcmp(tempbuf, "ERROR") == 0) return false;
+ }
+ return false;
+}
+
+bool sendATCommand(char *cmd) {
+ bleMode = COMMAND;
+ bleWrite(cmd);
+ bleWrite("\r\n");
+ bool result = waitForOK();
+ bleMode = DATA;
+ return result;
+}
+
+bool sendCommandCheckOK(char *cmd) {
+ return sendATCommand(cmd);
+}
+
+// =============================================================================
+
+void flush(void)
+{
+ m_rx_fifo.clear();
+}
+
+uint8_t highByte(uint16_t x) {
+ return (uint8_t) (x >> 8);
+}
+
+uint8_t lowByte(uint16_t x) {
+ return (uint8_t) (x & 0xff);
+}
+
+bool sendPacket(uint16_t command, const uint8_t* buf, uint8_t count, uint8_t more_data) {
+ // flush old response before sending the new command
+ if (more_data == 0) flush();
+
+ sdepMsgCommand_t msgCmd;
+
+ msgCmd.header.msg_type = SDEP_MSGTYPE_COMMAND;
+ msgCmd.header.cmd_id_high = highByte(command);
+ msgCmd.header.cmd_id_low = lowByte(command);
+ msgCmd.header.length = count;
+ msgCmd.header.more_data = (count == SDEP_MAX_PACKETSIZE) ? more_data : 0;
+
+ // Copy payload
+ if ( buf != NULL && count > 0) memcpy(msgCmd.payload, buf, count);
+
+ enable_spi();
+
+ TimeoutTimer tt(_timeout);
+
+ // Bluefruit may not be ready
+ while ( ( spixfer(msgCmd.header.msg_type) == SPI_IGNORED_BYTE ) && !tt.expired() )
+ {
+ // Disable & Re-enable CS with a bit of delay for Bluefruit to ready itself
+ disable_spi();
+ wait_us(SPI_DEFAULT_DELAY_US);
+ enable_spi();
+ }
+
+ bool result = !tt.expired();
+ if ( result )
+ {
+ // transfer the rest of the data
+ spixfer((void*) (((uint8_t*)&msgCmd) +1), sizeof(sdepMsgHeader_t)+count-1);
+ }
+
+ disable_spi();
+
+ return result;
+}
+
+size_t bleWriteChar(uint8_t c) {
+ if (bleMode == DATA)
+ {
+ sendPacket(SDEP_CMDTYPE_BLE_UARTTX, &c, 1, 0);
+ bleGetResponse();
+ return 1;
+ }
+
+ // Following code handle BLUEFRUIT_MODE_COMMAND
+
+ // Final packet due to \r or \n terminator
+ if (c == '\r' || c == '\n')
+ {
+ if (m_tx_count > 0)
+ {
+ bool result = sendPacket(SDEP_CMDTYPE_AT_WRAPPER, m_tx_buffer, m_tx_count, 0);
+ m_tx_count = 0;
+ if (!result) {
+ return 0;
+ }
+ }
+ }
+ // More than max packet buffered --> send with more_data = 1
+ else if (m_tx_count == SDEP_MAX_PACKETSIZE)
+ {
+ bool result = sendPacket(SDEP_CMDTYPE_AT_WRAPPER, m_tx_buffer, m_tx_count, 1);
+
+ m_tx_buffer[0] = c;
+ m_tx_count = 1;
+ if (!result) {
+ return 0;
+ }
+ }
+ // Not enough data, continue to buffer
+ else
+ {
+ m_tx_buffer[m_tx_count++] = c;
+ }
+ return 1;
+}
+
+int32_t readline_parseInt(void)
+{
+ char buffer[101] = { 0 };
+ uint16_t len = bleReadLine(buffer, 100);
+ if (len == 0) return 0;
+
+ // also parsed hex number e.g 0xADAF
+ int32_t val = strtol(buffer, NULL, 0);
+
+ return val;
+}
+
+bool send_arg_get_resp(int32_t *reply) {
+ bleWrite("\r\n"); // execute command
+
+ // parse integer response if required
+ if (reply)
+ {
+ (*reply) = readline_parseInt();
+ }
+
+ // check OK or ERROR status
+ return waitForOK();
+}
+
+int32_t sendATCommandIntReply(char *cmd) {
+ int32_t reply = 0;
+ BLE_MODE current_mode = bleMode;
+
+ // switch mode if necessary to execute command
+ bleMode = COMMAND;
+
+ // Execute command with parameter and get response
+ bleWrite(cmd);
+ send_arg_get_resp(&reply);
+
+ // switch back if necessary
+ if ( current_mode == DATA ) bleMode = DATA;
+ return reply;
+}
+
+bool sendInitializePattern() {
+ return sendPacket(SDEP_CMDTYPE_INITIALIZE, NULL, 0, 0);
+}
+
+bool isBLEConnected() {
+ int32_t connected = 0;
+ connected = sendATCommandIntReply("AT+GAPGETCONN");
+ return connected;
+}
+
+
+void timer_function_millis(void const *n) {
+ _millis += 1;
+}
+
+int main() {
+ // timer to bump _millis
+ RtosTimer millis_timer(timer_function_millis, osTimerPeriodic, (void *)0);
+ millis_timer.start(1);
+
+ pc.baud(115200);
+ spi.format(8, 0);
+ spi.frequency(4000000);
+
+ pc.printf("Prepare to initiaize BLE\n");
+
+ // initialize BLE
+ cs = 1;
+ if (!sendInitializePattern()) pc.printf("BLE Init Failed\n");
+ ble_reset = 1;
+ ble_reset = 0;
+ Thread::wait(10);
+ ble_reset = 1;
+ Thread::wait(500);
+ cs = 1;
+
+ pc.printf("Set BLE Name\n");
+
+ // change name
+ if (!sendCommandCheckOK("AT+GAPDEVNAME=AdafruitDJ")) {
+ pc.printf("Could not set device name.\n");
+ }
+
+ // wait until connected
+ while (!isBLEConnected()) {
+ Thread::wait(100);
+ }
+
+ pc.printf("Ready\n");
+
+ Thread::wait(1000);
+
+ int i = 0;
+ while (true) {
+ bleMode = DATA;
+ // send character
+ char str[40] = { 0 };
+ sprintf(str, "Hello DJ %d\r\n", i);
+ bleWrite(str);
+ Thread::wait(1000);
+ pc.printf("Send\n");
+ i += 1;
+ }
+
+ Thread::wait(osWaitForever);
+}