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.
main.cpp
- Committer:
- wninghj
- Date:
- 2018-04-05
- Revision:
- 0:39b7f3158ebe
File content as of revision 0:39b7f3158ebe:
#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);
}