GR-LYCHEE Special Project: Let's Connect a Smartphone with BLE!

Overview

This project details how to connect the Gadget Renesas GR-LYCHEE board and a smartphone using Bluetooth Low Energy (BLE). GR-LYCHEE has an ESP32 module, which allows connection between Wi-Fi and BLE. BLE will be used to communicate with the smartphone.


Preparations

Hardware

Prepare a GR-LYCHEE board, USB cable (Micro B type) and a smartphone that can use BLE.

Software

The iPhone application "LightBlue" for confirming BLE operation will also be needed.


GATT profile

The ESP32 initial firmware has the following GATT profile. This profile is used to communicate between GR-LYCHEE and the smartphone. In this example, GR-LYCHEE is the server and the smartphone is the client.

UUID Access property Size (bytes) Characteristic Number
A002 Read 2 -
C300 Read 1 1
C301 Read 512 2
C302 Write 1 3
C303 Write Without Response 3 4
C304 Write 2 5
C305 Notify 5 6
C306 Indicate 5 7

UUID

"A002" is set as the service UUID in the setting of Primary Service UUID (0x2800). Subsequently, Characteristic UUIDs "C300" - "C306" are included in the Characteristic Declaration (0x2803), and each access property is defined.

Access property

Read: Readable from client.
Write: Writable from client. For writing, there is a response from the server (GR-LYCHEE).
Write Without Response: Writable from client. There is no response from the server (GR-LYCHEE) for writing.
Notify: The server (GR-LYCHEE) notifies the client of the change of Characteristic.
Indicate: The server (GR-LYCHEE) notifies the client about the change of Characteristic. Unlike Notify, Indicate also requires a response from the client.

Size

The size of byte data that can be handled at once. It is the maximum length of the dynamic setting value.

Characteristic Number

The number of obtained by the AT command "AT + BLEGATTSCHAR?" It is used to program Characteristic by program.


Example

Below is a sample program of BLE.

Caution: In the IDE for GR v1.03, the parser does not operate properly due to the linker option "--specs = nano.specs", and "fail to get service" occurs. Although it is troublesome, replace "platform.txt" in IDE for GR folder "ide4gr-1.03\hardware\arduino\rza1lu\" with this version of the platform.txt file.


#include <Arduino.h>
#include <ATParser_os.h>
#define BLE_NOTIFICATION_CHAR 6
#define BLE_INDICATION_CHAR   7
 
BufferedSerial esp32(P7_1, P0_1, 1024);
ATParser_os esp_parser(esp32);
Semaphore event_sem(0);
 
const char ble_name[] = "GR-LYCHEE";
bool ble_advertizing;
bool ble_connected;
uint8_t ble_conn_index;
uint8_t ble_srv_index;
 
bool button0_flag = false;
bool button1_flag = false;
bool ble_notification_on = false;
bool ble_indication_on = false;
 
void esp32_event() {
  event_sem.release();
}
 
void ble_client_read() {
  uint8_t mac[6] = {
    0  };
 
  digitalWrite(PIN_LED_YELLOW, HIGH);
  esp_parser.recv("%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\"",
  &ble_conn_index, &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
  printf("conn_index=%d, mac[%x:%x:%x:%x:%x:%x]\r\n",
  ble_conn_index, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  digitalWrite(PIN_LED_YELLOW, LOW);
}
 
void ble_client_write() {
  uint8_t char_index, desc_index = 0;
  uint16_t len;
 
  digitalWrite(PIN_LED_ORANGE, HIGH);
  esp_parser.recv("%hhd,%hhd,%hhd,", &ble_conn_index, &ble_srv_index, &char_index);
  printf("conn=%d srv=%d char=%d\r\n", ble_conn_index, ble_srv_index, char_index);
 
  char c = esp_parser.getc();
  if (c != ',') {
    desc_index = c;
    esp_parser.getc(); // to read ',' after desc_index.
    printf("desc=%d\r\n", desc_index);
  }
 
  esp_parser.recv("%hhd,", &len);
  printf("length=%d\r\n", len);
 
  uint8_t *data = (uint8_t *)malloc(len * sizeof(uint8_t));
  for (int i = 0; i < len; i++) {
    data[i] = esp_parser.getc();
    printf("%x\r\n", data[i]);
  }
 
  if ((desc_index == 49) && (char_index == BLE_NOTIFICATION_CHAR)) {
    if (data[0] == 1) {
      printf("Notification On\r\n");
      ble_notification_on = true;
    } 
    else {
      printf("Notification Off\r\n");
      ble_notification_on = false;
    }
  } 
  else if ((desc_index == 49) && (char_index == BLE_INDICATION_CHAR)) {
    if (data[0] == 2) {
      printf("Indication On\r\n");
      ble_indication_on = true;
    } 
    else {
      printf("Indication Off\r\n");
      ble_indication_on = false;
    }
  }
  digitalWrite(PIN_LED_ORANGE, LOW);
}
 
void ble_client_disconn() {
  digitalWrite(PIN_LED_GREEN, LOW);
  printf("disconnected client\r\n");
  ble_connected = false;
}
 
void ble_client_conn() {
  uint8_t mac[6] = {
    0  };
  esp_parser.recv("%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\"",
  &ble_conn_index, &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
  printf("connected client conn_index is %d, mac[%x:%x:%x:%x:%x:%x]\r\n",
  ble_conn_index, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 
  digitalWrite(PIN_LED_GREEN, HIGH);
  ble_advertizing = false;
  ble_connected = true;
}
 
void ub0_interrupt() {
  if (button0_flag == false) {
    button0_flag = true;
    event_sem.release();
  }
}
 
void ub1_interrupt() {
  if (button1_flag == false) {
    button1_flag = true;
    event_sem.release();
  }
}
 
void setup() {
  printf("Bluetooth sample started\r\n");
  pinMode(PIN_LED_GREEN, OUTPUT);
  pinMode(PIN_LED_YELLOW, OUTPUT);
  pinMode(PIN_LED_ORANGE, OUTPUT);
  pinMode(PIN_LED_RED, OUTPUT);
  pinMode(PIN_ESP_IO0, OUTPUT);
  pinMode(PIN_ESP_EN, OUTPUT);
  pinMode(PIN_SW0, INPUT);
  pinMode(PIN_SW1, INPUT);
  attachInterrupt(4, ub0_interrupt, FALLING);
  attachInterrupt(3, ub1_interrupt, FALLING);
 
  // Initializing esp32 access
  esp32.baud(115200);
  esp32.attach(Callback<void()>(esp32_event));
  digitalWrite(PIN_ESP_IO0, HIGH);
  digitalWrite(PIN_ESP_EN, LOW);
  delay(10);
  digitalWrite(PIN_ESP_EN, HIGH);
  esp_parser.setTimeout(1500);
  if (esp_parser.recv("ready")) {
    printf("ESP32 ready\r\n");
  } 
  else {
    printf("ESP32 error\r\n");
    digitalWrite(PIN_LED_RED, HIGH);
    while (1);
  }
 
  // Initializing esp32 as a server with GATT service
  esp_parser.setTimeout(5000);
  if (esp_parser.send("AT+BLEINIT=2") && esp_parser.recv("OK")
    && esp_parser.send("AT+BLENAME=\"%s\"", ble_name) && esp_parser.recv("OK")
    && esp_parser.send("AT+BLEGATTSSRVCRE") && esp_parser.recv("OK")
    && esp_parser.send("AT+BLEGATTSSRVSTART") && esp_parser.recv("OK")) {
    printf("GATT initialized\r\n");
  } 
  else {
    printf("fail to initialize\r\n");
    digitalWrite(PIN_LED_RED, HIGH);
    while (1);
  }
  uint8_t start, type;
  uint16_t uuid;
  esp_parser.send("AT+BLEGATTSSRV?");
  if (esp_parser.recv("+BLEGATTSSRV:%hhd,%hhd,%hhx,%hhd\r\nOK", &ble_srv_index, &start, &uuid, &type)) {
    printf("srv_index is %d\r\n", ble_srv_index);
  } 
  else {
    printf("fail to get service\r\n");
    digitalWrite(PIN_LED_RED, HIGH);
  }
 
  esp_parser.oob("+READ:", ble_client_read);
  esp_parser.oob("+WRITE:", ble_client_write);
  esp_parser.oob("+BLEDISCONN:", ble_client_disconn);
  esp_parser.oob("+BLECONN:", ble_client_conn);
}
 
void loop() {
  esp_parser.setTimeout(5000);
  if (esp_parser.send("AT+BLEADVSTART") && esp_parser.recv("OK")) {
    printf("Advertising started. Please connect to any client\r\n");
    ble_advertizing = true;
  } 
  else {
    printf("fail to start advertising\r\n");
    digitalWrite(PIN_LED_RED, HIGH);
    while (1);
  }
 
  while (ble_connected || ble_advertizing) {
    event_sem.wait();
    esp_parser.setTimeout(5);
    esp_parser.recv(" ");  //dummy read for parser callback
 
    // Set attribute of C300
    if (button0_flag) {
      static uint8_t data = 1; // write data
      esp_parser.setTimeout(5000);
      // AT+BLEGATTSSETATTR=<srv_index>,<char_index>[,<desc_index>],<length>
      if (esp_parser.send("AT+BLEGATTSSETATTR=%d,1,,1", ble_srv_index) && esp_parser.recv(">")) {
        if (esp_parser.putc(data) && esp_parser.recv("OK")) {
          printf("success to send\r\n");
        } 
        else {
          printf("fail to send\r\n");
        }
      } 
      else {
        printf("fail to command AT\r\n");
      }
      esp_parser.flush();
      button0_flag = false;
      data++;
    }
 
    // Set notification of C305
    if (button1_flag && ble_notification_on) {
      static uint8_t data = 0xff; // write data
      esp_parser.setTimeout(5000);
      // AT+BLEGATTSNTFY=<conn_index>,<srv_index>,<char_index>,<length>
      if (esp_parser.send("AT+BLEGATTSNTFY=%d,%d,%d,1", ble_conn_index, ble_srv_index, BLE_NOTIFICATION_CHAR)
        && esp_parser.recv(">")) {
        if (esp_parser.putc(data) && esp_parser.recv("OK")) {
          printf("success to notify\r\n");
        } 
        else {
          printf("fail to notify\r\n");
        }
      } 
      else {
        printf("fail to command AT\r\n");
      }
      esp_parser.flush();
      button1_flag = false;
      data--;
    }
  } 
 
}

Operation Check

Activate the serial monitor and press the reset button on the GR-LYCHEE board. In the "LightBlue" smartphone application, "GR-LYCHEE" is displayed. The serial monitor shows that initialization of GATT and advertising started.

When you tap GR-LYCHEE with LightBlue, the connection is established and the properties are displayed. Also, the green LED on the GR-LYCHEE board will light up. On the serial monitor, the connection number (conn_index) and Mac address are displayed. Connection number is always 0 for single connection. After the connection, the client reads the property of the server, the log is displayed for eight lines.

In the property list for LightBlue, tap UUID "C300", the default value 0x30 is displayed. After you press the UB0 button on GR-LYCHEE, if you do "Read again" with LightBlue, you can see that 0x01 is written. As the value to be written is incremented by 1 each time the button is pressed, 0x02, 0x03 are sequentially written if it is repeated several times. For example, if you connect a sensor to GR-LYCHEE and update its value, you can read the sensor value with a smartphone.

Next, let's try notification. Display UUID "C305" and press "Listen for notifications". After doing this, you can see that the value is displayed every time you press the GR-LYCHEE UB1 button.

Finally, let's write the data from the smartphone to the GR-LYCHEE board. Display UUID "C302" and press "Write new value". In the figure to the right, "11" is entered. When input is complete, "11" is displayed on the serial monitor.