ver1.0

Dependencies:   RingBuffer

main.cpp

Committer:
noboruk
Date:
2018-02-07
Revision:
0:82eed72bc4e4

File content as of revision 0:82eed72bc4e4:

/*
 *  ------- BLE Central/Client UART function -----------------------------------
 *          communicate with BLE_UART_Server program
 *      --- Tested on Switch Science mbed TY51822r3 ---
 *
 *  Modified by Kenji Arai
 *      http://www.page.sannet.ne.jp/kenjia/index.html
 *      http://mbed.org/users/kenjiArai/
 *
 *      Started:  April     8th, 2016
 *      Revised:  June     13th, 2016
 *      Revised:  October  22nd, 2017   Run on mbed-OS-5.6.2
 *
 *  Original program (see original.cpp file):
 *      S130 potential unstability case [closed] by Fabien Comte
 *      https://devzone.nordicsemi.com/question/49705/
 *                              s130-potential-unstability-case/
 *      GitHub Q&A by Fabien COMTE
 *      https://github.com/ARMmbed/ble/issues/69
 *  Tested Server Device:
 *      BLE_Uart_Server
 *      https://developer.mbed.org/users/kenjiArai/code/BLE_Uart_Server/
 */

// 2018/02/07 太陽誘電 EYSGCNZWY 使用の試作機向けに改造


//  Include --------------------------------------------------------------------
#include "mbed.h"
#include "BLE.h"
#include "UARTService.h"
#include "ble/DiscoveredCharacteristic.h"
#include "ble/DiscoveredService.h"
#include "RingBuffer.h"

//  Definition -----------------------------------------------------------------
#define     NUM_ONCE                20
#define     BFSIZE                  (NUM_ONCE+4)

//#define    USE_DEBUG_MODE
#ifdef USE_DEBUG_MODE
#define DBG(...) { pc.printf(__VA_ARGS__); }
#else
#define DBG(...)
#endif

#define SOFT_DEVICE_FATHER_HANDLE   3

#define LED1_PIN    P0_25
#define LED2_PIN    P0_23
#define LED3_PIN    P0_21
#define EXT_OUT_PIN P0_9
#define ROM_CS_PIN  P0_12
#define ROM_SCK_PIN P0_13
#define ROM_SDI_PIN P0_15

//  Object ---------------------------------------------------------------------
DigitalOut  alivenessLED(LED1_PIN,1);
DigitalOut  connectedLED(LED2_PIN,1);
DigitalOut  out_led(LED3_PIN,1);
DigitalOut  out_oc(EXT_OUT_PIN,0);     //オープンコレクタ出力
//
// UARTの端子ははEEPROMの端子を流用
DigitalOut      dummy_cs(ROM_CS_PIN,1); //Disable Chip
Serial          pc(ROM_SDI_PIN,ROM_SCK_PIN, 19200);    //SDI=TXD,SCK=RXD

// Object
BLE         ble;
Ticker      ticker;
Ticker      out_port_ticker;
RingBuffer  ser_bf(1536);
Thread      tsk;

//  ROM / Constant data --------------------------------------------------------
//#warning "You need to modify below value based on your board."
//const Gap::Address_t    mac_board_0   = {0xae,0x2f,0x0d,0x6b,0x49,0xea};
//
const char PEER_NAME[] = "UART_S";
//
//  RAM ------------------------------------------------------------------------
Gap::Handle_t   connectionHandle        = 0xFFFF;
DiscoveredCharacteristic uartTXCharacteristic;
DiscoveredCharacteristic uartRXCharacteristic;
bool            foundUartRXCharacteristic = false;
bool            connected2server        = false;
bool            connection_tx           = false;
bool            connection_rx           = false;
UARTService *   uartServicePtr          = NULL;
Gap::Address_t  my_mac;
int             my_board_index          = -1;
bool            received_uart_dat       = false;
int8_t          uart_buffer[BFSIZE];
uint8_t         uart_bf_len;
volatile bool   rx_isr_busy             = false;
//
bool            out_port_flag           = false;

//  Function prototypes --------------------------------------------------------
//      BLE
void advertisementCallback(const Gap::AdvertisementCallbackParams_t *);
void serviceDiscoveryCallback(const DiscoveredService *);
void characteristicDiscoveryCallback(const DiscoveredCharacteristic *);
void discoveryTerminationCallback(Gap::Handle_t );
void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *);
void connectionCallback(const Gap::ConnectionCallbackParams_t *);
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *);
//
//      Interrupt related
void periodicCallback(void);
void serialRxCallback(void);
void out_port_ticker_Callback(void);
//      serial receiving
void pc_ser_rx(void);
void preparation_sending_data(void);
//      Pre-check
void adjust_line(uint8_t *);

//------------------------------------------------------------------------------
//  Control Program
//------------------------------------------------------------------------------
int main(void)
{
    dummy_cs = 1;
    out_oc = 0;
    alivenessLED = 1;
    connectedLED = 1;
    out_led = 1;

    pc.attach(&serialRxCallback, Serial::RxIrq);
    ticker.attach(periodicCallback, 1);
    tsk.start(pc_ser_rx);

    // clear terminal output
    for (int k = 0; k < 5; k++) { pc.printf("\r\n");}

    // opening message
    pc.printf("UART Communication / Central side\r\n");
    pc.printf("  need Peripheral module (run BLE_Peripheral_EYSGCNZWY program)\r\n"); 

    // Mixed role **************************************************************
    ble.init();
    ble.gap().onConnection(connectionCallback);
    ble.gap().onDisconnection(disconnectionCallback);

    // Client(Central) role ****************************************************
    ble.gattClient().onHVX(onReceivedDataFromDeviceCallback);
    ble.gap().setScanParams(500, 450);
    ble.gap().startScan(advertisementCallback);

    while(true)
    {
        // allow notifications from Peripheral
        if (foundUartRXCharacteristic &&
            !ble.gattClient().isServiceDiscoveryActive())
        {
            // need to do the following only once
            foundUartRXCharacteristic = false;
            uint16_t value = BLE_HVX_NOTIFICATION;
            ble.gattClient().write(
                GattClient::GATT_OP_WRITE_REQ,
                connectionHandle,
                uartRXCharacteristic.getValueHandle() + 1,
                sizeof(uint16_t),
                reinterpret_cast<const uint8_t *>(&value)
            );
        }
        if (received_uart_dat == true){
            received_uart_dat = false;
            for(int i = 0; i < uart_bf_len; i++){
                //pc.printf("%c", uart_buffer[i]);
                pc.putc(uart_buffer[i]);
            }
        }
        ble.waitForEvent();
    }
}


void periodicCallback(void)
{
    // Do blinky on alivenessLED to indicate system aliveness
    alivenessLED = !alivenessLED; 
    if (connected2server){
        connectedLED = 0;
    } else {
        connectedLED = 1;
    }
    if (rx_isr_busy == true){
        rx_isr_busy = false;
    } else {
        tsk.signal_set(0x01);
    }
}

void out_port_ticker_Callback(void)
{
    out_led = 1;
    out_oc = 0;
    out_port_ticker.detach();   //
}

void serialRxCallback()
{
    ser_bf.save(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.check() == 0){
            if (linebf_irq_len != 0){
                linebf_irq[linebf_irq_len] = 0;
                adjust_line(linebf_irq);
                linebf_irq_len = 0;
                uartTXCharacteristic.write(NUM_ONCE, linebf_irq);
            }
        }
        while(ser_bf.check() != 0){
            char c = ser_bf.read();
            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;
                    uartTXCharacteristic.write(linebf_irq_len, linebf_irq);
                    linebf_irq_len = 0;
                    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 onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *params)
{
    DBG(
        "received HVX callback for handle %u; type %s\r\r\n",
        params->handle,
        (params->type == BLE_HVX_NOTIFICATION) ? "notification" : "indication"
    );
    if (params->type == BLE_HVX_NOTIFICATION){
        if ((params->handle
                == uartRXCharacteristic.getValueHandle()) && (params->len > 0))
        {
            uart_bf_len = params->len;
            strcpy((char *)uart_buffer, (char *)params->data);
            received_uart_dat = true;
            // 0.5s port ON
            out_led = 0;
            out_oc = 1;
            out_port_ticker.attach(out_port_ticker_Callback, 0.5);
         }
    }
}


// Client(Central) role ********************************************************
void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params)
{
    bool name_match = false;

    // parse the advertising payload, looking for data type COMPLETE_LOCAL_NAME
    // The advertising payload is a collection of key/value records where
    // byte 0: length of the record excluding this byte
    // byte 1: The key, it is the type of the data
    // byte [2..N] The value. N is equal to byte0 - 1

    for( uint8_t i = 0; i < params->advertisingDataLen; ++i)
    {
       const uint8_t record_length = params->advertisingData[i];
        if (record_length == 0)
        {
            continue;
        }
        const uint8_t type = params->advertisingData[i + 1];
        const uint8_t* value = params->advertisingData + i + 2;
        const uint8_t value_length = record_length - 1;
         
        if(type == GapAdvertisingData::COMPLETE_LOCAL_NAME)
        {
            if ((value_length == sizeof(PEER_NAME)) && (memcmp(value, PEER_NAME, value_length) == 0))
            {
                pc.printf(
                    "\r\nadv peerAddr[%02x %02x %02x %02x %02x %02x] rssi %d, isScanResponse %u, AdvertisementType %u\r\n",
                    params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2],
                    params->peerAddr[1], params->peerAddr[0], params->rssi, params->isScanResponse, params->type
                );
                name_match = true;
                break;
            }
        }
        i += record_length;
    }    
    if( name_match != true ){ return; }

    pc.printf("Found device : %s\r\n",PEER_NAME);
    // connections
    ble.gap().connect(params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL);

}

void serviceDiscoveryCallback(const DiscoveredService *service)
{
    DBG("service found...\r\n");
    if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT){
        DBG(
            "Service UUID-%x attrs[%u %u]\r\n",
            service->getUUID().getShortUUID(),
            service->getStartHandle(),
            service->getEndHandle()
        );
    } else {
        DBG("Service UUID-");
        const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();
        for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
            DBG("%02x", longUUIDBytes[i]);
        }
        DBG(" attrs[%u %u]\r\n",
                service->getStartHandle(), service->getEndHandle());
    }
}

void characteristicDiscoveryCallback(
    const DiscoveredCharacteristic *characteristicP)
{
    DBG(
        " C UUID-%x valueAttr[%u] props[%x]\r\n",
        characteristicP->getUUID().getShortUUID(),
        characteristicP->getValueHandle(),
        (uint8_t)characteristicP->getProperties().broadcast()
    );
    if (characteristicP->getUUID().getShortUUID()
                == UARTServiceTXCharacteristicShortUUID)
    {
        DBG("Sevice TX 0x%04x\r\n", UARTServiceTXCharacteristicShortUUID);
        uartTXCharacteristic = *characteristicP;
        connection_tx = true;
    } else if (characteristicP->getUUID().getShortUUID()
                == UARTServiceRXCharacteristicShortUUID)
    {
        DBG("Sevice RX 0x%04x\r\n", UARTServiceRXCharacteristicShortUUID);
        uartRXCharacteristic = *characteristicP;
        foundUartRXCharacteristic = true;
        connection_rx = true;
    }
}

void discoveryTerminationCallback(Gap::Handle_t connectionHandle)
{
    DBG("terminated SD for handle=%u\r\n", connectionHandle);
}

// Mixed role ******************************************************************
void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
{
    if (params->role == Gap::CENTRAL) {
        pc.printf("connected as Client(Central) (handle = %d)\r\n\r",
                    params->handle);
        connected2server = true;
        connectionHandle = params->handle;
        ble.gattClient().onServiceDiscoveryTermination(
                discoveryTerminationCallback);
        ble.gattClient().launchServiceDiscovery(
                params->handle,
                serviceDiscoveryCallback,
                characteristicDiscoveryCallback
        );
    }
    pc.printf(
        "Client(Central/Myself)      %02x:%02x:%02x:%02x:%02x:%02x\r\n",
        params->ownAddr[5], params->ownAddr[4], params->ownAddr[3],
        params->ownAddr[2], params->ownAddr[1], params->ownAddr[0]
    );
    pc.printf(
        "Connected Sever(peripheral) %02x:%02x:%02x:%02x:%02x:%02x\r\n",
        params->peerAddr[5], params->peerAddr[4], params->peerAddr[3],
        params->peerAddr[2], params->peerAddr[1], params->peerAddr[0]
    );
}

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    DBG("handle = %d ", params->handle);
    pc.printf(" -> disconnected\r\n", params->handle);
    connected2server = false;
//    connection_1st = false;
    connection_tx = false;
    connection_rx = false;
    if (params->handle == SOFT_DEVICE_FATHER_HANDLE) {
        ble.startAdvertising();                         // restart advertising
    } else {
        ble.gap().startScan(advertisementCallback);     // restart scan
    }
}