mirror of https://github.com/arendst/Tasmota.git
850 lines
36 KiB
C++
850 lines
36 KiB
C++
/*
|
|
xsns_62_MI_HM10.ino - MI-BLE-sensors via HM-10 support for Tasmota
|
|
|
|
Copyright (C) 2020 Christian Baars and Theo Arends
|
|
|
|
This program 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.
|
|
|
|
This program 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
--------------------------------------------------------------------------------------------
|
|
Version yyyymmdd Action Description
|
|
--------------------------------------------------------------------------------------------
|
|
|
|
0.9.1.0 20200209 added - LYWSD02-support, including setting the time
|
|
---
|
|
0.9.0.0 20200130 started - initial development by Christian Baars (support LYWSD03 only)
|
|
forked - from arendst/tasmota - https://github.com/arendst/Tasmota
|
|
|
|
*/
|
|
#ifdef USE_HM10
|
|
|
|
#define XSNS_62 62
|
|
|
|
#include <TasmotaSerial.h>
|
|
#include <vector>
|
|
|
|
TasmotaSerial *HM10Serial;
|
|
#define HM10_BAUDRATE 115200 // default with FW>700 is 115200
|
|
|
|
#define HM10_MAX_TASK_NUMBER 12
|
|
uint8_t HM10_TASK_LIST[HM10_MAX_TASK_NUMBER+1][2]; // first value: kind of task - second value: delay in x * 100ms
|
|
|
|
#define HM10_MAX_RX_BUF 512
|
|
char HM10_RX_STRING[HM10_MAX_RX_BUF] = {0};
|
|
|
|
struct {
|
|
uint8_t current_task_delay; // number of 100ms-cycles
|
|
uint8_t last_command;
|
|
uint16_t firmware;
|
|
uint32_t period; // set manually in addition to TELE-period, is set to TELE-period after start
|
|
uint32_t serialSpeed;
|
|
union {
|
|
uint32_t time;
|
|
uint8_t timebuf[4];
|
|
};
|
|
struct {
|
|
uint32_t init:1;
|
|
uint32_t pending_task:1;
|
|
uint32_t connected:1;
|
|
uint32_t subscribed:1;
|
|
uint32_t awaitingHT:1;
|
|
uint32_t awaitingB:1;
|
|
// TODO: more to come
|
|
} mode;
|
|
struct {
|
|
uint8_t sensor; // points to to the number 0...255
|
|
// TODO: more to come
|
|
} state;
|
|
} HM10;
|
|
|
|
#pragma pack(1)
|
|
struct {
|
|
uint16_t temp;
|
|
uint8_t hum;
|
|
} LYWSD0x_HT;
|
|
#pragma pack(0)
|
|
|
|
struct mi_sensor_t{
|
|
uint8_t type; //Flora = 1; MI-HT_V1=2; LYWSD02=3; LYWSD03=4
|
|
uint8_t serial[6];
|
|
uint8_t showedUp;
|
|
float temp; //Flora, MJ_HT_V1, LYWSD0x
|
|
union {
|
|
struct {
|
|
float moisture;
|
|
float fertility;
|
|
uint16_t lux;
|
|
}; // Flora
|
|
struct {
|
|
float hum;
|
|
uint8_t bat;
|
|
}; // MJ_HT_V1, LYWSD0x
|
|
};
|
|
};
|
|
|
|
std::vector<mi_sensor_t> MIBLEsensors;
|
|
|
|
/*********************************************************************************************\
|
|
* constants
|
|
\*********************************************************************************************/
|
|
|
|
#define D_CMND_HM10 "HM10"
|
|
|
|
const char S_JSON_HM10_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s\":%d}";
|
|
const char S_JSON_HM10_COMMAND[] PROGMEM = "{\"" D_CMND_HM10 "%s%s\"}";
|
|
const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time";
|
|
|
|
#define FLORA 1
|
|
#define MJ_HT_V1 2
|
|
#define LYWSD02 3
|
|
#define LYWSD03MMC 4
|
|
|
|
uint8_t kHM10SlaveID[4][3] = { 0xC4,0x7C,0x8D, // Flora
|
|
0x58,0x2D,0x34, // MJ_HT_V1
|
|
0xE7,0x2E,0x00, // LYWSD02
|
|
0xA4,0xC1,0x38, // LYWSD03
|
|
};
|
|
|
|
const char kHM10SlaveType1[] PROGMEM = "Flora";
|
|
const char kHM10SlaveType2[] PROGMEM = "MJ_HT_V1";
|
|
const char kHM10SlaveType3[] PROGMEM = "LYWSD02";
|
|
const char kHM10SlaveType4[] PROGMEM = "LYWSD03";
|
|
const char * kHM10SlaveType[] PROGMEM = {kHM10SlaveType1,kHM10SlaveType2,kHM10SlaveType3,kHM10SlaveType4};
|
|
|
|
/*********************************************************************************************\
|
|
* enumerations
|
|
\*********************************************************************************************/
|
|
|
|
enum HM10_Commands { // commands useable in console or rules
|
|
CMND_HM10_DISC_SCAN, // re-scan for sensors
|
|
CMND_HM10_AT, // send AT-command for debugging and special configuration
|
|
CMND_HM10_PERIOD, // set period like TELE-period in seconds between read-cycles
|
|
CMND_HM10_BAUD, // serial speed of ESP8266 (<-> HM10), does not change baud rate of HM10
|
|
CMND_HM10_TIME // set LYWSD02-Time from ESP8266-time
|
|
};
|
|
|
|
/*********************************************************************************************\
|
|
* Task codes defines
|
|
\*********************************************************************************************/
|
|
|
|
#define TASK_HM10_NOTASK 0 // nothing to be done
|
|
#define TASK_HM10_ROLE1 1 // change role to 1
|
|
#define TASK_HM10_IMME1 2 // change imme to 1
|
|
#define TASK_HM10_RENEW 3 // device factory setting
|
|
#define TASK_HM10_RESET 4 // device reset
|
|
#define TASK_HM10_DISC 5 // device discovery scan
|
|
#define TASK_HM10_CONN 6 // connect to given MAC
|
|
#define TASK_HM10_VERSION 7 // query FW version
|
|
#define TASK_HM10_NAME 8 // query device name
|
|
#define TASK_HM10_FEEDBACK 9 // get device response
|
|
#define TASK_HM10_DISCONN 10 // disconnect
|
|
#define TASK_HM10_SUB_L3 11 // subscribe to service handle 37
|
|
#define TASK_HM10_READ_HT 12 // read from handle 36 -> Hum & Temp
|
|
#define TASK_HM10_FINDALLCHARS 13 // read all available characteristics
|
|
#define TASK_HM10_UN_L3 14 // subscribe service handle 37
|
|
#define TASK_HM10_DELAY_SUB 15 // start reading from subscription delayed
|
|
#define TASK_HM10_READ_BT_L3 16 // read from handle 3A -> Battery
|
|
#define TASK_HM10_SUB_L2 17 // subscribe to service handle 3C
|
|
#define TASK_HM10_UN_L2 18 // subscribe service handle 3C
|
|
#define TASK_HM10_READ_BT_L2 19 // read from handle 43 -> Battery
|
|
#define TASK_HM10_TIME_L2 20 // set time of LYWSD02 to system time
|
|
|
|
#define TASK_HM10_DONE 99 // used, if there was a task in the slot or just to wait
|
|
|
|
/*********************************************************************************************\
|
|
* Helper functions
|
|
\*********************************************************************************************/
|
|
|
|
void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay){
|
|
HM10_TASK_LIST[slot][0] = task;
|
|
HM10_TASK_LIST[slot][1] = delay;
|
|
HM10_TASK_LIST[slot+1][0] = TASK_HM10_NOTASK; // the tasks must always be launched in ascending order!!
|
|
HM10.current_task_delay = HM10_TASK_LIST[0][1];
|
|
}
|
|
|
|
void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot){
|
|
HM10.last_command = HM10_TASK_LIST[slot][0]; // save command
|
|
HM10_TASK_LIST[slot][0] = task;
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* chained tasks
|
|
\*********************************************************************************************/
|
|
|
|
void HM10_Reset(void) { HM10_Launchtask(TASK_HM10_DISCONN,0,1); // disconnect
|
|
HM10_Launchtask(TASK_HM10_ROLE1,1,1); // set role to 1
|
|
HM10_Launchtask(TASK_HM10_IMME1,2,1); // set imme to 1
|
|
HM10_Launchtask(TASK_HM10_RESET,3,1); // reset Device
|
|
HM10_Launchtask(TASK_HM10_VERSION,4,10); // read SW Version
|
|
HM10_Launchtask(TASK_HM10_DISC,5,50); // discovery
|
|
}
|
|
|
|
void HM10_Discovery_Scan(void) {
|
|
HM10_Launchtask(TASK_HM10_DISCONN,0,1); // disconnect
|
|
HM10_Launchtask(TASK_HM10_DISC,1,1); // discovery
|
|
}
|
|
|
|
void HM10_Read_LYWSD03(void) {
|
|
HM10_Launchtask(TASK_HM10_CONN,0,1); // connect
|
|
HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); // get OK+CONN
|
|
HM10_Launchtask(TASK_HM10_SUB_L3,2,20); // subscribe
|
|
HM10_Launchtask(TASK_HM10_UN_L3,3,80); // unsubscribe
|
|
HM10_Launchtask(TASK_HM10_READ_BT_L3,4,5); // read Battery
|
|
HM10_Launchtask(TASK_HM10_DISCONN,5,5); // disconnect
|
|
}
|
|
|
|
void HM10_Read_LYWSD02(void) {
|
|
HM10_Launchtask(TASK_HM10_CONN,0,1); // connect
|
|
HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); // get OK+CONN
|
|
HM10_Launchtask(TASK_HM10_SUB_L2,2,20); // subscribe
|
|
HM10_Launchtask(TASK_HM10_UN_L2,3,80); // unsubscribe
|
|
HM10_Launchtask(TASK_HM10_READ_BT_L2,4,5); // read Battery
|
|
HM10_Launchtask(TASK_HM10_DISCONN,5,5); // disconnect
|
|
}
|
|
|
|
void HM10_Time_LYWSD02(void) {
|
|
HM10_Launchtask(TASK_HM10_DISCONN,0,0); // disconnect
|
|
HM10_Launchtask(TASK_HM10_CONN,1,5); // connect
|
|
HM10_Launchtask(TASK_HM10_FEEDBACK,2,35); // get OK+CONN
|
|
HM10_Launchtask(TASK_HM10_TIME_L2,3,20); // subscribe
|
|
HM10_Launchtask(TASK_HM10_DISCONN,4,5); // disconnect
|
|
}
|
|
|
|
/**
|
|
* @brief Return the slot number of a known sensor or return create new sensor slot
|
|
*
|
|
* @param _serial BLE address of the sensor
|
|
* @param _type Type number of the sensor, 0xff for Auto-type
|
|
* @return uint32_t Known or new slot in the sensors-vector
|
|
*/
|
|
uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint8_t _type){
|
|
if(_type==0xff){
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE: will test MAC-type"));
|
|
for (uint32_t i=0;i<4;i++){
|
|
if(memcmp(_serial,kHM10SlaveID+i,3)==0){
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE: MAC is type %u"), i);
|
|
_type = i+1;
|
|
}
|
|
else {
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE: MAC-type is unknown"));
|
|
}
|
|
}
|
|
}
|
|
if(_type==0xff) return _type; // error
|
|
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE: vector size %u"), MIBLEsensors.size());
|
|
for(uint32_t i=0; i<MIBLEsensors.size(); i++){
|
|
if(memcmp(_serial,MIBLEsensors.at(i).serial,sizeof(_serial))==0){
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE: known sensor at slot: %u"), i);
|
|
if(MIBLEsensors.at(i).showedUp < 3){ // if we got an intact packet, the sensor should show up several times
|
|
MIBLEsensors.at(i).showedUp++; // count up to the above number ... now we are pretty sure
|
|
}
|
|
return i;
|
|
}
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE i: %x %x %x %x %x %x"), MIBLEsensors.at(i).serial[0], MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]);
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE n: %x %x %x %x %x %x"), _serial[0], _serial[1], _serial[2],_serial[3],_serial[4],_serial[5]);
|
|
}
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE: found new sensor"));
|
|
mi_sensor_t _newSensor;
|
|
memcpy(_newSensor.serial,_serial, sizeof(_serial));
|
|
_newSensor.type = _type;
|
|
_newSensor.showedUp = 1; // does not matter for HM-10
|
|
_newSensor.temp =-1000.0f;
|
|
switch (_type)
|
|
{
|
|
case 1:
|
|
_newSensor.moisture =-1000.0f;
|
|
_newSensor.fertility =-1000.0f;
|
|
_newSensor.lux = 0xffff;
|
|
break;
|
|
case 2: case 3: case 4:
|
|
_newSensor.hum=-1.0f;
|
|
_newSensor.bat=0xff;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
MIBLEsensors.push_back(_newSensor);
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE: new sensor at slot: %u"), MIBLEsensors.size()-1);
|
|
return MIBLEsensors.size()-1;
|
|
};
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* init serial
|
|
* define serial rx/tx port fixed with 115200 baud
|
|
\*********************************************************************************************/
|
|
|
|
void HM10SerialInit(void) {
|
|
HM10.mode.init = false;
|
|
HM10.serialSpeed = HM10_BAUDRATE;
|
|
HM10Serial = new TasmotaSerial(pin[GPIO_HM10_RX], pin[GPIO_HM10_TX], 1, 0, HM10_MAX_RX_BUF);
|
|
if (HM10Serial->begin(HM10.serialSpeed)) {
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10);
|
|
if (HM10Serial->hardwareSerial()) {
|
|
ClaimSerial();
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: claim HW"));
|
|
}
|
|
HM10_Reset();
|
|
HM10.mode.pending_task = 1;
|
|
HM10.mode.init = 1;
|
|
HM10.period = Settings.tele_period;
|
|
DEBUG_SENSOR_LOG(PSTR("%s_TASK_LIST initialized, now return to main loop"),D_CMND_HM10);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief convert Mac-String to byte array
|
|
*
|
|
* @param string Hex-string, must contain 12 chars (no error checking)
|
|
* @param _mac Must be a uint8_t[6], filled with zeros
|
|
*/
|
|
|
|
void HM10MACStringToBytes(const char* string, uint8_t _mac[]) {
|
|
uint32_t index = 0;
|
|
while (index < 12) {
|
|
char c = string[index];
|
|
uint32_t value = 0;
|
|
if(c >= '0' && c <= '9')
|
|
value = (c - '0');
|
|
else if (c >= 'A' && c <= 'F')
|
|
value = (10 + (c - 'A'));
|
|
_mac[(index/2)] += value << (((index + 1) % 2) * 4);
|
|
// DEBUG_SENSOR_LOG(PSTR("HM10: Char: %c, Value: %x, Index/2: %u, valueadded: %x, MAC-index: %x"), c, value,(index/2),value << (((index + 1) % 2) * 4), _mac[index/2]);
|
|
index++;
|
|
}
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: MAC-array: %x%x%x%x%x%x"),_mac[0],_mac[1],_mac[2],_mac[3],_mac[4],_mac[5]);
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* parse the response
|
|
\*********************************************************************************************/
|
|
|
|
void HM10ParseResponse(char *buf) {
|
|
if (!strncmp(buf,"OK",2)) {
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: got OK"));
|
|
}
|
|
if (!strncmp(buf,"HMSoft",6)) { //8
|
|
const char* _fw = "000";
|
|
memcpy((void *)_fw,(void *)(buf+8),3);
|
|
HM10.firmware = atoi(_fw);
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: Firmware: %d"), HM10.firmware);
|
|
return;
|
|
}
|
|
char * _pos = strstr(buf, "IS0:");
|
|
if(_pos) {
|
|
const char* _mac = "000000000000";
|
|
memcpy((void *)_mac,(void *)(_pos+4),12);
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: found Mac: %s"), _mac);
|
|
uint8_t _newMacArray[6] = {0};
|
|
HM10MACStringToBytes(_mac, _newMacArray);
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: MAC-array: %x%x%x%x%x%x"),_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]);
|
|
MIBLEgetSensorSlot(_newMacArray, 0xff);
|
|
}
|
|
if (strstr(buf, "LOST")){
|
|
HM10.mode.connected = false;
|
|
}
|
|
else {
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: empty response"));
|
|
}
|
|
}
|
|
|
|
void HM10readTempHum(char *_buf){
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: raw data: %x%x%x%x%x%x%x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]);
|
|
if(_buf[0] != 0 && _buf[1] != 0){
|
|
memcpy(&LYWSD0x_HT,(void *)_buf,3);
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: Temperature * 100: %u, Humidity: %u"),LYWSD0x_HT.temp,LYWSD0x_HT.hum);
|
|
uint32_t _slot = HM10.state.sensor;
|
|
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot);
|
|
static float _tempFloat;
|
|
_tempFloat=(float)(LYWSD0x_HT.temp)/100.0f;
|
|
if(_tempFloat<60){
|
|
MIBLEsensors.at(_slot).temp=_tempFloat;
|
|
HM10.mode.awaitingHT = false;
|
|
HM10.current_task_delay = 0;
|
|
}
|
|
_tempFloat=(float)LYWSD0x_HT.hum;
|
|
if(_tempFloat<100){
|
|
MIBLEsensors.at(_slot).hum = _tempFloat;
|
|
DEBUG_SENSOR_LOG(PSTR("LYWSD03: hum updated"));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool HM10readBat(char *_buf){
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: raw data: %x%x%x%x%x%x%x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]);
|
|
if(_buf[0] != 0){
|
|
DEBUG_SENSOR_LOG(PSTR("HM10: Battery: %u"),_buf[0]);
|
|
uint32_t _slot = HM10.state.sensor;
|
|
DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot);
|
|
if(_buf[0]<101){
|
|
MIBLEsensors.at(_slot).bat=_buf[0];
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* handle the return value from the HM10
|
|
\*********************************************************************************************/
|
|
|
|
bool HM10SerialHandleFeedback(){ // every 50 milliseconds
|
|
bool success = false;
|
|
uint32_t i = 0;
|
|
char ret[HM10_MAX_RX_BUF] = {0}; // reset array with zeros
|
|
|
|
|
|
while(HM10Serial->available()) {
|
|
// delay(0);
|
|
if(i<HM10_MAX_RX_BUF){
|
|
ret[i] = HM10Serial->read();
|
|
}
|
|
i++;
|
|
success = true;
|
|
}
|
|
if(HM10.mode.awaitingHT) {
|
|
if (HM10.mode.connected) HM10readTempHum(ret);
|
|
}
|
|
else if(HM10.mode.awaitingB) {
|
|
if (HM10.mode.connected) {
|
|
if (HM10readBat(ret)){
|
|
HM10.mode.awaitingB = false;
|
|
HM10.current_task_delay = 0;
|
|
}
|
|
}
|
|
}
|
|
else if(success) {
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s response: %s"),D_CMND_HM10, (char *)ret);
|
|
HM10ParseResponse(ret);
|
|
}
|
|
else {
|
|
// DEBUG_SENSOR_LOG(PSTR("%s got no response"),D_CMND_HM10);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* execute the next Task
|
|
\*********************************************************************************************/
|
|
|
|
void HM10_TaskEvery100ms(){
|
|
if (HM10.current_task_delay == 0) {
|
|
uint8_t i = 0;
|
|
bool runningTaskLoop = true;
|
|
while (runningTaskLoop) { // always iterate through the whole task list
|
|
switch(HM10_TASK_LIST[i][0]) { // handle the kind of task
|
|
case TASK_HM10_ROLE1:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set role to 1"),D_CMND_HM10);
|
|
HM10.current_task_delay = 5; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+ROLE1");
|
|
break;
|
|
case TASK_HM10_IMME1:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set imme to 1"),D_CMND_HM10);
|
|
HM10.current_task_delay = 5; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+IMME1");
|
|
break;
|
|
case TASK_HM10_DISC:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start discovery"),D_CMND_HM10);
|
|
HM10.current_task_delay = 35; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+DISC?");
|
|
break;
|
|
case TASK_HM10_VERSION:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read version"),D_CMND_HM10);
|
|
HM10.current_task_delay = 5; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+VERR?");
|
|
break;
|
|
case TASK_HM10_NAME:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read name"),D_CMND_HM10);
|
|
HM10.current_task_delay = 5; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+NAME?");
|
|
break;
|
|
case TASK_HM10_CONN:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s connect"),D_CMND_HM10);
|
|
HM10.current_task_delay = 2; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
char _con[20];
|
|
sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors.at(HM10.state.sensor).serial[0],MIBLEsensors.at(HM10.state.sensor).serial[1],MIBLEsensors.at(HM10.state.sensor).serial[2],MIBLEsensors.at(HM10.state.sensor).serial[3],MIBLEsensors.at(HM10.state.sensor).serial[4],MIBLEsensors.at(HM10.state.sensor).serial[5]);
|
|
HM10Serial->write(_con);
|
|
HM10.mode.awaitingB = false;
|
|
HM10.mode.connected = true;
|
|
break;
|
|
case TASK_HM10_DISCONN:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s disconnect"),D_CMND_HM10);
|
|
HM10.current_task_delay = 5; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT");
|
|
break;
|
|
case TASK_HM10_RESET:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s Reset Device"),D_CMND_HM10);
|
|
HM10Serial->write("AT+RESET");
|
|
HM10.current_task_delay = 5; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
break;
|
|
case TASK_HM10_SUB_L3:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s subscribe"),D_CMND_HM10);
|
|
HM10.current_task_delay = 25; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+NOTIFY_ON0037");
|
|
break;
|
|
case TASK_HM10_UN_L3:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s un-subscribe"),D_CMND_HM10);
|
|
HM10.current_task_delay = 5; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10.mode.awaitingHT = false;
|
|
HM10Serial->write("AT+NOTIFYOFF0037");
|
|
break;
|
|
case TASK_HM10_SUB_L2:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s subscribe"),D_CMND_HM10);
|
|
HM10.current_task_delay = 25; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+NOTIFY_ON003C");
|
|
break;
|
|
case TASK_HM10_UN_L2:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s un-subscribe"),D_CMND_HM10);
|
|
HM10.current_task_delay = 5; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10.mode.awaitingHT = false;
|
|
HM10Serial->write("AT+NOTIFYOFF003C");
|
|
break;
|
|
case TASK_HM10_TIME_L2:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set time"),D_CMND_HM10);
|
|
HM10.current_task_delay = 5; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10.time = Rtc.utc_time;
|
|
HM10Serial->write("AT+SEND_DATAWR002F");
|
|
HM10Serial->write(HM10.timebuf,4);
|
|
HM10Serial->write(Rtc.time_timezone / 60);
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60));
|
|
break;
|
|
case TASK_HM10_READ_HT:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 0036"),D_CMND_HM10);
|
|
HM10.current_task_delay = 0; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+READDATA0036?");
|
|
HM10.mode.awaitingHT = true;
|
|
break;
|
|
case TASK_HM10_READ_BT_L3:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 003A"),D_CMND_HM10);
|
|
HM10.current_task_delay = 2; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+READDATA003A?");
|
|
HM10.mode.awaitingB = true;
|
|
break;
|
|
case TASK_HM10_READ_BT_L2:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 0043"),D_CMND_HM10);
|
|
HM10.current_task_delay = 2; // set task delay
|
|
HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
runningTaskLoop = false;
|
|
HM10Serial->write("AT+READDATA0043?");
|
|
HM10.mode.awaitingB = true;
|
|
break;
|
|
// case TASK_HM10_FINDALLCHARS:
|
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s find all chars"),D_CMND_HM10);
|
|
// HM10.current_task_delay = 5; // set task delay
|
|
// HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i);
|
|
// runningTaskLoop = false;
|
|
// HM10Serial->write("AT+FINDALLCHARS?");
|
|
// break;
|
|
case TASK_HM10_FEEDBACK:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s get response"),D_CMND_HM10);
|
|
HM10SerialHandleFeedback();
|
|
HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; // set task delay
|
|
HM10_TASK_LIST[i][0] = TASK_HM10_DONE; // no feedback for reset
|
|
runningTaskLoop = false;
|
|
break;
|
|
case TASK_HM10_DELAY_SUB:
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start reading"),D_CMND_HM10);
|
|
HM10SerialHandleFeedback();
|
|
HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; // set task delay
|
|
HM10_TASK_LIST[i][0] = TASK_HM10_DONE; // no feedback for reset
|
|
HM10.mode.awaitingHT = true;
|
|
runningTaskLoop = false;
|
|
break;
|
|
case TASK_HM10_DONE: // this entry was already handled
|
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%sFound done HM10_TASK"),D_CMND_HM10);
|
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%snext slot:%u, i: %u"),D_CMND_HM10, HM10_TASK_LIST[i+1][0],i);
|
|
if(HM10_TASK_LIST[i+1][0] == TASK_HM10_NOTASK) { // check the next entry and if there is none
|
|
DEBUG_SENSOR_LOG(PSTR("%sno Tasks left"),D_CMND_HM10);
|
|
DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK_DONE current slot %u"),D_CMND_HM10, i);
|
|
for (uint8_t j = 0; j < HM10_MAX_TASK_NUMBER+1; j++) { // do a clean-up:
|
|
DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK cleanup slot %u"),D_CMND_HM10, j);
|
|
HM10_TASK_LIST[j][0] = TASK_HM10_NOTASK; // reset all task entries
|
|
HM10_TASK_LIST[j][1] = 0; // reset all delays
|
|
}
|
|
runningTaskLoop = false; // return to main loop
|
|
HM10.mode.pending_task = 0; // back to main loop control
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
else {
|
|
HM10.current_task_delay--; // count down every 100 ms
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Main loop of the driver, "high level"-loop
|
|
*
|
|
*/
|
|
|
|
void HM10EverySecond(){
|
|
if(HM10.firmware == 0) return;
|
|
if(HM10.mode.pending_task == 1) return;
|
|
if (MIBLEsensors.size()==0) return;
|
|
|
|
static uint32_t _counter = 0;
|
|
static uint32_t _nextSensorSlot = 0;
|
|
if(_counter==0) {
|
|
HM10.state.sensor = _nextSensorSlot;
|
|
_nextSensorSlot++;
|
|
if(MIBLEsensors.at(HM10.state.sensor).type==LYWSD03MMC) {
|
|
HM10.mode.pending_task = 1;
|
|
HM10_Read_LYWSD03();
|
|
}
|
|
if(MIBLEsensors.at(HM10.state.sensor).type==LYWSD02) {
|
|
HM10.mode.pending_task = 1;
|
|
HM10_Read_LYWSD02();
|
|
}
|
|
if (HM10.state.sensor==MIBLEsensors.size()-1) {
|
|
_nextSensorSlot= 0;
|
|
_counter++;
|
|
}
|
|
DEBUG_SENSOR_LOG(PSTR("%s active sensor now: %u"),D_CMND_HM10, HM10.state.sensor);
|
|
}
|
|
else _counter++;
|
|
if (_counter>HM10.period) _counter = 0;
|
|
}
|
|
|
|
bool HM10Cmd(void) {
|
|
char command[CMDSZ];
|
|
bool serviced = true;
|
|
uint8_t disp_len = strlen(D_CMND_HM10);
|
|
|
|
if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_HM10), disp_len)) { // prefix
|
|
uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kHM10_Commands);
|
|
switch (command_code) {
|
|
case CMND_HM10_PERIOD:
|
|
if (XdrvMailbox.data_len > 0) {
|
|
if (command_code == CMND_HM10_PERIOD) { HM10.period = XdrvMailbox.payload; }
|
|
}
|
|
else {
|
|
if (command_code == CMND_HM10_PERIOD) XdrvMailbox.payload = HM10.period;
|
|
}
|
|
Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload);
|
|
break;
|
|
case CMND_HM10_BAUD:
|
|
if (XdrvMailbox.data_len > 0) {
|
|
if (command_code == CMND_HM10_BAUD) {
|
|
HM10.serialSpeed = XdrvMailbox.payload;
|
|
HM10Serial->begin(HM10.serialSpeed);
|
|
}
|
|
}
|
|
else {
|
|
if (command_code == CMND_HM10_BAUD) XdrvMailbox.payload = HM10.serialSpeed;
|
|
}
|
|
Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload);
|
|
break;
|
|
case CMND_HM10_TIME:
|
|
if (XdrvMailbox.data_len > 0) {
|
|
if(MIBLEsensors.size()>XdrvMailbox.payload){
|
|
if(MIBLEsensors.at(XdrvMailbox.payload).type == LYWSD02){
|
|
HM10.state.sensor = XdrvMailbox.payload;
|
|
HM10_Time_LYWSD02();
|
|
}
|
|
}
|
|
}
|
|
Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload);
|
|
break;
|
|
case CMND_HM10_AT:
|
|
HM10Serial->write("AT"); // without an argument this will disconnect
|
|
if (strlen(XdrvMailbox.data)!=0) {
|
|
HM10Serial->write("+");
|
|
HM10Serial->write(XdrvMailbox.data); // pass everything without checks
|
|
Response_P(S_JSON_HM10_COMMAND, ":AT+",XdrvMailbox.data);
|
|
}
|
|
else Response_P(S_JSON_HM10_COMMAND, ":AT",XdrvMailbox.data);
|
|
break;
|
|
case CMND_HM10_DISC_SCAN:
|
|
if (command_code == CMND_HM10_DISC_SCAN) { HM10_Discovery_Scan(); }
|
|
Response_P(S_JSON_HM10_COMMAND, command, "");
|
|
break;
|
|
default:
|
|
// else for Unknown command
|
|
serviced = false;
|
|
break;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
return serviced;
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* Presentation
|
|
\*********************************************************************************************/
|
|
|
|
const char HTTP_HM10[] PROGMEM = "{s}HM10" " Firmware " "{m}%u{e}";
|
|
const char HTTP_HM10_SERIAL[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}";
|
|
const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}";
|
|
const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%sus/cm{e}";
|
|
const char HTTP_HM10_HL[] PROGMEM = "{s}<hr>{m}<hr>{e}";
|
|
|
|
void HM10Show(bool json)
|
|
{
|
|
if (json) {
|
|
for (uint32_t i = 0; i < MIBLEsensors.size(); i++) {
|
|
char slave[33];
|
|
sprintf_P(slave,"%s-%02x%02x%02x",kHM10SlaveType[MIBLEsensors.at(i).type-1],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]);
|
|
char temperature[33]; // all sensors have temperature
|
|
dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature);
|
|
|
|
ResponseAppend_P(PSTR(",\"%s\":{"),slave);
|
|
if(MIBLEsensors.at(i).temp!=-1000.0f){ // this is the error code -> no temperature
|
|
ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature);
|
|
}
|
|
if (MIBLEsensors.at(i).type==FLORA){
|
|
char lux[33];
|
|
char moisture[33];
|
|
char fertility[33];
|
|
dtostrfd((float)MIBLEsensors.at(i).lux, 0, lux);
|
|
dtostrfd(MIBLEsensors.at(i).moisture, 0, moisture);
|
|
dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility);
|
|
if(MIBLEsensors.at(i).lux!=0xffff){ // this is the error code -> no temperature
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%s"), lux);
|
|
}
|
|
if(MIBLEsensors.at(i).moisture!=-1000.0f){ // this is the error code -> no moisture
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), moisture);
|
|
}
|
|
if(MIBLEsensors.at(i).fertility!=-1000.0f){ // this is the error code -> no fertility
|
|
ResponseAppend_P(PSTR(",\"Fertility\":%s"), fertility);
|
|
}
|
|
}
|
|
if (MIBLEsensors.at(i).type>FLORA){
|
|
char humidity[33];
|
|
dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity);
|
|
if(MIBLEsensors.at(i).hum!=-1.0f){ // this is the error code -> no humidity
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity);
|
|
}
|
|
if(MIBLEsensors.at(i).bat!=0xff){ // this is the error code -> no battery
|
|
ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors.at(i).bat);
|
|
}
|
|
}
|
|
ResponseAppend_P(PSTR("}"));
|
|
}
|
|
#ifdef USE_WEBSERVER
|
|
} else {
|
|
WSContentSend_PD(HTTP_HM10, HM10.firmware);
|
|
for (uint32_t i = 0; i < MIBLEsensors.size(); i++) {
|
|
WSContentSend_PD(HTTP_HM10_HL);
|
|
WSContentSend_PD(HTTP_HM10_SERIAL, kHM10SlaveType[MIBLEsensors.at(i).type-1], D_MAC_ADDRESS, MIBLEsensors.at(i).serial[0], MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]);
|
|
if(MIBLEsensors.at(i).temp!=-1000.0f){
|
|
char temperature[33];
|
|
dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature);
|
|
WSContentSend_PD(HTTP_SNS_TEMP, kHM10SlaveType[MIBLEsensors.at(i).type-1], temperature, TempUnit());
|
|
}
|
|
if (MIBLEsensors.at(i).type==FLORA){
|
|
if(MIBLEsensors.at(i).lux!=0xffff){ // this is the error code -> no valid value
|
|
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).lux);
|
|
}
|
|
if(MIBLEsensors.at(i).moisture!=-1000.0f){ // this is the error code -> no valid value
|
|
WSContentSend_PD(HTTP_SNS_MOISTURE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).moisture);
|
|
}
|
|
if(MIBLEsensors.at(i).fertility!=-1000.0f){ // this is the error code -> no valid value
|
|
char fertility[33];
|
|
dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility);
|
|
WSContentSend_PD(HTTP_HM10_FLORA_DATA, kHM10SlaveType[MIBLEsensors.at(i).type-1], fertility);
|
|
}
|
|
}
|
|
if (MIBLEsensors.at(i).type>FLORA){ // everything "above" Flora
|
|
if(MIBLEsensors.at(i).hum!=-1.0f){ // this is the error code -> no humidity
|
|
char humidity[33];
|
|
dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity);
|
|
WSContentSend_PD(HTTP_SNS_HUM, kHM10SlaveType[MIBLEsensors.at(i).type-1], humidity);
|
|
}
|
|
if(MIBLEsensors.at(i).bat!=0xff){
|
|
WSContentSend_PD(HTTP_BATTERY, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).bat);
|
|
}
|
|
}
|
|
}
|
|
#endif // USE_WEBSERVER
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* Interface
|
|
\*********************************************************************************************/
|
|
|
|
bool Xsns62(uint8_t function)
|
|
{
|
|
bool result = false;
|
|
|
|
if ((pin[GPIO_HM10_RX] < 99) && (pin[GPIO_HM10_TX] < 99)) {
|
|
switch (function) {
|
|
case FUNC_INIT:
|
|
HM10SerialInit(); // init and start communication
|
|
break;
|
|
case FUNC_EVERY_50_MSECOND:
|
|
HM10SerialHandleFeedback(); // check for device feedback very often
|
|
break;
|
|
case FUNC_EVERY_100_MSECOND:
|
|
if (HM10_TASK_LIST[0][0] != TASK_HM10_NOTASK) {
|
|
HM10_TaskEvery100ms(); // something has to be done, we'll check in the next step
|
|
}
|
|
break;
|
|
case FUNC_EVERY_SECOND:
|
|
HM10EverySecond();
|
|
break;
|
|
case FUNC_COMMAND:
|
|
result = HM10Cmd();
|
|
break;
|
|
case FUNC_JSON_APPEND:
|
|
HM10Show(1);
|
|
break;
|
|
#ifdef USE_WEBSERVER
|
|
case FUNC_WEB_SENSOR:
|
|
HM10Show(0);
|
|
break;
|
|
#endif // USE_WEBSERVER
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
#endif //USE_HM10
|