/* OpenMQTTGateway - ESP8266 or Arduino program for home automation Act as a wifi or ethernet gateway between your 433mhz/infrared IR signal/BLE and a MQTT broker Send and receiving command by MQTT This gateway enables to: - publish MQTT data to a different topic related to BLE devices rssi signal Copyright: (c)Florian ROBERT This file is part of OpenMQTTGateway. OpenMQTTGateway is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenMQTTGateway is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Thanks to wolass https://github.com/wolass for suggesting me HM 10 and dinosd https://github.com/dinosd/BLE_PROXIMITY for inspiring me how to implement the gateway */ #ifdef ZgatewayBT #ifdef ESP32 /* Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp Ported to Arduino ESP32 by Evandro Copercini */ // core task implementation thanks to https://techtutorialsx.com/2017/05/09/esp32-running-code-on-a-specific-core/ #include #include #include #include //core on which the BLE detection task will run static int taskCore = 0; class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { void onResult(BLEAdvertisedDevice advertisedDevice) { String mac_adress = advertisedDevice.getAddress().toString().c_str(); mac_adress.replace(":",""); mac_adress.toUpperCase(); String mactopic = subjectBTtoMQTT + mac_adress; if (advertisedDevice.haveName()){ String nameBLE = advertisedDevice.getName().c_str(); trc(mactopic + " " + nameBLE); client.publish((char *)(mactopic + "/name").c_str(),(char *)nameBLE.c_str()); } String rssi = String(advertisedDevice.getRSSI()); trc(mactopic + " " + rssi); client.publish((char *)mactopic.c_str(),(char *)rssi.c_str()); if (advertisedDevice.haveServiceData()){ trc(F("Get service data ")); std::string serviceData = advertisedDevice.getServiceData(); int serviceDataLength = serviceData.length(); String returnedString = ""; for (int i=0; isetAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster BLEScanResults foundDevices = pBLEScan->start(Scan_duration); } } #else // arduino or ESP8266 working with HM10/11 #include #define STRING_MSG "OK+DISC:" #define RESPONSE_MSG "OK+DISIS" #define RESP_END_MSG "OK+DISCE" #define SETUP_MSG "OK+RESET" SoftwareSerial softserial(BT_RX, BT_TX); String returnedString = ""; unsigned long timebt = 0; // this struct define which parts of the hexadecimal chain we extract and what to do with these parts struct decompose d[6] = {{"mac",16,12,true},{"typ",28,2,false},{"rsi",30,2,false},{"rdl",32,2,false},{"sty",44,4,true},{"rda",34,60,false}}; void setupBT() { softserial.begin(9600); softserial.print(F("AT+ROLE1")); delay(100); softserial.print(F("AT+IMME1")); delay(100); softserial.print(F("AT+RESET")); delay(100); } #ifdef ZgatewayBT_v6xx #define QUESTION_MSG "AT+DISA?" boolean BTtoMQTT() { //extract serial data from module in hexa format while (softserial.available() > 0) { int a = softserial.read(); if (a < 16) { returnedString = returnedString + "0"; } returnedString = returnedString + String(a,HEX); } if (millis() > (timebt + TimeBtw_Read)) {//retriving data timebt = millis(); #if defined(ESP8266) yield(); #endif if (returnedString != "") { size_t pos = 0; while ((pos = returnedString.lastIndexOf(delimiter)) != -1) { #if defined(ESP8266) yield(); #endif String token = returnedString.substring(pos); trc(token); returnedString.remove(pos,returnedString.length() ); char token_char[token.length()+1]; token.toCharArray(token_char, token.length()+1); trc(token); if ( token.length() > 60){// we extract data only if we have detailled infos for(int i =0; i<6;i++) { extract_char(token_char,d[i].extract,d[i].start, d[i].len ,d[i].reverse, false); if (i == 3) d[5].len = (int)strtol(d[i].extract, NULL, 16) * 2; // extracting the length of the rest data } if((strlen(d[0].extract)) == 12) // if a mac adress is detected we publish it { strupp(d[0].extract); String mactopic(d[0].extract); trc(mactopic); mactopic = subjectBTtoMQTT + mactopic; int rssi = (int)strtol(d[2].extract, NULL, 16) - 256; char val[12]; sprintf(val, "%d", rssi); client.publish((char *)mactopic.c_str(),val); if (strcmp(d[4].extract, "fe95") == 0) boolean result = process_miflora_data(0,d[5].extract,d[0].extract); return true; } } } returnedString = ""; //init data string return false; } softserial.print(F(QUESTION_MSG)); return false; }else{ return false; } } void strupp(char* beg) { while (*beg = toupper(*beg)) ++beg; } #endif #ifndef ZgatewayBT_v6xx #define QUESTION_MSG "AT+DISI?" boolean BTtoMQTT() { while (softserial.available() > 0) { #if defined(ESP8266) yield(); #endif String discResult = softserial.readString(); if (discResult.indexOf(STRING_MSG)>=0){ discResult.replace(RESPONSE_MSG,""); discResult.replace(RESP_END_MSG,""); float device_number = discResult.length()/78.0; if (device_number == (int)device_number){ // to avoid publishing partial values we detect if the serial data has been fully read = a multiple of 78 trc(F("Sending BT data to MQTT")); #if defined(ESP8266) yield(); #endif for (int i=0;i<(int)device_number;i++){ String onedevice = discResult.substring(0,78); onedevice.replace(STRING_MSG,""); String mac = onedevice.substring(53,65); String rssi = onedevice.substring(66,70); String mactopic = subjectBTtoMQTT + mac; trc(mactopic + " " + rssi); client.publish((char *)mactopic.c_str(),(char *)rssi.c_str()); discResult = discResult.substring(78); } return true; } } if (discResult.indexOf(SETUP_MSG)>=0) { trc(F("Connection OK to HM-10")); } } if (millis() > (timebt + TimeBtw_Read)) {//retriving value of adresses and rssi timebt = millis(); #if defined(ESP8266) yield(); #endif softserial.print(F(QUESTION_MSG)); } return false; } #endif #endif boolean process_miflora_data(int offset, char * rest_data, char * mac_adress){ int data_length = 0; switch (rest_data[51 + offset]) { case '1' : case '2' : case '3' : case '4' : data_length = ((rest_data[51 + offset] - '0') * 2)+1; trc(String(data_length)); break; default: trc("can't read data_length"); return false; } char rev_data[data_length]; char data[data_length]; memcpy( rev_data, &rest_data[52 + offset], data_length ); rev_data[data_length] = '\0'; // reverse data order revert_hex_data(rev_data, data, data_length); double value = strtol(data, NULL, 16); trc(String(value)); char val[12]; String mactopic(mac_adress); mactopic = subjectBTtoMQTT + mactopic; // following the value of digit 47 we determine the type of data we get from the sensor switch (rest_data[47 + offset]) { case '9' : mactopic = mactopic + "/" + "fer"; dtostrf(value,0,0,val); break; case '4' : mactopic = mactopic + "/" + "tem"; dtostrf(value/10,3,1,val); // temp has to be divided by 10 break; case '7' : mactopic = mactopic + "/" + "lux"; dtostrf(value,0,0,val); break; case '8' : mactopic = mactopic + "/" + "hum"; dtostrf(value,0,0,val); break; default: trc("can't read values"); return false; } client.publish((char *)mactopic.c_str(),val);; trc(String(val)); return true; } #endif