新たにSwitchBot防水温湿度計を買ったので、またプログラムの変更が必要になりました。SwitchBotのデバイスは、以下のサイトの情報を元に、ServiceDataの最初の1Byteでデバイスの種類が判別できます。ただし、防水温湿度計の情報は無かったため、同様に調べたところ、0x77であることが分かりました。
Product | Device Type | Product | Device Type |
---|---|---|---|
Bot | (0x48) | 温湿度計 | (0x54) |
Humidifier | (0x65) | Curtain | (0x63) |
Motion Sensor | (0x73) | Contact Sensor | (0x64) |
Color Bulb | (0x75) | LED Strip Light | (0x72) |
Smart Lock | (0x6F) | Plug Mini | (0x67) |
温湿度計 Plus | (0x69) | 防水温湿度計 | (0x77) |
この情報を元に、プログラムの修正を行いました。i2cにセンサを直結した場合にも対応させているため、ソースがやたらと長くなっていますが、BLE関連クラスの中が重要な部分です。
main.cpp
//
// i2c直結センサ対応(ENV2,ENV3,eCO2)
// MQTTパスワードあり,暗号化なし
//
#include <M5Atom.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include "Adafruit_Sensor.h"
#include <Adafruit_BMP280.h>
#include "M5_ENV.h"
#include "Adafruit_SGP30.h"
SHT3X sht30;
QMP6988 qmp6988;
Adafruit_SGP30 sgp;
Adafruit_BMP280 bmp;
char telNo[10] ="test";
char ClientID[20];
char COCOAdevName[20];
char ENVdevName[20];
// WiFiClientSecure https_client;
WiFiClient https_client;
PubSubClient mqttClient(https_client);
BLEScan* pBLEScan;
extern int ssid_len;
extern char ssid_list[2][20];
extern char password_list[2][20];
extern char server[20];
extern int port;
extern char mqttuser[20];
extern char mqttpasswd[20];
extern char root_ca[];
int scanTime = 5; //In seconds
int seq[32];
int maxCh[32]; //デバイス内の最小チャンネル
char MAC_Addr[48];
char dbuf[32][255];
//------------------------------------------------------------------
//
//------------------------------------------------------------------
char uuid[37] = "0000fd6f-0000-1000-8000-00805f9b34fb";
// bool found = false;
bool CocoaFlag = false;
bool sgpFlag = false;
bool env3Flag = false;
bool env2Flag = false;
int deviceNum = 0;
int nearDeviceNum = 0;
int LastUpdate = 0;
char devices[64][17];
char TargetDev[17];
struct dev_info {
char mac[20]; // Mac Address
long last_update; // Last Update
};
struct dev_info DEVs[32];
void mqttPublish(const char* topicBuffer,const char* buf);
//------------------------------------------------------------------------------------
// BLE関連クラス
//------------------------------------------------------------------------------------
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if(!advertisedDevice.haveServiceData()) return;
if(advertisedDevice.haveServiceUUID()){
if (CocoaFlag && (strncmp(advertisedDevice.getServiceUUID().toString().c_str(),uuid, 36) == 0)){
strncpy(TargetDev,advertisedDevice.getAddress().toString().c_str(),17);
for (int i =0;i<deviceNum;i++){
if(strncmp(devices[i],TargetDev,17)==0) return;
}
strncpy(devices[deviceNum],TargetDev,17);
int rssi = advertisedDevice.getRSSI();
deviceNum++;
if(rssi > -69) nearDeviceNum++;
}
}
//---------------------------------------------------------------------------------------------
// Switch bot()温湿度計のデータ読み込み
//---------------------------------------------------------------------------------------------
BLEUUID serviceUUID = advertisedDevice.getServiceDataUUID();
if(
(serviceUUID.equals(BLEUUID("00000d00-0000-1000-8000-00805f9b34fb")))||
(serviceUUID.equals(BLEUUID("0000fd3d-0000-1000-8000-00805f9b34fb")))
){
std::string s = advertisedDevice.getServiceData();
std::string md = advertisedDevice.getManufacturerData();
const char* uuid = serviceUUID.toString().c_str();
const char* madata = md.c_str();
const char* sedata = s.c_str();
const char* MAC_Addr = advertisedDevice.getAddress().toString().c_str();
if (strlen(MAC_Addr)==17){
int i=0;
char topic_buf[50];
char buf[1000];
for(i=0;((strlen(DEVs[i].mac)>0)&&(i<32));i++){
if(strcmp(MAC_Addr,DEVs[i].mac)==0){
//printf("Index:%d,%s,timer:%5.2f,\n",i,DEVs[i].mac,(millis()-DEVs[i].last_update)/1000.);
if (millis() - DEVs[i].last_update > 29000){
// printf("%02x:%02x:%02x:%02x ",madata[0],madata[1],madata[2],madata[3]);
// printf("%02x:%02x:%02x:%02x ",madata[4],madata[5],madata[6],madata[7]);
// printf("%02x:%02x:%02x:%02x ",madata[8],madata[9],madata[10],madata[11]);
// printf("%02x:%02x:%02x\n",madata[12],madata[13],madata[14]);
int battery=100;
bool isTemperatureAboveFreezing;
float temperature;
int humidity;
//温湿度計 or 温湿度計plus
if((sedata[0]==0x54)||(sedata[0]==0x69)){
battery = sedata[2] & 0b01111111;
isTemperatureAboveFreezing = sedata[4] & 0b10000000;
temperature = ( sedata[3] & 0b00001111 ) / 10.0 + ( sedata[4] & 0b01111111 );
if(!isTemperatureAboveFreezing){temperature = -temperature;}
humidity = sedata[5] & 0b01111111;
}
// 防水温湿度計
if (sedata[0]==0x77){
humidity = madata[12] & 0b01111111;
isTemperatureAboveFreezing = madata[11] & 0b10000000;
temperature = (madata[10] & 0b00001111 ) / 10.0 + (madata[11] & 0b01111111 );
if(!isTemperatureAboveFreezing){temperature = -temperature;}
}
M5.dis.fillpix(0x0000f0);
snprintf (topic_buf, sizeof(topic_buf),"titc_data3/%s_%.5s/%s",telNo,MAC_Addr,"ENV");
snprintf(buf,sizeof(buf),"tmp,F,%.2f,hum,I,%d,batt,I,%d",temperature,humidity,battery);
mqttPublish(topic_buf,buf);
DEVs[i].last_update = millis();
delay(50);
M5.dis.fillpix(0x000000);
delay(50);
}
return;
}
}
sprintf(DEVs[i].mac, "%s",MAC_Addr);
DEVs[i].last_update = millis();
printf("insert New Device to %d!!\n",i);
}
}
}
};
//------------------------------------------------------------------------------------
// WiFi接続関数
//------------------------------------------------------------------------------------
void WifiConnect(int Count){
int cnt=0;
while(WiFi.status() != WL_CONNECTED) {
cnt++;
for(int i=0;(WiFi.status()!= WL_CONNECTED&&i<ssid_len);i++){
Serial.printf("connect to %s.",ssid_list[i]);
WiFi.begin(ssid_list[i], password_list[i]);
for(int j=0;(WiFi.status()!= WL_CONNECTED)&&(j<20);j++){
delay(500);Serial.print(".");
}
Serial.printf("\n");
}
if (cnt>=Count){ESP.restart();}
}
}
//------------------------------------------------------------------------------------
// 初期化メソッド
//------------------------------------------------------------------------------------
void setup() {
M5.begin(true, false, true);
Wire.begin(26, 32); // Initialize i2c_pin 26,32 @ Atom
Serial.begin(115200);
snprintf(ClientID,sizeof(ClientID),"Denpa%s",telNo);
snprintf(COCOAdevName,sizeof(COCOAdevName),"COCOA%s",telNo);
snprintf(ENVdevName,sizeof(ENVdevName),"ENV%s",telNo);
M5.dis.fillpix(0xf00000);
Serial.println("\nScanning...");
WifiConnect(3);
M5.dis.setBrightness(10);
M5.dis.fillpix(0x00f0f0);
Serial.printf("WiFi connected!!\nIP address: ");
Serial.print(WiFi.localIP());
Serial.printf("\n");
delay(500);
for(int i=0;i<32;i++){
seq[i]=-1; maxCh[i]=-1;
sprintf(dbuf[i],"BLEDEV%d",i);
}
// eCO2 Unit 接続フラグ確認
if (sgp.begin()){
sgpFlag = true;
Serial.print("Found SGP30 serial #");
Serial.print(sgp.serialnumber[0], HEX);
Serial.print(sgp.serialnumber[1], HEX);
Serial.println(sgp.serialnumber[2], HEX);
}else{Serial.println("SGP30 Not Found..");}
// Env Unit 接続フラグ確認
if(qmp6988.init()){
env3Flag = true;
Serial.println("Found ENVIII device!");
}else{
if(bmp.begin(0x76)){
env2Flag = true;
Serial.println("Found ENVII device!");
}else{Serial.println("ENV Unit Not Found..");}
}
// ボタンを押すまで60秒スリープ設定
for(int i=0;i<50;i++){
if (M5.Btn.wasPressed()) break;
M5.dis.setBrightness(10);
if(i%10<5) M5.dis.clear();//M5.dis.drawpix(0, 0x000000);
else M5.dis.fillpix(0x00f0f0);//M5.dis.drawpix(0, 0x00f0f0);
delay(100);
M5.update();
}
Serial.printf("MQTT connection start..\n");
M5.dis.setBrightness(10);
M5.dis.fillpix(0x00f0f0);
// https_client.setCACert(root_ca);
mqttClient.setServer(server,port);
// mqttClient.setBufferSize(1000);
mqttClient.connect(ClientID,mqttuser,mqttpasswd);
printf("MQTT connected!\n");
delay(500);
for(int i=0;i<15;i++) seq[i]=-1;
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), true);
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
M5.dis.fillpix(0x000000);
printf("BLE init finished!\n");
LastUpdate = millis();
mqttPublish("titc_data3","test_data");
}
//=====================================================================================
// Loop関数
//=====================================================================================
void loop() {
char buf1[100];
char buf2[100];
char topicbuf[200];
char buf[1000];
int cnt = 0;
deviceNum = 0;
nearDeviceNum = 0;
CocoaFlag = false;
// if (sgp.begin()){sgpFlag = true;}
// if(qmp6988.init()){env3Flag = true;} else{env3Flag = false;}
// if(bmp.begin(0x76)){env2Flag = true;} else{env2Flag = false;}
while(!mqttClient.connected()){
if(WiFi.status() != WL_CONNECTED) WifiConnect(3);
printf(".");
mqttClient.connect(ClientID,mqttuser,mqttpasswd);
delay(500);
}
mqttClient.loop();
if (millis() - LastUpdate > 29000){
LastUpdate = millis();
CocoaFlag = true;
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
delay(200);
snprintf(buf,sizeof(buf),"");
snprintf(buf1,sizeof(buf1),"");
snprintf(buf2,sizeof(buf2),"");
if(env3Flag){
sht30.get();
delay(50);
snprintf(topicbuf,sizeof(topicbuf),"%s/tmp",ENVdevName);
snprintf(buf,sizeof(buf),"%2.2f",sht30.cTemp);
mqttPublish(topicbuf,buf);
snprintf(topicbuf,sizeof(topicbuf),"%s/hum",ENVdevName);
snprintf(buf,sizeof(buf),"%2.2f",sht30.humidity);
mqttPublish(topicbuf,buf);
snprintf(buf1,sizeof(buf1),"tmp,F,%2.2f,hum,F,%2.2f,press,F,%.2f",sht30.cTemp,sht30.humidity,qmp6988.calcPressure()/100);
}
if(env2Flag){
sht30.get();
delay(50);
snprintf(topicbuf,sizeof(topicbuf),"%s/tmp",ENVdevName);
snprintf(buf,sizeof(buf),"%2.2f",sht30.cTemp);
mqttPublish(topicbuf,buf);
snprintf(topicbuf,sizeof(topicbuf),"%s/hum",ENVdevName);
snprintf(buf,sizeof(buf),"%2.2f",sht30.humidity);
mqttPublish(topicbuf,buf);
snprintf(buf1,sizeof(buf1),"tmp,F,%2.2f,hum,F,%2.2f,press,F,%.2f",sht30.cTemp,sht30.humidity,bmp.readPressure()/100);
}
if (sgpFlag) {
if (!sgp.IAQmeasure()){
Serial.println("Measurement failed");
return;
}
snprintf(topicbuf,sizeof(topicbuf),"%s/eCO2",ENVdevName);
snprintf(buf,sizeof(buf),"%d",sgp.eCO2);
mqttPublish(topicbuf,buf);
snprintf(buf2,sizeof(buf2),",TVOC,I,%d,eCO2,I,%d",sgp.TVOC,sgp.eCO2);
}
if ((env2Flag||env3Flag)||(sgpFlag && sgp.IAQmeasure())){
snprintf(topicbuf,sizeof(topicbuf),"%s/%s/%s","titc_data3",telNo,"ENV");
snprintf(buf,sizeof(buf),"%s%s",buf1,buf2);
mqttPublish(topicbuf,buf);
}
// snprintf(topicbuf,sizeof(topicbuf),"%s/%s/%s","titc_data3",telNo,"COCOA");
// snprintf(buf,sizeof(buf),"cocoa,I,%d,near,I,%d",deviceNum,nearDeviceNum);
// mqttPublish(topicbuf,buf);
}
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(200);
}
//-----------------------------------------------------------------------
//topicを含むpublisher
//-----------------------------------------------------------------------
void mqttPublish(const char* topicBuffer,const char* buf) {
// while(WiFi.status() != WL_CONNECTED){
// printf(".");
// WifiConnect(3);
// }
// while(!mqttClient.connected()){
// printf("mqtt reconnect.\n");
// mqttClient.connect(ClientID,mqttuser,mqttpasswd);
// delay(200);
// }
mqttClient.publish(topicBuffer,buf);
printf("topic:%s\tmsg:%s\n",topicBuffer,buf);
}