this is a new program based on BLE_Uart_Peripheral. Change program name due to relation Client&Peripheral to Client&Server
Dependencies: RingBuffer nRF51_Vdd nRF51_WakeUp
This is a BLE Server (Device) program for nRF51.
You can communicate with mbed BLE using "BLE_Uart_Client" program as follows.
/users/kenjiArai/code/BLE_Uart_Client/
Please refer following my notebook.
/users/kenjiArai/notebook/ble-client-and-peripheral-using-switch-sience-ty51/#
main.cpp
- Committer:
- kenjiArai
- Date:
- 2018-02-11
- Revision:
- 8:dd23c6d67b58
- Parent:
- 7:0d94f4cf2a69
File content as of revision 8:dd23c6d67b58:
/* mbed Microcontroller Library * Copyright (c) 2006-2013 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * ------- BLE Peripheral/Server UART function -------------------------------- * communicate with BLE_UART_Client program * --- Tested on Switch Science mbed TY51822r3 --- * * http://www.page.sannet.ne.jp/kenjia/index.html * https://os.mbed.com/users/kenjiArai/ * * Started: March 7th, 2016 * Revised: June 13th, 2016 * Revised: Feburary 10th, 2018 Not set mac addr but use device name * Revised: Feburary 11th, 2018 use mbed-os5.7.4 with CircularBuffer * * Original program: * BLE_LoopbackUART * https://developer.mbed.org/teams/Bluetooth-Low-Energy/ * code/BLE_LoopbackUART/ * Reference program: * BLE_Peripheral_test by noboru koshinaka * https://os.mbed.com/users/noboruk/code/BLE_Peripheral_test/ * Tested Client Device: * BLE_Uart_Client * https://os.mbed.com/users/kenjiArai/code/BLE_Uart_Client/ */ // Include -------------------------------------------------------------------- #include "mbed.h" #include "BLE.h" #include "UARTService.h" #include "nRF51_Vdd.h" #include "nRF51_WakeUp.h" #include "CircularBuffer.h" // Definition ----------------------------------------------------------------- //#define USE_MAC // if you use mac address, please define it #define NUM_ONCE 20 #define BFSIZE (NUM_ONCE+4) // Please refer nRF51_WakeUP library #define GOTO_SLEEP_MODE 0 #if GOTO_SLEEP_MODE #warning "Make sure!! -> You need to connected P0_21(LED1) and P0_0" #endif //#define USE_DEBUG_MODE #ifdef USE_DEBUG_MODE #define DEBUG(...) { printf(__VA_ARGS__); } #else #define DEBUG(...) #endif // Object --------------------------------------------------------------------- BLE& ble_uart = BLE::Instance(); DigitalOut connectedLED(LED2); InterruptIn wake_up_sw(P0_1); nRF51_WakeUp wakeup(P0_21, P0_0); nRF51_Vdd vdd(3.0f, 2.2f); Serial pc(USBTX, USBRX, 115200); //Serial pc(P0_3, P0_1, 115200); // for another board UARTService *uartServicePtr; Ticker ticker; CircularBuffer<char, 1536> ser_bf; Thread tsk; Mutex bletx_mutex; // ROM / Constant data -------------------------------------------------------- #warning "You need to confirm your device name." const static char DEVICE_NAME[] = "UART_PJL"; // RAM ------------------------------------------------------------------------ Gap::Address_t my_mac; uint8_t tx_buf[BFSIZE]; uint8_t tx_len = 0; uint8_t rx_buf[BFSIZE]; volatile bool trigger_transmit = false; volatile bool trigger_receive = false; volatile uint8_t command_continue = 0; uint16_t time_out_cntr = 3600; volatile bool time_out = false; uint32_t sleep_time = 30; // unit:second volatile bool rx_isr_busy = false; // Function prototypes -------------------------------------------------------- // BLE void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *); void onDataWritten_action(const GattWriteCallbackParams *); // Tasks void pc_ser_rx(void); void data_from_ble(void); void Update_Values(void); // Application related void command(uint8_t *cmd); void action_tx_help(void); void action_tx_vdd(void); void action_tx_temperature(void); void action_tx_wait_time(uint8_t *); void action_tx_quit(void); int xatoi (char **, int32_t *); void adjust_line(uint8_t *); // Interrupt related void interrupt_by_sw(void); void serialRxCallback(void); void periodicCallback(void); //------------------------------------------------------------------------------ // Control Program //------------------------------------------------------------------------------ int main(void) { connectedLED = 0; pc.attach(&serialRxCallback, Serial::RxIrq); ticker.attach(periodicCallback, 1); tsk.start(pc_ser_rx); // clear terminal output for (int k = 0; k < 3; k++) { pc.printf("\r\n"); } // opening message pc.printf("UART Communication / Server(Peripheral) side\r\n"); pc.printf(" need Client module (run BLE_Uart_Client program)\r\n"); // Interrupt by switch wake_up_sw.fall(&interrupt_by_sw); ble_uart.init(); Gap::AddressType_t my_mac_type; ble_uart.gap().getAddress(&my_mac_type, my_mac); DEBUG( " my_MAC %02x:%02x:%02x:%02x:%02x:%02x (%s)\r\n", my_mac[5], my_mac[4], my_mac[3], my_mac[2], my_mac[1], my_mac[0], (my_mac_type == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random" ); pc.printf( " My device name : %s\r\n", DEVICE_NAME); pc.printf( " My mac data %02x:%02x:%02x:%02x:%02x:%02x\r\n", my_mac[5], my_mac[4], my_mac[3], my_mac[2], my_mac[1], my_mac[0] ); #ifdef USE_MAC pc.printf( " mac_board_x = {0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x};\r\n", my_mac[0], my_mac[1], my_mac[2], my_mac[3], my_mac[4], my_mac[5] ); pc.printf( " Please write above data(mac_board_x line (x=0,1,2,...))\r\n"); pc.printf( " into Client/main.cpp [ROM / Constant data] area\r\n"); #endif ble_uart.onDisconnection(disconnectionCallback); ble_uart.onDataWritten(onDataWritten_action); /* setup advertising */ ble_uart.accumulateAdvertisingPayload( GapAdvertisingData::BREDR_NOT_SUPPORTED); ble_uart.setAdvertisingType( GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); ble_uart.accumulateAdvertisingPayload( GapAdvertisingData::COMPLETE_LOCAL_NAME, (const uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME) ); ble_uart.accumulateAdvertisingPayload( GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)UARTServiceUUID_reversed, sizeof(UARTServiceUUID_reversed) ); // Advertize Interval ble_uart.setAdvertisingInterval(1000); /* 1000ms;in multiples of 0.625ms.*/ // Start ble_uart.startAdvertising(); UARTService uartService(ble_uart); uartServicePtr = &uartService; while(true) { if (time_out) { #if GOTO_SLEEP_MODE wakeup.set_and_wait(sleep_time); while(true) { // never come here but just in case deepsleep(); } #endif } if (trigger_transmit) { static uint8_t cmd_buf[BFSIZE]; static volatile bool flag_continue = 0; trigger_transmit = false; pc.printf((const char*)rx_buf); if (flag_continue == true) { strcat((char *)cmd_buf, (char *)rx_buf); if (strchr((const char*)cmd_buf,(int)'\r') == 0) { flag_continue = true; } else { command(cmd_buf); for(uint8_t i = 0; i < BFSIZE; i++) { cmd_buf[i] = 0; } flag_continue = false; } } if ((rx_buf[0] == '~')) { strcpy((char *)cmd_buf, (char *)rx_buf); if (strchr((const char*)cmd_buf,(int)'\r') == 0) { flag_continue = true; } else { command(cmd_buf); for(uint8_t i = 0; i < BFSIZE; i++) { cmd_buf[i] = 0; } flag_continue = false; } } } ble_uart.waitForEvent(); } } void command(uint8_t *cmd) { uint8_t *p = cmd; while(*p == ' ') { ++p; // skip space } if (*p++ == '~') { while(*p < '!') { ++p; // skip space } uint8_t c = *p; //pc.printf("c=%c\r\n", c); switch (c) { case 'v': action_tx_vdd(); break; case 't': action_tx_temperature(); break; case 'q': action_tx_quit(); break; case 'w': action_tx_wait_time(cmd); break; case 'h': case '?': action_tx_help(); break; default: //pc.printf("\r\nStep(%u)\r\n", __LINE__); break; } } } void periodicCallback(void) { #if GOTO_SLEEP_MODE if (--time_out_cntr == 0) { time_out = true; } #endif if (rx_isr_busy == true) { rx_isr_busy = false; } else { tsk.signal_set(0x01); } } void serialRxCallback() { ser_bf.push(pc.getc()); rx_isr_busy = true; tsk.signal_set(0x01); } void pc_ser_rx() { static uint8_t linebf_irq[BFSIZE]; static volatile uint8_t linebf_irq_len = 0; while(true) { Thread::signal_wait(0x01); if (ser_bf.empty()) { if (linebf_irq_len != 0) { linebf_irq[linebf_irq_len] = 0; adjust_line(linebf_irq); linebf_irq_len = 0; bletx_mutex.lock(); ble_uart.updateCharacteristicValue( uartServicePtr->getRXCharacteristicHandle(), linebf_irq, NUM_ONCE ); bletx_mutex.unlock(); } } while(!ser_bf.empty()) { char c; ser_bf.pop(c); if (c == '\b') { linebf_irq_len--; pc.putc(c); pc.putc(' '); pc.putc(c); } else if ((c >= ' ') || (c == '\r') || (c == '\n')) { bool overflow = false; if ((c == '\r') || (c == '\n')) { if (linebf_irq_len == NUM_ONCE - 1) { // remain only 1 buffer overflow = true; linebf_irq[linebf_irq_len++] = '\r'; pc.putc('\r'); } else { overflow = false; linebf_irq[linebf_irq_len++] = '\r'; linebf_irq[linebf_irq_len++] = '\n'; pc.printf("\r\n"); } } else { linebf_irq[linebf_irq_len++] = c; pc.putc(c); } if (linebf_irq_len >= NUM_ONCE ) { linebf_irq[linebf_irq_len] = 0; adjust_line(linebf_irq); linebf_irq_len = 0; bletx_mutex.lock(); ble_uart.updateCharacteristicValue( uartServicePtr->getRXCharacteristicHandle(), linebf_irq, NUM_ONCE ); bletx_mutex.unlock(); if (overflow == true) { overflow = false; linebf_irq[linebf_irq_len++] = '\n'; pc.putc('\n'); } } } } } } void adjust_line(uint8_t *bf) { uint8_t i, c; for (i = 0; i <NUM_ONCE; bf++, i++) { c = *bf; if (c == 0) { break; } } for (; i < NUM_ONCE; bf++, i++) { *bf = 0x11; } *(bf + 1) = 0; } void onDataWritten_action(const GattWriteCallbackParams *params) { if ((uartServicePtr != NULL) && (params->handle == uartServicePtr->getTXCharacteristicHandle())) { strcpy((char *)rx_buf, (const char *)params->data); trigger_transmit = true; } } void action_tx_help() { // 12345678901234567890 sprintf((char *)tx_buf," ~?:help\r\n"); tx_len = strlen((const char *)tx_buf); Update_Values(); Thread::wait(200); // 12345678901234567890 sprintf((char *)tx_buf," ~v:vdd\r\n"); tx_len = strlen((const char *)tx_buf); Update_Values(); Thread::wait(200); // 12345678901234567890 sprintf((char *)tx_buf," ~t:temperature\r\n"); tx_len = strlen((const char *)tx_buf); Update_Values(); Thread::wait(200); // 12345678901234567890 sprintf((char *)tx_buf," ~w:wait, w 120\r\n"); tx_len = strlen((const char *)tx_buf); Update_Values(); Thread::wait(200); // 12345678901234567890 sprintf((char *)tx_buf," ~q:quit/sleep\r\n"); tx_len = strlen((const char *)tx_buf); Update_Values(); Thread::wait(200); } void action_tx_vdd() { sprintf((char *)tx_buf,"Vdd: %3.2f V\r\n", vdd.read_real_value()); tx_len = strlen((const char *)tx_buf); Update_Values(); } void action_tx_temperature() { int32_t p_temp; float temperature; // Update a temperature (inside nRF51822 chip) sd_temp_get(&p_temp); // -16.0f is offset vale for chip die temp // to ambient temp (depend on your board) temperature = float(p_temp) / 4; // Original = float(p_temp)/4.0f - 16.0f; sprintf((char *)tx_buf,"T: %+4.1f dC\r\n", temperature); tx_len = strlen((const char *)tx_buf); Update_Values(); } void action_tx_wait_time(uint8_t *cmd) { int32_t dt; char *p; p = (char *)(cmd); p += 2; // point to time value if (xatoi(&p, &dt)) { if (dt <= 5) { dt = 5; } sleep_time = dt; // set next wake-up period } else { DEBUG("data is unknown!\r\n"); sleep_time = 30; } DEBUG("slp_t:%d\r\n", sleep_time); //pc.printf("slp_t:%d\r\n", sleep_time); // 12345678901234567890 sprintf((char *)tx_buf, "W: %d sec\r\n", sleep_time); tx_len = strlen((const char *)tx_buf); Update_Values(); } void action_tx_quit() { #if GOTO_SLEEP_MODE ticker.detach(); // 12345678901234567890 sprintf((char *)tx_buf,"Terminated the BLE"); tx_len = strlen((const char *)tx_buf); Update_Values(); Thread::wait(1000); wakeup.set_and_wait(sleep_time); while(true) { // never come here but just in case deepsleep(); } #else SCB->AIRCR = 0x05fa0004; // System RESET!! #endif } // Change string -> integer int xatoi (char **str, int32_t *res) { unsigned long val; unsigned char c, radix, s = 0; for (;;) { c = **str; if (c == 0) { return 0; } if (c == '-') { break; } if (c == '+') { (*str)++; c = **str; } if (c>='0'&& c<='9') { break; } else { (*str)++; c = **str; } } if (c == '-') { s = 1; c = *(++(*str)); } if (c == '0') { c = *(++(*str)); if (c <= ' ') { *res = 0; return 1; } if (c == 'x') { radix = 16; c = *(++(*str)); } else { if (c == 'b') { radix = 2; c = *(++(*str)); } else { if ((c >= '0')&&(c <= '9')) { radix = 8; } else { return 0; } } } } else { if ((c < '1')||(c > '9')) { return 0; } radix = 10; } val = 0; while (c > ' ') { if (c >= 'a') c -= 0x20; c -= '0'; if (c >= 17) { c -= 7; if (c <= 9) return 0; } if (c >= radix) return 0; val = val * radix + c; c = *(++(*str)); } if (s) val = -val; *res = val; return 1; } void interrupt_by_sw() // Go to sleep { NVIC_SystemReset(); // Not come here (Just in case) sleep(); } void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) { DEBUG("Disconnected!\r\n"); DEBUG("Restarting the advertising process\r\n"); ble_uart.startAdvertising(); } void Update_Values(void) { bletx_mutex.lock(); ble_uart.updateCharacteristicValue( uartServicePtr->getRXCharacteristicHandle(), tx_buf, tx_len ); bletx_mutex.unlock(); tx_len = 0; }