Instantly share code, notes, and snippets.

Embed
What would you like to do?
IoT BOT to monitor BLE tags using ESP32
#include <codecvt>
#include <string>
#include <cassert>
#include <locale>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#include <aJSON.h>
#define DEBUG
#define MONITOR_NAME "m01"
#define SCAN_TIME 20
#define SLEEP_TIME 30
#define WLAN_SSID "{your_ssid}"
#define WLAN_PSWD "{your_password}"
#define API_HOST "{api_host}"
#define API_PATH "{api_path}"
#define API_KEY "{api_key}"
#define IFTTT_HOST "maker.ifttt.com"
#define IFTTT_PATH "/trigger/{your_event}/with/key/{your_key}"
#define API_ROOT_CA "{api_root_ca}"
#define IFTTT_ROOT_CA "{ifttt_root_ca}"
BLEScan* pBLEScan;
WiFiClientSecure client;
void wifiInit();
void wifiConnect();
bool checkWifiConnected();
void updateAPI(BLEScanResults results);
void sendIftttEvent(char* name, int action);
String convAddress(BLEAddress address);
String UTF16toUTF8(String str);
std::string utf16_to_utf8(std::u16string const& src);
void setup()
{
delay(500);
#ifdef DEBUG
// デバッグ用にシリアルを開く
Serial.begin(115200);
delay(500);
Serial.println("[start]");
#endif
// BLEスキャン
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
BLEDevice::init("");
pBLEScan = BLEDevice::getScan();
pBLEScan->setActiveScan(false);
#ifdef DEBUG
Serial.println("BLE Scanning...");
#endif
BLEScanResults scanResults = pBLEScan->start(SCAN_TIME);
int nTags = scanResults.getCount();
#ifdef DEBUG
Serial.print("BLE Scan done! (");
Serial.print(nTags);
Serial.println(" devices found)");
#endif
// スキャン結果をWEB APIに送信、レスポンスに応じてIFTTTにイベント送信
if (nTags > 0)
{
// WiFi接続
wifiInit();
wifiConnect();
while (!checkWifiConnected())
{
wifiConnect();
}
// HTTP通信
updateAPI(scanResults);
}
// deep sleep
#ifdef DEBUG
Serial.println("Going to sleep now...");
#endif
//esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
//esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF);
//esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
//esp_deep_sleep_pd_config(ESP_PD_DOMAIN_MAX, ESP_PD_OPTION_OFF);
esp_sleep_enable_timer_wakeup(SLEEP_TIME * 1000 * 1000);
esp_deep_sleep_start();
}
void loop()
{
}
void wifiInit()
{
WiFi.mode(WIFI_STA);
WiFi.setAutoConnect(false);
WiFi.setAutoReconnect(false);
}
void wifiConnect()
{
#ifdef DEBUG
Serial.print("Try to connect to SSID: ");
Serial.println(WLAN_SSID);
#endif
WiFi.begin(WLAN_SSID, WLAN_PSWD);
}
bool checkWifiConnected()
{
int count = 0;
while (WiFi.status() != WL_CONNECTED) {
#ifdef DEBUG
Serial.print(".");
#endif
delay(1000);
count++;
if (count > 15) { // about 15s
#ifdef DEBUG
Serial.println("(wifiConnect) failed!");
#endif
return false;
}
}
#ifdef DEBUG
Serial.print("Connected to ");
Serial.print(WLAN_SSID);
Serial.print(" (IP = ");
Serial.print(WiFi.localIP());
Serial.println(")");
#endif
return true;
}
// BLEタグMACアドレスを書式変換
String convAddress(BLEAddress address)
{
String out = String(address.toString().c_str());
out.toUpperCase();
return out;
}
void updateAPI(BLEScanResults results)
{
// スキャン結果からHTTPリクエスト用のJSONを生成
unsigned char nTags = results.getCount();
if (nTags > 0)
{
String data = "{\"monitor_name\":\"" + String(MONITOR_NAME) + "\",\"tag_detected\":[\"";
//String data = "{\"tag_detected\":[\"";
data += convAddress(results.getDevice(0).getAddress()) + "\"";
for (unsigned char i=1; i<nTags; i++)
{
data += ",\"" + convAddress(results.getDevice(i).getAddress()) + "\"";
}
data += "]}";
// HTTPクライアント準備
client.setCACert(API_ROOT_CA);
// HTTPリクエスト
char buf[512];
if (!client.connect(API_HOST, 443))
{
#ifdef DEBUG
Serial.println("Connection failed");
#endif
}
else
{
#ifdef DEBUG
Serial.println("Connected to API");
Serial.println("--- request body ---");
Serial.println(data);
#endif
// HTTPリクエストを送信
sprintf(buf, "POST %s HTTP/1.1\n", API_PATH);
client.print(buf);
sprintf(buf, "Host: %s\n", API_HOST);
client.print(buf);
sprintf(buf, "x-api-key: %s\n", API_KEY);
client.print(buf);
client.print("User-Agent: ESP32/1.0\n");
client.print("Connection: close\n");
client.print("Content-Type: application/json\n");
sprintf(buf, "Content-Length: %d\n\n", data.length());
client.print(buf);
client.print(data.c_str());
// レスポンスを読み込み
// レスポンスヘッダを読み飛ばす
char cbuf0 = '\0';
char cbuf1 = '\0';
char cbuf2 = '\0';
while (client.connected())
{
char c = client.read();
if (cbuf0 == '\r' && cbuf1 == '\n' && cbuf2 == '\r' && c == '\n')
{
break;
}
cbuf0 = cbuf1;
cbuf1 = cbuf2;
cbuf2 = c;
}
// レスポンスボディを読み込む
int i = 0;
while (client.available()) {
buf[i++] = client.read();
}
buf[i] = '\0';
String body = String(buf);
body = UTF16toUTF8(body);
body.toCharArray(buf, sizeof(buf));
#ifdef DEBUG
Serial.println("--- response body ---");
Serial.println(body);
Serial.println("------");
#endif
}
// HTTP切断
if (client.connected())
{
client.stop();
}
// レスポンスボディをJSONパース
aJsonObject* root = aJson.parse(buf);
if (root == NULL)
{
#ifdef DEBUG
Serial.println("aJson.parse() failed");
#endif
}
else
{
aJsonObject* updated = aJson.getObjectItem(root, "updated");
if (updated != NULL)
{
unsigned char num = aJson.getArraySize(updated);
for (unsigned char i=0; i<num; i++)
{
aJsonObject* elem = aJson.getArrayItem(updated, i);
if (elem != NULL)
{
aJsonObject* o_name = aJson.getObjectItem(elem, "name");
aJsonObject* o_action = aJson.getObjectItem(elem, "action");
if (o_action->valueint >= 0)
{
#ifdef DEBUG
Serial.print("send IFTTT event [");
Serial.print(o_name->valuestring);
Serial.print(" => ");
Serial.print(o_action->valueint);
Serial.println("]");
#endif
sendIftttEvent(o_name->valuestring, o_action->valueint);
}
}
}
}
}
}
}
void sendIftttEvent(char* name, int action)
{
// HTTPクライアント準備
client.setCACert(IFTTT_ROOT_CA);
if (!client.connect(IFTTT_HOST, 443))
{
#ifdef DEBUG
Serial.println("Connection failed");
#endif
}
else
{
#ifdef DEBUG
Serial.println("Connected to IFTTT");
#endif
char buf[256];
char data[256];
if (action == 1)
{
sprintf(buf, "%sが自宅に着きました (%s)", name, MONITOR_NAME);
}
else
{
sprintf(buf, "%sが自宅から出ました (%s)", name, MONITOR_NAME);
}
int data_len = sprintf(data, "{\"value1\":\"%s\",\"value2\":\"%s\",\"value3\":%d}", buf, name, action);
#ifdef DEBUG
Serial.println("--- request body ---");
Serial.println(data);
#endif
sprintf(buf, "POST %s HTTP/1.1\n", IFTTT_PATH);
client.print(buf);
sprintf(buf, "Host: %s\n", IFTTT_HOST);
client.print(buf);
client.print("User-Agent: ESP32/1.0\n");
client.print("Connection: close\n");
client.print("Content-Type: application/json\n");
sprintf(buf, "Content-Length: %d\n\n", data_len);
client.print(buf);
client.print(data);
// レスポンスを読み込み
// レスポンスヘッダを読み飛ばす
char cbuf0 = '\0';
char cbuf1 = '\0';
char cbuf2 = '\0';
while (client.connected())
{
char c = client.read();
if (cbuf0 == '\r' && cbuf1 == '\n' && cbuf2 == '\r' && c == '\n')
{
break;
}
cbuf0 = cbuf1;
cbuf1 = cbuf2;
cbuf2 = c;
}
// レスポンスボディを読み込む
int i = 0;
while (client.available()) {
buf[i++] = client.read();
}
buf[i] = '\0';
#ifdef DEBUG
Serial.println("--- response body ---");
Serial.println(buf);
Serial.println("------");
#endif
}
// 切断
if (client.connected())
{
client.stop();
}
}
//******************************************
String UTF16toUTF8(String str)
{
str.replace("\\u","\\");
str += '\0';
uint16_t len = str.length();
char16_t utf16code[len];
int i=0;
String str4 = "";
for(int j=0; j<len; j++){
if(str[j] == 0x5C){ //'\'を消去
j++;
for(int k=0; k<4; k++){
str4 += str[j+k];
}
utf16code[i] = strtol(str4.c_str(), NULL, 16); //16進文字列を16進数値に変換
str4 = "";
j = j+3;
i++;
}else if(str[j] == 0x23){ //'#'を消去
utf16code[i] = 0xFF03; //全角#に変換
i++;
}else{
utf16code[i] = (char16_t)str[j];
i++;
}
}
std::u16string u16str(utf16code);
std::string u8str = utf16_to_utf8(u16str);
String ret_str = String(u8str.c_str());
//URLに影響のある特殊文字を全角に変換
ret_str.replace("+", "");
ret_str.replace("&", "");
ret_str.replace("\\", "");
return ret_str;
}
//*****************************************************
std::string utf16_to_utf8(std::u16string const& src)
{
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
return converter.to_bytes(src);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment