IoT Sharing: Raspberry Pi

Hot

Showing posts with label Raspberry Pi. Show all posts
Showing posts with label Raspberry Pi. Show all posts

Wednesday, March 21, 2018

Demo 41: ESP32 connects with nRF24L01 2.4 GHz wireless chip

8:43 PM 19
1. Introduction
Figure: Module nRF24L01
The nRF24L01 2.4 GHz wireless chip from Nordic Semiconductor. It has:
- SPI interface, hardware link layer, multiple pipelines, ...
- The chip is very cheap.
Comparing to Wifi modules such as ESP8266, ESP32:
- It has less power consumption.
- It is cheaper (but need a MCU to control).
- It has lower data rate.
If your Wifi infrastructure is good, you can consider using Wifi modules for your application.
If power consumption is not your issue, you can consider using Wifi modules for your application.
If we use nrf24l01 and need to send data to cloud via Internet, consider using ESP or Raspberry as a Gateway.
Some main characteristics:
1.1 NRF24L01 Hardware Interface
- 8 pins Vcc, GND, IRQ, CE, SPI pins (CSN (chip select not), SCK, MISO, and MOSI).
- IO pins are 5V-tolerant and operating range of 1.9 to 3.6V for Vcc.
- CSN, SCK, MISO, and MOSI for data transmission and reception.
- The CSN pin is active-low, and is normally kept high. When this pin goes low, the 24L01 begins listening on its SPI port for data and processes it accordingly.
- CE is used to control data transmission and reception when in TX and RX modes.
- IRQ is the interrupt pin, and is active-low. There are three internal interrupts that can cause this pin to go low when they are active. Each of these bits can be masked out such that when the bit’s respective interrupt becomes active, the status of the IRQ pin is not changed.
1.2 Interfacing the nRF24L01 via SPI
- The SPI interface allows you to read/write registers, transmit data, receive data from and to the 24L01. For start step we use SPI at 2 Mbps. The high SPI data rates are used if you needed huge on-air data rates.
1.3 SPI Instruction Set Summary
- In order to send data to or receive data from the SPI port on the nRF24L01:
  + The CSN pin on the 24L01 must be high then bring the CSN pin low to receive SPI data. Note: this pin will stay low throughout the entire transaction.
  + You will transmit the command byte of the instruction you wish to send. If you are receiving data bytes for this instruction, you must then send one byte to the 24L01 for every one byte that you wish to get out of the 24L01. If you are just sending the 24L01 data, you simply send your data bytes and generally don’t worry about what it sends back to you.
  + Once you have transmitted and/or read all of the bytes that you need, you bring CSN back high.
  + When you send any command byte, the 24L01 always returns to you the STATUS register.
1.4 FIFO Info
- There are FIFOs for both TX and RX modes.
- Both of the FIFOs  will hold the three newest packets that have been put into them. If you receive three packets in RX mode and you don’t read the payloads, the first (oldest) packet received is pushed out by the newest packet received. The same goes for the TX payload – if you load three packets and don’t transmit them (by executing the aforementioned CE toggle), then the fourth packet will push out the first packet you loaded.
1.5 Data Packet Format
- The transceiver protocol has 2 modes: Shockburst and Enhanced Shockburst
- The message format for both Shockburst and Enhanced Shockburst modes are different.
- In both modes, the preamble is sent first (1 byte), is used to allow the receiver to know that what it is hearing is the beginning of a packet and not just on-air noise.
- The next bytes sent are the address bytes. This is set by the user, and is between three and five bytes long.
- The next bytes sent is different between the two modes. In Enhanced Shockburst only, a flag word of nine bits is sent to indicate the message status concerning re-transmissions. Only two bits are currently used (a count of resent packets), and the other seven are reserved for future use.
- The last half of the packet that is sent is the same in both modes. The first of the fields to be sent in both modes is the payload data. The length of the payload is also set by the user (1 to 32 bytes long). The final part of the packet to be sent is the CRC, which is user-settable (0, 1, or 2 byte(s)).
Let 's make 2 demos:
- Demo 1: ESP32 Nano will send "hello" message to Arduino.
- Demo 2: Arduino Nano will send "hello" message to ESP32.
If you have 2 ESP32 you can replace Arduino Nano with ESP32.
2. Hardware
You need 2 Arduino/ESP8266/ESP32/Raspberry boards for testing: 1 board will send/receive message and 1 board will receive/send message which was sent by first board.
I used Arduino for testing, just do wiring like below:
Arduino_D8      x   NRF24_CSN
Arduino_D7      x   NRF24_CE
Arduino_D13    x   NRF24_SCK
Arduino_D11    x   NRF24_MOSI
Arduino_D12    x   NRF24_MISO

Arduino_3.3      x   NRF24_Vcc
Arduino_GND  x   NRF24_GND
For ESP32, I modified the library RF24 so that the software can use SPI software instead of using SPI hardware. Do wiring like below:
NRF24_CE      x   ESP32_IO12
NRF24_CSN    x   ESP32_IO14
NRF24_SCK    x   ESP32_IO26
NRF24_MISO  x   ESP32_IO25
NRF24_MOSI  x   ESP32_IO27
NRF24_Vcc      x   ESP32_3.3V
NRF24_GND    x   ESP32_GND  
3. Software
For ESP32, I modified the library RF24 (https://github.com/nhatuan84/RF24) so that the software can use SPI software insted of using SPI hardware. The new API has new form:
RF24(uint16_t _cepin, uint16_t _cspin, uint16_t sck, uint16_t miso, uint16_t mosi)
and to create an instance: RF24 radio(12, 14, 26, 25, 27);
For Arduino, we use SPI hardware so just use form:  
RF24(uint16_t _cepin, uint16_t _cspin)
and the instance is RF24 radio(7,8);
- Demo 1: ESP32 Nano will send "hello" message to Arduino.
ESP32 transmitter code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include  <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
char msg[6] = "hello";
RF24 radio(12, 14, 26, 25, 27);
const uint64_t pipe = 0xE8E8F0F0E1LL;

void setup(void) {
  Serial.begin(115200);
  radio.begin();
  radio.setChannel(2);
  radio.setPayloadSize(7);
  radio.setDataRate(RF24_250KBPS);
  radio.openWritingPipe(pipe);
}
void loop(void) {
  Serial.println("send ...");
  radio.write(msg, 6);
  delay(3000);
}
Arduino receiver code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
char msg[6];
RF24 radio(7,8);
const uint64_t pipe = 0xE8E8F0F0E1LL;
void setup(void){
 Serial.begin(115200);
 radio.begin();
 radio.setChannel(2);
 radio.setPayloadSize(7);
 radio.setDataRate(RF24_250KBPS);
 radio.openReadingPipe(1,pipe);
 radio.startListening();
}

void loop(void){
 if (radio.available()){  
     radio.read(msg, 6);      
     Serial.println(msg);
     delay(10);
 }
 else{
  //Serial.println("No radio available");
 }
}
- Demo 2: Arduino Nano will send "hello" message to ESP32.
ESP32 receiver code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
char msg[6];
RF24 radio(12, 14, 26, 25, 27);
const uint64_t pipe = 0xE8E8F0F0E1LL;
void setup(void){
 Serial.begin(115200);
 radio.begin();
 radio.setChannel(2);
 radio.setPayloadSize(7);
 radio.setDataRate(RF24_250KBPS);
 radio.openReadingPipe(1,pipe);
 radio.startListening();
}

void loop(void){
 if (radio.available()){  
     radio.read(msg, 6);      
     Serial.println(msg);
     delay(10);
 }
 else{
  //Serial.println("No radio available");
 }
}
Arduino transmitter code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include  <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
char msg[6] = "hello";
RF24 radio(7,8);
const uint64_t pipe = 0xE8E8F0F0E1LL;

void setup(void) {
  Serial.begin(115200);
  radio.begin();
  radio.setChannel(2);
  radio.setPayloadSize(7);
  radio.setDataRate(RF24_250KBPS);
  radio.openWritingPipe(pipe);
}
void loop(void) {
  Serial.println("send ...");
  radio.write(msg, 6);
  delay(3000);
}
4. Result
Figure: ESP32 received "hello" message from Arduino
Read More

Monday, January 22, 2018

Demo 40: Create a Facebook Messenger chat bot for monitoring and controlling home devices using Raspberry/Orange Pi and ESP32/8266

12:05 AM 1
1. Introduction
In this demo, I will show you how to create a Facebook Messenger chat bot for monitoring and controlling home devices using Raspberry/Orange Pi and ESP32/8266. This is not a full solution so you need to improve it by yourself.
Figure: Facebook Messenger chat bot for monitoring and controlling home devices
There are 2 steps to make this demo:
- How to setup a Facebook chat bot
- How to setup local system (Raspberry/Orange Pi, ESP32/8266)
This is the model of the demo:
Figure: The model of the demo
2. Setup
2.1 Create Facebook page
You can refer to this guideline. And choose type of Page is Business, Brand or Place. In my demo, I created a page named IoT Sharing.
2.2 Setup Facebook chat bot
In order to develop or use Facebook services for Software development you need to register a Facebook developer account here.
After created a FB developer account choose My Apps -> Add a new App -> Fill Display name
In Select a Product -> choose messenger -> Set Up
Choose Webhooks -> Setup Webhooks
Note: We do not do this step now. This step will be done after running the Raspbbery/Orange Pi software. Because after filled Callback URL, Verify Token and press Verify and Save, FB will send a GET request with "hub.challenge=value of Verify Token" (in this case Verify Token is "iotsharing.com" string) to Pi. Pi need to check the value of hub.challenge must match the value that we filled in Verify Token field (in this case Verify Token is "iotsharing.com" string). And then send this hub.challenge back to FB to finish the verification.
1
2
3
4
5
6
@app.route('/', methods=['GET'])
def handle_verification():
 if(request.args['hub.challenge'] == 'iotsharing.com'):
  return request.args['hub.challenge']
 else:
  return 'not match'
Fill information like below and choose Verify and Save.
choose the Page that you created in previous step and Subscribe/Unsubscribe.
Finally, extract the ACCESS_TOKEN of the FB app so that our application can authenticate to use FB services.
It is done for Facebook setup step.
2.2 Setup local system (Raspberry/Orange Pi, ESP32/8266)
In this demo, Pi will keep some roles:
- Communicate with Facebook server to receive and respond message.
- Parse Facebook message and then using MQTT protocol to publish the commands to ESP32/8266 clients and subscribe responses from ESP32/8266 clients.
- Refer to this post to setup MQTT for Pi.
- We use Python Paho MQTT for local communication. Refer to this post to install it.
- Besides we need to install some packages (Flask, ngrok) on Raspberry/Orange Pi for our demo. 
+ Flask is a Python web framework. In our demo, It is a web server to handle Facebook https request.
+ ngrok secure introspect-able tunnels to local host web hook development tool and debugging tool. This tool helps Facebook server can see our Flask local web server.
From Pi Terminal running the commands:
+ Install Flask Python server: "sudo pip install Flask"
+ Download ngrok: "wget https://bin.equinox.io/a/26q6mq7ddJR/ngrok-2.2.9-linux-arm.zip"
+ Unzip zip file for executable app: "unzip ngrok-2.2.9-linux-arm.zip"
3. Hardware
- 1 ESP32 or ESP8266
- 1 Raspberry Pi or Orange Pi
- 1 LED connect to ESP
- 1 temperature sensor connect to ESP. 
In this demo I do not use temperature sensor. I used random(min, max) function to generate temperature.
4. Software
The source code of this demo can be found on github.
Define messages:
- Pi send MQTT request topics to ESP:
Temperature topic: "floor1/room1/temp1" to measure temperature
Led topic:               "floor1/room1/led1" to set "0" (off) or "1" (on)
- ESP send MQTT response topics to Pi:
Temperature response topic: "floor1/room1/temp1/res" with temperature value
Led response topic:               "floor1/room1/led1/res" to inform that the request was executed
- The template for Facebook message:
"set floor1/room1/led1 on" -> turn on LED and get response
"set floor1/room1/led1 off" -> turn off LED and get response
"get floor1/room1/temp1" -> request temperature measurement and get response
ESP code I reused the post (esp32chatbot.ino)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "dd-wrt";
const char* password = "0000000000";
//ip address of Raspberry/orange pi
const char* mqtt_server = "192.168.1.106";
char msg[20];

/* create an instance of PubSubClient client */
WiFiClient espClient;
PubSubClient client(espClient);

/*LED GPIO pin*/
const char led = 4;
/* topics */
#define TEMP_TOPIC    "floor1/room1/temp1"
#define LED_TOPIC     "floor1/room1/led1" /* on, off */

void receivedCallback(char* topic, byte* payload, unsigned int length) {
    Serial.print("topic: ");
    Serial.println(topic);
      if(strcmp(topic, LED_TOPIC) == 0){
        Serial.print("payload: ");
        for (int i = 0; i < length; i++) {
          Serial.print((char)payload[i]);
        }
        Serial.println();
        /* we got '1' -> on */
        if ((char)payload[0] == '1') {
          digitalWrite(led, HIGH); 
          snprintf (msg, 20, "%s", "on");
        /* publish the response */
        client.publish(LED_TOPIC "/res", msg);
        } else {
          /* we got '0' -> on */
          digitalWrite(led, LOW);
          snprintf (msg, 20, "%s", "off");
          client.publish(LED_TOPIC "/res", msg);
        }
    }else {
        snprintf (msg, 20, "%d", random(0, 40));
        client.publish(TEMP_TOPIC "/res", msg);
    }
}

void mqttconnect() {
  /* Loop until reconnected */
  while (!client.connected()) {
    Serial.print("MQTT connecting ...");
    /* client ID */
    String clientId = "ESP32Client";
    /* connect now */
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      /* subscribe topic */
      client.subscribe(LED_TOPIC);
      client.subscribe(TEMP_TOPIC);
    } else {
      Serial.print("failed, status code =");
      Serial.print(client.state());
      Serial.println("try again in 5 seconds");
      /* Wait 5 seconds before retrying */
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  /* set led as output to control led on-off */
  pinMode(led, OUTPUT);

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  /* configure the MQTT server with IPaddress and port */
  client.setServer(mqtt_server, 1883);
  /* this receivedCallback function will be invoked 
  when client received subscribed topic */
  client.setCallback(receivedCallback);
}
void loop() {
  /* if client was disconnected then try to reconnect again */
  if (!client.connected()) {
    mqttconnect();
  }
  /* this function will listen for incomming 
  subscribed topic-process-invoke receivedCallback */
  client.loop();
}
Pi code (chatbot.py):
We use variable history to hold the id of Facebook messenger sender while waiting for the response from ESP. This sender id is used in reply() function to send response back to Facebook messenger sender.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from flask import Flask, request
import requests
import paho.mqtt.publish as publish
import paho.mqtt.client as mqtt
import thread

ACCESS_TOKEN = "your_fb_access token_here";

#hold sender id
history = dict()

set_cmd = 'floor1/room1/led1'
get_cmd = 'floor1/room1/temp1'

#respond to FB messenger
def reply(user_id, msg):
    data = {
        "recipient": {"id": user_id},
        "message": {"text": msg}
    }
    resp = requests.post("https://graph.facebook.com/v2.6/me/messages?access_token=" + ACCESS_TOKEN, json=data)
    print(resp.content)

#MQTT handler
def on_connect(mqttc, obj, flags, rc):
    print("rc: "+str(rc))
def on_message(mqttc, obj, msg):
    print(msg.topic+": "+str(msg.payload))
    if msg.topic in history:
        user_id = history[msg.topic]
  #reply FB messenger
        reply(user_id, msg.payload)
        history.pop(msg.topic, None)

mqttc = mqtt.Client()
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.connect("localhost", 1883, 60)
mqttc.subscribe("floor1/#", 0)

#MQTT subscribe thread 
def mqtt_thread( threadName, delay):
   mqttc.loop_forever()
try:
   thread.start_new_thread( mqtt_thread, ("mqtt-thread", 0, ) )
except:
   print "Error: unable to start thread"

#Flask web server instance
app = Flask(__name__)

#handle GET request from Facebook
@app.route('/', methods=['GET'])
def handle_verification():
 if(request.args['hub.challenge'] == 'iotsharing.com'):
  return request.args['hub.challenge']
 else:
  return 'not matched'

#handle POST request from Facebook
@app.route('/', methods=['POST'])
def handle_incoming_messages():
    data = request.json
    print(data)
    sender = data['entry'][0]['messaging'][0]['sender']['id']
    message = data['entry'][0]['messaging'][0]['message']['text']
    print(message)
    if(message.startswith('set')):
        arr = message.split()
        l = len(arr)
        if(l == 3 and set_cmd == arr[1]):
            cmd = arr[1]
            val = '1' if arr[2]=='on' else '0'
            publish.single(cmd, val, hostname="localhost")
   #record command with sender id
            history[cmd+'/res'] = sender
            return 'ok'
    elif(message.startswith('get')):
        arr = message.split()
        l = len(arr)
        if(l == 2 and get_cmd == arr[1]):
            cmd = arr[1]
            publish.single(cmd, '', hostname="localhost")
   #record command with sender id
            history[cmd+'/res'] = sender
            return 'ok'
    reply(sender, 'invalid query')
    return "ok"

if __name__ == '__main__':
app.run(debug=True)
5. Steps to deploy
- MQTT broker run on Pi
- Open 2 Terminal on Pi:
   + Run "python chatbot.py" with Flask in it.
   + Run ngrok (5000 is the port Flask listen on): "./ngrok http 5000"

Copy the url in red box to Callback URL field that mention in step 2.2 Setup Facebook chat bot
Note: in case we shutdown ngrok and run it again the new url will be generated. We have to re-register this url to Facebook.
Choose Webhooks -> Edit Subscription
And then Unsubcribe and Subscribe the page again:
Now go to the Page that we created, choose About and Send Message
And type one of commands:
"set floor1/room1/led1 on"
"set floor1/room1/led1 off"
"get floor1/room1/temp1"

6. Result



Read More

Tuesday, January 16, 2018

Demo 39: ESP32/8266 multipart upload a file and download a file via HTTP

6:43 PM 10
1. Introduction
In previous demos, I showed you how to use MQTT/MQTTS and how to update firmware OTA (TCP/UDP and HTTP). In this demo, I will show you another interesting topic. That is how to download and multi-part upload a file via HTTP.
Figure: multipart upload and download a file via http
Based on this demo, you can build an application that can download the new firmware or the configuration file to memory card that is attached to client board for using later. Or you can upload the log file that records occurred events in run time to a server.
This is the requirement of this demo: a ESP32 will download a file from internet and after finish downloading, upload it to local apache server. The downloaded file will be stored in sdcard.
In order to make it easy, I created a library here in github. You just install and use it.
Note: this library can be apply for ESP8266.
2. Software
The library is simple to use. It has 2 APIs: upload() and download() with parameters. The parameters are:
- url of uploading and downloading file.
- progress displaying callback function.
- response processing callback function.
- reading and writing data to storage (sdcard, SPIFFS, ...) callback functions.
You should change them accordingly to your application.
Note: The url parser of this library is quite simple and it will be updated later.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "UDHttp.h"
#include "mySD.h"


const char* ssid     = "dd-wrt";
const char* password = "0000000000";

File root;
//these callbacks will be invoked to read and write data to sdcard
//and process response
//and showing progress 
int responsef(uint8_t *buffer, int len){
  Serial.printf("%s\n", buffer);
  return 0;
}
//read data callback
int rdataf(uint8_t *buffer, int len){
  //read file to upload
  if (root.available()) {
    return root.read(buffer, len);
  }
  return 0;
}
//write data callback
int wdataf(uint8_t *buffer, int len){
  //write downloaded data to file
  return root.write(buffer, len);
}

void progressf(int percent){
  Serial.printf("%d\n", percent);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
    
  Serial.print("Initializing SD card...");
  if (!SD.begin(32, 14, 12, 27)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  SD.remove("test.pdf");
  {
    UDHttp udh;
    //open file on sdcard to write
    root = SD.open("test.pdf", FILE_WRITE);
    if (!root) {
       Serial.println("can not open file!");
       return;
    }
    //download the file from url
    udh.download("http://www.smart-words.org/linking-words/linking-words.pdf", wdataf, progressf);
    root.close();
    Serial.printf("done downloading\n");
  }
  {
    UDHttp udh;
    //open file on sdcard to read
    root = SD.open("test.pdf");
    if (!root) {
       Serial.println("can not open file!");
       return;
    }
    //upload downloaded file to local server
    udh.upload("http://192.168.1.107:80/upload/upload.php", "test.pdf", root.size(), rdataf, progressf, responsef);
    root.close();
    Serial.printf("done uploading\n");
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}
And the php script was used for this demo:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?php
$target_path = "uploads/";

$target_path = $target_path . basename( $_FILES['data']['name']); 

if(move_uploaded_file($_FILES['data']['tmp_name'], $target_path)) {
    echo "The file ".  basename( $_FILES['data']['name']). 
    " has been uploaded";
} else{
    echo "There was an error uploading the file, please try again!";
}
?>
Note: The variable $_FILES['data']['name'] has fixed 'data' field name (similar to html form ""). The downloaded file will be stored in sub-folder 'uploads' which is relative to folder where the php script located. Reference this post to install local apache server. 
3. Hardware
For harware connection please refer here.

4. Result
Figure: start downloading with progress displaying
Read More

Saturday, August 26, 2017

Demo 30: How to use Arduino ESP32 MQTTS with MQTTS Mosquitto broker (TLS/SSL)

9:05 PM 16
1. Introduction
Arduino-ESP32 SDK has been updated and new WiFiClientSecure library not working any more. In order to fix it, please using the old github commit or download the zip file here.
After downloading the old commit; extract it; go to the libraries folder; copy the
WiFiClientSecure folder; replace the current WiFiClientSecure folder (in Arduino/hardware/espressif/esp32/libraries or refer here) by the copied WiFiClientSecure folder.
- In this tutorial, I will show you how to use ESP32 MQTTS with MQTTS Mosquitto broker (TLS/SSL).
- In order to make this tutorial, please refer topics:
How to set up secure transportation for MQTT Mosquitto broker with SSL/TLS
Demo 29: How to use HTTPS in Arduino ESP32
Demo 14: How to use MQTT and Arduino ESP32 to build a simple Smart home system
- The requirement for this demo: ESP32 with a LED on it will turn On/Off when subscribing a topic "smarthome/room1/led" with values (0: off, 1: on).
2. Steps
- After doing the steps in this demo How to set up secure transportation for MQTT Mosquitto broker with SSL/TLS, you have to extract the content of ca.crt for SSL/TLS handshake phase by running the command:  
cat /etc/mosquitto/certs/ca.crt
 Figure: Extract content of ca.crt
- Get the IP address of machine that run MQTT mosquitto by running command: ifconfig or if you use MDNS for that machine you can follow this tutorial to get the IP directly from ESP32: How to get the IP address of a node by its mdns host name in Arduino ESP32.
- We will use WiFiClientSecure class for SSL/TLS handshake phase and PubSubClient library for ESP32 MQTT communication. Please see the topics that I mentioned above.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <ESPmDNS.h>

/* change it with your ssid-password */
const char* ssid = "dd-wrt";
const char* password = "0000000000";
/* this is the MDNS name of PC where you installed MQTT Server */
const char* serverHostname = "iotsharing";

const char* ca_cert = \ 
"-----BEGIN CERTIFICATE-----\n" \
"MIIDpzCCAo+gAwIBAgIJAOHc50EeJ6aeMA0GCSqGSIb3DQEBDQUAMGoxFzAVBgNV\n" \
"BAMMDkFuIE1RVFQgYnJva2VyMRYwFAYDVQQKDA1Pd25UcmFja3Mub3JnMRQwEgYD\n" \
"VQQLDAtnZW5lcmF0ZS1DQTEhMB8GCSqGSIb3DQEJARYSbm9ib2R5QGV4YW1wbGUu\n" \
"bmV0MB4XDTE3MDgyNzAyMDQwNFoXDTMyMDgyMzAyMDQwNFowajEXMBUGA1UEAwwO\n" \
"QW4gTVFUVCBicm9rZXIxFjAUBgNVBAoMDU93blRyYWNrcy5vcmcxFDASBgNVBAsM\n" \
"C2dlbmVyYXRlLUNBMSEwHwYJKoZIhvcNAQkBFhJub2JvZHlAZXhhbXBsZS5uZXQw\n" \
"ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8mQfR058k7LBV7Xt6RiZL\n" \
"BIpC5ewSweAnsaS6Ue+jx6uruJsccnIJbiHJVjOcSYV3JLMt3un7d0JTottW6Zx/\n" \
"VleYC4CUzf3bcHLe7Qhl0veP9QAdZceblMCbEqAM9227EaXKQVzAYGwRnuxvfJM7\n" \
"P84iu2J/zB4o+AEWUJDIW38SO4b+0i1FYHmKKxEx7dztDyCJvTsn35wX3oKlJ5e+\n" \
"na/6csD+Ngh9QQ+wDd02TBqXKB7UXyRT5ECesu19U6CTE7Wr9xECbDO262aD6GtC\n" \
"HpeSAWqa6HKbKjZ20pL5V5ceS85TdWbvPdVrFUDWMIhK9dNuwhIK0s6VyRcJkbe5\n" \
"AgMBAAGjUDBOMB0GA1UdDgQWBBQPZ0u1jIg65jT3Th42VGMnya6VDDAfBgNVHSME\n" \
"GDAWgBQPZ0u1jIg65jT3Th42VGMnya6VDDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3\n" \
"DQEBDQUAA4IBAQB8JrNsV32aXBOLSX8WqoHuU2NOJH+1fTdGDLYLEAPe/x3YxkNC\n" \
"MJDx8TGLSQsz4L0kCsLEQ/qsnkuPJUt3x7eNIMOrvS+zDv9wXqnGwMgeAOtEkG5n\n" \
"NbPezM50ovgZAuEZMphp2s/Zl0EROydnqpkweWa5bxYRkiQBlp0nUkCtec3PZ6iO\n" \
"QVPj/bHHi4T3jkbL0+oLNShv9Zw9hr6BkKCVqeHe9prlOMVuEJOVrEKzkhhAY90p\n" \
"Wwrj+OQYCLllJVD9VQhLVOXLg1bSuqkld3PCQBFNvrvk4Up61wuu/Oj5c5g8vwYd\n" \
"0ul29sU3heTw4ZXifpbMXRM36LFNH5uLrSba\n" \
"-----END CERTIFICATE-----\n";

/* create an instance of WiFiClientSecure */
WiFiClientSecure espClient;
PubSubClient client(espClient);

/*LED GPIO pin*/
const char led = 4;

/* topics */
#define COUNTER_TOPIC    "smarthome/room1/counter"
#define LED_TOPIC     "smarthome/room1/led" /* 1=on, 0=off */

long lastMsg = 0;
char msg[20];
int counter = 0;

void receivedCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message received: ");
  Serial.println(topic);

  Serial.print("payload: ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  /* we got '1' -> on */
  if ((char)payload[0] == '1') {
    digitalWrite(led, HIGH); 
  } else {
    /* we got '0' -> on */
    digitalWrite(led, LOW);
  }

}

void mqttconnect() {
  /* Loop until reconnected */
  while (!client.connected()) {
    Serial.print("MQTT connecting ...");
    /* client ID */
    String clientId = "ESP32Client";
    /* connect now */
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      /* subscribe topic */
      client.subscribe(LED_TOPIC);
    } else {
      Serial.print("failed, status code =");
      Serial.print(client.state());
      Serial.println("try again in 5 seconds");
      /* Wait 5 seconds before retrying */
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  /* set led as output to control led on-off */
  pinMode(led, OUTPUT);

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  /*setup MDNS for ESP32 */
  if (!MDNS.begin("esp32")) {
      Serial.println("Error setting up MDNS responder!");
      while(1) {
          delay(1000);
      }
  }
  /* get the IP address of server by MDNS name */
  Serial.println("mDNS responder started");
  IPAddress serverIp = MDNS.queryHost(serverHostname);
  Serial.print("IP address of server: ");
  Serial.println(serverIp.toString());
  /* set SSL/TLS certificate */
  espClient.setCACert(ca_cert);
  /* configure the MQTT server with IPaddress and port */
  client.setServer(serverIp, 8883);
  /* this receivedCallback function will be invoked 
  when client received subscribed topic */
  client.setCallback(receivedCallback);
  
}
void loop() {
  /* if client was disconnected then try to reconnect again */
  if (!client.connected()) {
    mqttconnect();
  }
  /* this function will listen for incomming 
  subscribed topic-process-invoke receivedCallback */
  client.loop();
  /* we increase counter every 3 secs
  we count until 3 secs reached to avoid blocking program if using delay()*/
  long now = millis();
  if (now - lastMsg > 3000) {
    lastMsg = now;
    if (counter < 100) {
      counter++;
      snprintf (msg, 20, "%d", counter);
      /* publish the message */
      client.publish(COUNTER_TOPIC, msg);
    }else {
      counter = 0;  
    }
  }
}
3. Result
Read More

How to set up secure transportation for MQTT Mosquitto broker with SSL/TLS

7:19 PM 3
1. Introduction
In Demo 29 you knew how SSL/TLS is important to make communication between client and server safer. So I will show you how to set up secure transportation for MQTT Mosquitto broker with SSL/TLS.
2. Steps
Note: This way is applied for Linux OS
2.1. Generating the server self-certificates
- Download the script: https://github.com/owntracks/tools/blob/master/TLS/generate-CA.sh
- Set execution permission: chmod +x generate-CA.sh
- Run the script: ./generate-CA.sh
- You will see created files:
Figure: Created files when running the script
- Run commands:  
     sudo cp ca.crt /etc/mosquitto/certs
     sudo cp tuan-ThinkPad-T410.* /etc/mosquitto/certs
2.2. Mosquitto configuration
- Run command:
     sudo gedit /etc/mosquitto/mosquitto.conf
- And paste lines to it:
     # Plain MQTT protocol
     listener 1883
     # End of plain MQTT configuration
     # MQTT over TLS/SSL
     listener 8883
     cafile /etc/mosquitto/certs/ca.crt
     certfile /etc/mosquitto/certs/tuan-ThinkPad-T410.crt
     keyfile /etc/mosquitto/certs/tuan-ThinkPad-T410.key

- Restart mosquitto server:
     sudo service mosquitto restart
2.3. Testing MQTT TLS/SSL configuration
- Run commands:
     mosquitto_sub -t \# -v --cafile /etc/mosquitto/certs/ca.crt -p 8883
     mosquitto_pub --cafile /etc/mosquitto/certs/ca.crt -h localhost -t "smarthome/room1/led" -m 0 -p 8883


 Figure: Testing result
 

Read More

Wednesday, July 19, 2017

Demo 27: How to use Arduino ESP32 BLE (Bluetooth Low Energy) as a GATT server

9:19 AM 1
1. Introduction
In this tutorial (2 parts: part 1: GATT server and part 2: GATT client), I will show you how to use BLE (Bluetooth Low Energy) in Arduino ESP32. Firstly, we need to know some basic concepts.Or you can refer here.
-  GAP stands for Generic Access Profile. GAP makes your device visible to the other BLE devices (BLE devices can scan your BLE device), and determines how two devices can interact with each other.
- There are 2 kinds of devices in BLE communication: Central devices and Peripheral devices.
+ Peripheral devices are small, low power, resource constrained devices that can connect and give data to to a powerful central device. Example: a heart rate monitor.
+ Central devices are usually the powerful  devices like smart phone or tablet. It can scan and connect to any peripheral device that is advertising information to get data from peripheral device.
- When the connection is established between the peripheral and a central device, the advertising process will generally stop and you will use GATT (Generic Attribute Profile) services and characteristics to communicate in both directions.
- GATT is based on a traditional client-server architecture including GATT Server and GATT Client.
- The peripheral device keeps the role as the GATT Server, and the central device keeps the role as GATT Client, which sends requests to this server. 
- Beside that GATT also has some concepts called Profiles, Services and Characteristics. 
+ Profiles: is a collection of Services.
+ Services: is a collection of characteristic. Service distinguishes from other services by a unique 16-bit numeric ID called a UUID.
+ Characteristics: is data. Characteristic distinguishes from other Characteristics by a unique 16-bit or 128-bit numeric ID called a UUID.
Figure: Example of Hear Rate Profile


- In this tutorial, I will make a demo using GATT to turn ON/OFF a LED. So ESP32 will act as a GATT server and a GATT client (I use Raspbbery Pi3 with BLE or if your laptop is equipped with BLE you can use it).

2. Software
- We will use LightBlue on iOS or on Android for testing or  Bluez Gatttool for Raspberry Pi3 as a GATT client to connect to our ESP32 GATT server.
2.1 Bluez Gatttool for Raspberry Pi3

If the gatttool was not installed on your RPi3 then you can follow these steps to install it:
+ wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.46.tar.xz
+ tar xvf bluez-5.46.tar.xz
+ sudo apt-get install libglib2.0-dev libdbus-1-dev libusb-dev libudev-dev libical-dev systemd libreadline-dev
+ ./configure --enable-library
+ make -j8 && sudo make install
+ sudo cp attrib/gatttool /usr/local/bin/

- Run a BLE scan: sudo hcitool lescan : It will return the MAC address of BLE device
- Connect to the BLE device: sudo gatttool -b BLE_ADDR -I : then type connect to connect. We use sudo hcitool lescan to get BLE_ADDR
Note: If you could not connect after typing connect, you should try typing connect some times.
- List all uuids of services: primary
- List all available handles (Handles are the «connection points» where you can read and write access data): char-desc
- Read from a handle: char-read-hnd
- Write to a handle: char-write-req
2.2 ESP32 GATT server
The role of GATT server:
- Receive the Write command of GATT client and convert it to LED state
- Sending its temperature to GATT client using BLE notification
The name of our ESP32 BLE device is "ESP_GATTS_IOTSHARING" and we will create 1 service GATTS_SERVICE_UUID_TEST_LED with 2 characteristics: GATTS_CHAR_UUID_LED_CTRL and GATTS_CHAR_UUID_TEMP_NOTI.
The characteristic GATTS_CHAR_UUID_LED_CTRL with read and write permission to write the LED control command to it and to read the data from BLE device (For testing the read request only returns "iotsharing.com" string).
The characteristic GATTS_CHAR_UUID_TEMP_NOTI with notification permission that enable the GATT client to register BLE notification to receive the temperature data of the GATT server.

I also wrote many comments in the code, so you can read and map them with theory above.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-fpermissive"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "bt.h"
#include "bta_api.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_main.h"

#include "sdkconfig.h"

#pragma GCC diagnostic pop

/* this function will be invoked to handle incomming events */
static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

#define LED                          4
#define GATTS_SERVICE_UUID_TEST_LED   0xAABB
#define GATTS_CHAR_UUID_LED_CTRL      0xAA01
#define GATTS_CHAR_UUID_TEMP_NOTI     0xBB01
#define GATTS_NUM_HANDLES     8

#define TEST_DEVICE_NAME            "ESP_GATTS_IOTSHARING"
/* maximum value of a characteristic */
#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0xFF

/* profile info */
#define PROFILE_ON_APP_ID 0
/* characteristic ids for led ctrl and temp noti */
#define CHAR_NUM 2
#define CHARACTERISTIC_LED_CTRL_ID    0
#define CHARACTERISTIC_TEMP_NOTI_ID   1

/* value range of a attribute (characteristic) */
uint8_t attr_str[] = {0x00};
esp_attr_value_t gatts_attr_val =
{
    .attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX,
    .attr_len     = sizeof(attr_str),
    .attr_value   = attr_str,
};
/* custom uuid */
static uint8_t service_uuid128[32] = {
    /* LSB <--------------------------------------------------------------------------------> MSB */
    //first uuid, 16bit, [12],[13] is the value
    0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xAB, 0xCD, 0x00, 0x00,
};

static esp_ble_adv_data_t test_adv_data = {
    .set_scan_rsp = false,
    .include_name = true,
    .include_txpower = true,
    .min_interval = 0x20,
    .max_interval = 0x40,
    .appearance = 0x00,
    .manufacturer_len = 0,
    .p_manufacturer_data =  NULL,
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = 16,
    .p_service_uuid = service_uuid128,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

//this variable holds advertising parameters
esp_ble_adv_params_t test_adv_params;

//this structure holds the information of characteristic
struct gatts_characteristic_inst{
    esp_bt_uuid_t char_uuid;
    esp_bt_uuid_t descr_uuid;
    uint16_t char_handle;
    esp_gatt_perm_t perm;
    esp_gatt_char_prop_t property;
    uint16_t descr_handle;
};

//this structure holds the information of current BLE connection
struct gatts_profile_inst {
    esp_gatts_cb_t gatts_cb;
    uint16_t gatts_if;
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_handle;
    esp_gatt_srvc_id_t service_id;
    struct gatts_characteristic_inst chars[CHAR_NUM];
};

//this variable holds the information of current BLE connection
static struct gatts_profile_inst test_profile;

/* 
This callback will be invoked when GAP advertising events come.
Refer GAP BLE callback event type here: 
https://github.com/espressif/esp-idf/blob/master/components/bt/bluedroid/api/include/esp_gap_ble_api.h
*/
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    switch (event) {
  case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
   esp_ble_gap_start_advertising(&test_adv_params);
   break;
  case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
   esp_ble_gap_start_advertising(&test_adv_params);
   break;
  case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
   esp_ble_gap_start_advertising(&test_adv_params);
   break;
  case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
   //advertising start complete event to indicate advertising start successfully or failed
   if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
    Serial.printf("\nAdvertising start failed\n");
   }
   break;
  case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
   if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
    Serial.printf("\nAdvertising stop failed\n");
   }
   else {
    Serial.printf("\nStop adv successfully\n");
   }
   break;
  default:
   break;
    }
}

//process BLE write event ESP_GATTS_WRITE_EVT
void process_write_event_env(esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param){
    /* check char handle and set LED */
    if(test_profile.chars[CHARACTERISTIC_LED_CTRL_ID].char_handle == param->write.handle){
        if(param->write.len == 1){
            uint8_t state = param->write.value[0];
            digitalWrite(LED, state);
        }
    }
    /* send response if any */
    if (param->write.need_rsp){
        Serial.printf("respond");
        esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
        if (response_err != ESP_OK){
            Serial.printf("\nSend response error\n");
        }
    }
}

/*
This callback will will be invoked when GATT BLE events come.
Refer GATT Server callback function events here: 
https://github.com/espressif/esp-idf/blob/master/components/bt/bluedroid/api/include/esp_gatts_api.h
*/
static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
    switch (event) {
    //When register application id, the event comes
    case ESP_GATTS_REG_EVT:{
        Serial.printf("\nREGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id);
        test_profile.service_id.is_primary = true;
        test_profile.service_id.id.inst_id = 0x00;
        test_profile.service_id.id.uuid.len = ESP_UUID_LEN_16;
        test_profile.service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_LED;
  //after finishing registering, the ESP_GATTS_REG_EVT event comes, we start the next step is creating service
        esp_ble_gatts_create_service(gatts_if, &test_profile.service_id, GATTS_NUM_HANDLES);
        break;
    }
    case ESP_GATTS_READ_EVT: {
        Serial.printf("\nESP_GATTS_READ_EVT\n");
        esp_gatt_rsp_t rsp;
        memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
        rsp.attr_value.handle = param->read.handle;
        rsp.attr_value.len = 14;
        rsp.attr_value.value[0] = 105;
        rsp.attr_value.value[1] = 111;
        rsp.attr_value.value[2] = 116;
        rsp.attr_value.value[3] = 115;
        rsp.attr_value.value[4] = 104;
        rsp.attr_value.value[5] = 97;
        rsp.attr_value.value[6] = 114;
        rsp.attr_value.value[7] = 105;
        rsp.attr_value.value[8] = 110;
        rsp.attr_value.value[9] = 103;
        rsp.attr_value.value[10] = 46;
        rsp.attr_value.value[11] = 99;
        rsp.attr_value.value[12] = 111;
        rsp.attr_value.value[13] = 109;
  /* 
  When central device send READ request to GATT server, the ESP_GATTS_READ_EVT comes 
  This always responds "iotsharing.com" string 
  */
        esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
                                    ESP_GATT_OK, &rsp);
        break;
    }
 /* 
 When central device send WRITE request to GATT server, the ESP_GATTS_WRITE_EVT comes 
 Invoking process_write_event_env() to process and send response if any
 */
    case ESP_GATTS_WRITE_EVT: {
        Serial.printf("\nESP_GATTS_WRITE_EVT\n");
        process_write_event_env(gatts_if, param);
        break;
    }
    // When create service complete, the event comes
    case ESP_GATTS_CREATE_EVT:{
        Serial.printf("\nstatus %d, service_handle %x, service id %x\n", param->create.status, param->create.service_handle, param->create.service_id.id.uuid.uuid.uuid16);
        // store service handle and add characteristics
        test_profile.service_handle = param->create.service_handle;
        // LED controller characteristic
        esp_ble_gatts_add_char(test_profile.service_handle, &test_profile.chars[CHARACTERISTIC_LED_CTRL_ID].char_uuid,
                               test_profile.chars[CHARACTERISTIC_LED_CTRL_ID].perm,
                               test_profile.chars[CHARACTERISTIC_LED_CTRL_ID].property,
                               &gatts_attr_val, NULL);
        // temperature monitoring characteristic
        esp_ble_gatts_add_char(test_profile.service_handle, 
                               &test_profile.chars[CHARACTERISTIC_TEMP_NOTI_ID].char_uuid,
                               test_profile.chars[CHARACTERISTIC_TEMP_NOTI_ID].perm,
                               test_profile.chars[CHARACTERISTIC_TEMP_NOTI_ID].property,
                               &gatts_attr_val, NULL);
    //and start service
        esp_ble_gatts_start_service(test_profile.service_handle);
        break;
    }
 //When add characteristic complete, the event comes
    case ESP_GATTS_ADD_CHAR_EVT: {
        Serial.printf("\nADD_CHAR_EVT, status %d,  attr_handle %x, service_handle %x, char uuid %x\n",
                param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle, param->add_char.char_uuid.uuid.uuid16);
        /* store characteristic handles for later usage */
        if(param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_LED_CTRL){
            test_profile.chars[CHARACTERISTIC_LED_CTRL_ID].char_handle = param->add_char.attr_handle;
        }else if(param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_TEMP_NOTI){
            test_profile.chars[CHARACTERISTIC_TEMP_NOTI_ID].char_handle = param->add_char.attr_handle;
        }   
  //if having characteristic description calling esp_ble_gatts_add_char_descr() here 
        break;
    }
 //When add descriptor complete, the event comes
    case ESP_GATTS_ADD_CHAR_DESCR_EVT:{
        Serial.printf("\nESP_GATTS_ADD_CHAR_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n",
                 param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);
        break;
    }
    /* when disconneting, send advertising information again */
    case ESP_GATTS_DISCONNECT_EVT:{
        esp_ble_gap_start_advertising(&test_adv_params);
        break;
    }
    // When gatt client connect, the event comes
    case ESP_GATTS_CONNECT_EVT: {
        Serial.printf("\nESP_GATTS_CONNECT_EVT\n");
        esp_ble_conn_update_params_t conn_params = {0};
        memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
        /* For the IOS system, please reference the apple official documents about the ble connection parameters restrictions. */
        conn_params.latency = 0;
        conn_params.max_int = 0x50;    // max_int = 0x50*1.25ms = 100ms
        conn_params.min_int = 0x30;    // min_int = 0x30*1.25ms = 60ms
        conn_params.timeout = 1000;    // timeout = 1000*10ms = 10000ms
        test_profile.conn_id = param->connect.conn_id;
        //start sent the update connection parameters to the peer device.
        esp_ble_gap_update_conn_params(&conn_params);
        break;
    }
    default:
        break;
    }
}

static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
    /* If event is register event, store the gatts_if for the profile */
    if (event == ESP_GATTS_REG_EVT) {
        if (param->reg.status == ESP_GATT_OK) {
            test_profile.gatts_if = gatts_if;
        } else {
            Serial.printf("\nReg app failed, app_id %04x, status %d\n",
                    param->reg.app_id,
                    param->reg.status);
            return;
        }
    }
    /* here call each profile's callback */
    if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
            gatts_if == test_profile.gatts_if) {
        if (test_profile.gatts_cb) {
            test_profile.gatts_cb(event, gatts_if, param);
        }
    }
}

void setup(){
    Serial.begin(115200);
    pinMode(LED, OUTPUT);
    digitalWrite(LED, LOW);
    /* initialize advertising info */
    test_adv_params.adv_int_min        = 0x20;
    test_adv_params.adv_int_max        = 0x40;
    test_adv_params.adv_type           = ADV_TYPE_IND;
    test_adv_params.own_addr_type      = BLE_ADDR_TYPE_PUBLIC;
    test_adv_params.channel_map        = ADV_CHNL_ALL;
    test_adv_params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
    /* initialize profile and characteristic permission and property*/
    test_profile.gatts_cb = gatts_profile_event_handler;
    test_profile.gatts_if = ESP_GATT_IF_NONE;
    test_profile.chars[CHARACTERISTIC_LED_CTRL_ID].char_uuid.len = ESP_UUID_LEN_16;
    test_profile.chars[CHARACTERISTIC_LED_CTRL_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_LED_CTRL;
    test_profile.chars[CHARACTERISTIC_LED_CTRL_ID].perm = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
    test_profile.chars[CHARACTERISTIC_LED_CTRL_ID].property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE;
    test_profile.chars[CHARACTERISTIC_TEMP_NOTI_ID].char_uuid.len = ESP_UUID_LEN_16;
    test_profile.chars[CHARACTERISTIC_TEMP_NOTI_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEMP_NOTI;
    test_profile.chars[CHARACTERISTIC_TEMP_NOTI_ID].perm = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
    test_profile.chars[CHARACTERISTIC_TEMP_NOTI_ID].property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
    
    esp_err_t ret;
    /* initialize BLE and bluedroid */
    btStart();
    ret = esp_bluedroid_init();
    if (ret) {
        Serial.printf("\n%s init bluetooth failed\n", __func__);
        return;
    }
    ret = esp_bluedroid_enable();
    if (ret) {
        Serial.printf("\n%s enable bluetooth failed\n", __func__);
        return;
    }
    // set BLE name and broadcast advertising info so that the world can see you
    esp_ble_gap_set_device_name(TEST_DEVICE_NAME);
    esp_ble_gap_config_adv_data(&test_adv_data);
    // register callbacks to handle events of GAp and GATT
    esp_ble_gatts_register_callback(gatts_event_handler);
    esp_ble_gap_register_callback(gap_event_handler);
    // register profiles with app id
    esp_ble_gatts_app_register(CHARACTERISTIC_LED_CTRL_ID);
}

long lastMsg = 0;
//send temperature value to registered notification client every 5 seconds via GATT notification
void loop(){
    long now = millis();
    if (now - lastMsg > 5000) {
        lastMsg = now;
        uint8_t temp = random(0, 50);
        esp_ble_gatts_send_indicate(test_profile.gatts_if, 
                                    test_profile.conn_id, 
                                    test_profile.chars[CHARACTERISTIC_TEMP_NOTI_ID].char_handle,
                                    sizeof(temp), &temp, false);
    }
}
3. Result
3.1 Using LightBlue to connect to GATT server
Figure: 2 characteristics with uuid AA01 and BB01
 Fifure: Choose Write to write value to GATT server
 Figure: Choose 1 or 0 to turn ON/OFF LED
 Figure: press Listen for notifications to receive temp data
 Figure: Temp data are 0x25, 0x27, 0x04, ...
3.2 Using Bluez Gatttool + Raspberry Pi3 to connect to GATT server
If using Bluez Gatttool for Raspberry Pi3:
- Using sudo hcitool lescan to get the address of my BLE device. It is 30:AE:A4:02:70:76. Then I used sudo gatttool -b 30:AE:A4:02:70:76 -I to establish connection to it.

- Using char-desc command we will see our characteristic uuids that we set in the code, are aa01 and bb01 with handles are 0x002a and 0x002c. So we just operate on these handles with commands below. 
 Figure: Available characteristics
 Figure: write data to ESP32 BLE

Read More
Thường mất vài phút để quảng cáo xuất hiện trên trang nhưng thỉnh thoảng, việc này có thể mất đến 1 giờ. Hãy xem hướng dẫn triển khai mã của chúng tôi để biết thêm chi tiết. Ðã xong