LoginSignup
1
0

More than 1 year has passed since last update.

M5AtomLiteでSwitchBot温湿度計のデータをMQTTに投げるプログラム

Posted at

新たに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);
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0