name it DARKNESS, handle non-reading of temperature, small bugfixes

This commit is contained in:
Staars 2019-09-18 20:57:35 +02:00
parent a02c733073
commit eb8539dd91

View File

@ -1,571 +1,535 @@
/*
  xsns_48_chirp.ino - soil moisture sensor support for Sonoff-Tasmota
xsns_48_chirp.ino - soil moisture sensor support for Sonoff-Tasmota
  Copyright (C) 2019  Theo Arends & Christian Baars
Copyright (C) 2019 Theo Arends & Christian Baars
  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 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.
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/>.
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 Date      Action    Description
  --------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
Version Date Action Description
--------------------------------------------------------------------------------------------
  1.0.0.1 20190917  changed   - rework of the inner loop to enable delays in the middle of I2C-reads
                    added     - now really support for the (slower) CHIRP-Sensor
  ---
  1.0.0.0 20190608  started   - further development by Christian Baars  - https://github.com/Staars/Sonoff-Tasmota
                    forked    - from arendst/tasmota                    - https://github.com/arendst/Sonoff-Tasmota
                    base      - code base from arendst and              - https://github.com/Miceuz/i2c-moisture-sensor
1.0.0.1 20190917 changed - rework of the inner loop to enable delays in the middle of I2C-reads
changed - use DEBUG_SENSOR_LOG, change ILLUMINANCE to DARKNESS
changed - do not publish missing temperature reads
added - now really support for the (slower) CHIRP-Sensor
---
1.0.0.0 20190608 started - further development by Christian Baars - https://github.com/Staars/Sonoff-Tasmota
forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota
base - code base from arendst and - https://github.com/Miceuz/i2c-moisture-sensor
*/
// #define USE_CHIRP
#ifdef USE_I2C
#ifdef USE_CHIRP
#define USE_CHIRP
#ifdef USE_I2C
#ifdef USE_CHIRP
/*********************************************************************************************\
 * CHIRP - Soil moisture sensor
 * 
 * I2C Address: 0x20 - standard address, is changeable
* CHIRP - Soil moisture sensor
*
* I2C Address: 0x20 - standard address, is changeable
\*********************************************************************************************/
#define XSNS_48                     48
#define CHIRP_MAX_SENSOR_COUNT      3               // 127 is expectectd to be the max number
#define XSNS_48 48
#define CHIRP_MAX_SENSOR_COUNT 3 // 127 is expectectd to be the max number
#define CHIRP_ADDR_STANDARD         0x20            // standard address
#define CHIRP_ADDR_STANDARD 0x20 // standard address
/*********************************************************************************************\
 * constants
* constants
\*********************************************************************************************/
#define D_CMND_CHIRP "CHIRP"
#define D_CMND_CHIRP "CHIRP"
const char S_JSON_CHIRP_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_CHIRP "%s\":%d}";
const char S_JSON_CHIRP_COMMAND[] PROGMEM        = "{\"" D_CMND_CHIRP "%s\"}";
const char kCHIRP_Commands[] PROGMEM             = "Select|Set|Scan|Reset|Sleep|Wake";
const char S_JSON_CHIRP_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_CHIRP "%s\":%d}";
const char S_JSON_CHIRP_COMMAND[] PROGMEM = "{\"" D_CMND_CHIRP "%s\"}";
const char kCHIRP_Commands[] PROGMEM = "Select|Set|Scan|Reset|Sleep|Wake";
const char kChirpTypes[] PROGMEM = "CHIRP";
const char kChirpTypes[] PROGMEM = "CHIRP";
/*********************************************************************************************\
 * enumerations
* enumerations
\*********************************************************************************************/
enum CHIRP_Commands {                                 // commands useable in console or rules
  CMND_CHIRP_SELECT,                                  // select active sensor by I2C address, makes only sense for multiple sensors
  CMND_CHIRP_SET,                                     // set new I2C address for selected/active sensor, will reset
  CMND_CHIRP_SCAN,                                    // scan the I2C bus for one or more chirp sensors
  CMND_CHIRP_RESET,                                   // CHIRPReset, a fresh and default restart
  CMND_CHIRP_SLEEP,                                   // put sensor to sleep
  CMND_CHIRP_WAKE };                                  // wake sensor by reading firmware version
enum CHIRP_Commands { // commands useable in console or rules
CMND_CHIRP_SELECT, // select active sensor by I2C address, makes only sense for multiple sensors
CMND_CHIRP_SET, // set new I2C address for selected/active sensor, will reset
CMND_CHIRP_SCAN, // scan the I2C bus for one or more chirp sensors
CMND_CHIRP_RESET, // CHIRPReset, a fresh and default restart
CMND_CHIRP_SLEEP, // put sensor to sleep
CMND_CHIRP_WAKE }; // wake sensor by reading firmware version
/*********************************************************************************************\
 * command defines
* command defines
\*********************************************************************************************/
#define CHIRP_GET_CAPACITANCE       0x00            // 16 bit, read
#define CHIRP_SET_ADDRESS           0x01            // 8 bit,  write
#define CHIRP_GET_ADDRESS           0x02            // 8 bit,  read
#define CHIRP_MEASURE_LIGHT         0x03            // no value, write, -> initiate measurement, then wait at least 3 seconds
#define CHIRP_GET_LIGHT             0x04            // 16 bit, read, -> higher value means darker environment, noisy data, not calibrated
#define CHIRP_GET_TEMPERATURE       0x05            // 16 bit, read
#define CHIRP_RESET                 0x06            // no value, write
#define CHIRP_GET_VERSION           0x07            // 8 bit, read, -> 22 means 2.2
#define CHIRP_SLEEP                 0x08            // no value, write
#define CHIRP_GET_BUSY              0x09            // 8 bit, read, -> 1 = busy, 0 = otherwise
#define CHIRP_GET_CAPACITANCE 0x00 // 16 bit, read
#define CHIRP_SET_ADDRESS 0x01 // 8 bit, write
#define CHIRP_GET_ADDRESS 0x02 // 8 bit, read
#define CHIRP_MEASURE_LIGHT 0x03 // no value, write, -> initiate measurement, then wait at least 3 seconds
#define CHIRP_GET_LIGHT 0x04 // 16 bit, read, -> higher value means darker environment, noisy data, not calibrated
#define CHIRP_GET_TEMPERATURE 0x05 // 16 bit, read
#define CHIRP_RESET 0x06 // no value, write
#define CHIRP_GET_VERSION 0x07 // 8 bit, read, -> 22 means 2.2
#define CHIRP_SLEEP 0x08 // no value, write
#define CHIRP_GET_BUSY 0x09 // 8 bit, read, -> 1 = busy, 0 = otherwise
/*********************************************************************************************\
 * helper function
* helper function
\*********************************************************************************************/
void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg) {
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.endTransmission(); 
} // now the original CHIRP needs 1100 ms delay
void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg) {
Wire.beginTransmission(addr);
Wire.write(reg);
Wire.endTransmission();
} // now the original CHIRP needs 1100 ms delay
uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr) {
  Wire.requestFrom(addr,(uint8_t)2);
  uint16_t t = Wire.read() << 8;
  t = t | Wire.read();
  return t;
uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr) {
Wire.requestFrom(addr,(uint8_t)2);
uint16_t t = Wire.read() << 8;
t = t | Wire.read();
return t;
}
/********************************************************************************************/
// globals
// globals
uint8_t    chirp_current        = 0;    // current selected/active sensor
uint8_t    chirp_found_sensors  = 0;    // number of found sensors
uint8_t chirp_current = 0; // current selected/active sensor
uint8_t chirp_found_sensors = 0; // number of found sensors
char       chirp_name[7];
uint8_t    chirp_next_job       = 0;    //0=reset, 1=auto-wake, 2-9 = various measure steps; 10 = TELE done
uint32_t   chirp_timeout_count  = 0;    //is handled every second, so value is equal to seconds (it is a slow sensor)
char chirp_name[7];
uint8_t chirp_next_job = 0; //0=reset, 1=auto-wake, 2-13 = various measure steps; 14 = TELE done
uint32_t chirp_timeout_count = 0; //is handled every second, so value is equal to seconds (it is a slow sensor)
#pragma pack(1)
struct ChirpSensor_t{
    uint16_t   moisture = 0;      // shall hold post-processed data, if implemented
    uint16_t   light = 0;         // light level, maybe already postprocessed depending on the firmware
    int16_t    temperature= 0;    // temperature in degrees CELSIUS * 10
    uint8_t    version = 0;       // firmware-version
    uint8_t    address:7;         // we need only 7bit so...
    uint8_t    explicitSleep:1;   // there is a free bit to play with ;)
#pragma pack(1)
struct ChirpSensor_t{
uint16_t moisture = 0; // shall hold post-processed data, if implemented
uint16_t light = 0; // light level, maybe already postprocessed depending on the firmware
int16_t temperature = 0; // temperature in degrees CELSIUS * 10 , we will also store the I2C error code
uint8_t version = 0; // firmware-version
uint8_t address:7; // we need only 7bit so...
uint8_t explicitSleep:1; // there is a free bit to play with ;)
};
#pragma pack()
#pragma pack()
ChirpSensor_t chirp_sensor[CHIRP_MAX_SENSOR_COUNT];       // should be 8 bytes per sensor slot
ChirpSensor_t chirp_sensor[CHIRP_MAX_SENSOR_COUNT]; // should be 8 bytes per sensor slot
/********************************************************************************************/
void ChirpReset(uint8_t addr) {
    ChirpWriteI2CRegister(addr, CHIRP_RESET);
void ChirpReset(uint8_t addr) {
ChirpWriteI2CRegister(addr, CHIRP_RESET);
}
/********************************************************************************************/
void ChirpResetAll(void) {
    for (uint32_t i = 0; i < chirp_found_sensors; i++) {
      if (chirp_sensor[i].version) { 
        ChirpReset(chirp_sensor[i].address);
        }
    }
void ChirpResetAll(void) {
for (uint32_t i = 0; i < chirp_found_sensors; i++) {
if (chirp_sensor[i].version) {
ChirpReset(chirp_sensor[i].address);
}
}
}
/********************************************************************************************/
void ChirpClockSet() { // set I2C for this slow sensor
    Wire.setClockStretchLimit(4000);
    Wire.setClock(50000);
void ChirpClockSet() { // set I2C for this slow sensor
Wire.setClockStretchLimit(4000);
Wire.setClock(50000);
}
/********************************************************************************************/
void ChirpSleep(uint8_t addr) {
    ChirpWriteI2CRegister(addr, CHIRP_SLEEP);
void ChirpSleep(uint8_t addr) {
ChirpWriteI2CRegister(addr, CHIRP_SLEEP);
}
/********************************************************************************************/
// void ChirpSleepAll(void) {
//     for (uint32_t i = 0; i < chirp_found_sensors; i++) {
//       if (chirp_sensor[i].version) { 
//         ChirpSleep(chirp_sensor[i].address);
//         }
//     }
// }
// void ChirpSleepAll(void) {
// for (uint32_t i = 0; i < chirp_found_sensors; i++) {
// if (chirp_sensor[i].version) {
// ChirpSleep(chirp_sensor[i].address);
// }
// }
// }
// /********************************************************************************************/
// /********************************************************************************************/
// void ChirpAutoWakeAll(void) {
//     for (uint32_t i = 0; i < chirp_found_sensors; i++) {
//       if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { 
//         ChirpReadVersion(chirp_sensor[i].address);
//         }
//     }
// }
// void ChirpAutoWakeAll(void) {
// for (uint32_t i = 0; i < chirp_found_sensors; i++) {
// if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) {
// ChirpReadVersion(chirp_sensor[i].address);
// }
// }
// }
/********************************************************************************************/
void ChirpSelect(uint8_t sensor) {
  if(sensor < chirp_found_sensors) { //TODO: show some infos
    chirp_current = sensor;
    DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u now active."), chirp_current);
  }
  if (sensor == 255) {
    DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u active at address 0x%x."), chirp_current, chirp_sensor[chirp_current].address);
  }
void ChirpSelect(uint8_t sensor) {
if(sensor < chirp_found_sensors) { //TODO: show some infos
chirp_current = sensor;
DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u now active."), chirp_current);
}
if (sensor == 255) {
DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u active at address 0x%x."), chirp_current, chirp_sensor[chirp_current].address);
}
}
/********************************************************************************************/
// bool ChirpMeasureLight(void) {
//   for (uint32_t i = 0; i < chirp_found_sensors; i++) {
//     if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { 
//       uint8_t lightReady = I2cRead8(chirp_sensor[i].address, CHIRP_GET_BUSY);
//       DEBUG_SENSOR_LOG(PSTR("CHIRP: busy status for light for sensor %u"), lightReady);
//       if (lightReady == 1) {
//         return false; // a measurement is still in progress, we stop everything and come back in the next loop = 1 second
//       }
//       DEBUG_SENSOR_LOG(PSTR("CHIRP: init measure light for sensor %u"), i);
//       ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_MEASURE_LIGHT); 
//       }
//   }
//   return true; // we could read all values (maybe at different times, but that does not really matter) and consider this job finished
// }
/********************************************************************************************/
// void ChirpReadCapTemp() { // no timeout needed for both measurements, so we do it at once
//     for (uint32_t i = 0; i < chirp_found_sensors; i++) {
//       if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { 
//         DEBUG_SENSOR_LOG(PSTR("CHIRP: now really read CapTemp for sensor at address 0x%x"), chirp_sensor[i].address);
//         chirp_sensor[i].moisture = I2cRead16(chirp_sensor[i].address, CHIRP_GET_CAPACITANCE);
//         chirp_sensor[i].temperature = I2cRead16(chirp_sensor[i].address, CHIRP_GET_TEMPERATURE); 
//         }
//     }
// }
/********************************************************************************************/
// bool ChirpReadLight() {   // sophisticated calculations could be done here
//   bool success = false;
//   for (uint32_t i = 0; i < chirp_found_sensors; i++) {
//     DEBUG_SENSOR_LOG(PSTR("CHIRP: will read light for sensor %u"), i);
//     if (chirp_sensor[i].version) {
//       if (I2cValidRead16(&chirp_sensor[i].light, chirp_sensor[i].address, CHIRP_GET_LIGHT)){
//         DEBUG_SENSOR_LOG(PSTR("CHIRP: light read success"));
//         success = true;
//       }
//       if(!chirp_sensor[i].explicitSleep){ success = true;} 
//       }
//   }
//   return success;
// }
/********************************************************************************************/
uint8_t ChirpReadVersion(uint8_t addr) {
  return (I2cRead8(addr, CHIRP_GET_VERSION));
uint8_t ChirpReadVersion(uint8_t addr) {
return (I2cRead8(addr, CHIRP_GET_VERSION));
}
/********************************************************************************************/
bool ChirpSet(uint8_t addr) {
  if(addr < 128){
    if (I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr)){
      I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr); // two calls are needed for sensor firmware version 2.6
      DEBUG_SENSOR_LOG(PSTR("CHIRP: Wrote adress %u "), addr);
      ChirpReset(chirp_sensor[chirp_current].address);
      chirp_sensor[chirp_current].address = addr;
      return true;
    }
  }
  return false;
bool ChirpSet(uint8_t addr) {
if(addr < 128){
if (I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr)){
I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr); // two calls are needed for sensor firmware version 2.6
DEBUG_SENSOR_LOG(PSTR("CHIRP: Wrote adress %u "), addr);
ChirpReset(chirp_sensor[chirp_current].address);
chirp_sensor[chirp_current].address = addr;
return true;
}
}
return false;
}
/********************************************************************************************/
bool ChirpScan() {
    ChirpClockSet();
    chirp_found_sensors = 0;
    for (uint8_t address = 1; address <= 127; address++) {
      chirp_sensor[chirp_found_sensors].version = 0;
      chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address);
      delay(2);
      chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address);
      if(chirp_sensor[chirp_found_sensors].version > 0) {
        AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "CHIRP:", address);    
        if(chirp_found_sensors<CHIRP_MAX_SENSOR_COUNT){
          chirp_sensor[chirp_found_sensors].address = address; // push next sensor, as long as there is space in the array
          AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CHIRP: fw %u"), chirp_sensor[chirp_found_sensors].version);
        }
        chirp_found_sensors++;
      }
    }
    AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Found %u CHIRP sensor(s)."), chirp_found_sensors);
    if (chirp_found_sensors == 0) {return false;}
    else {return true;}
bool ChirpScan() {
ChirpClockSet();
chirp_found_sensors = 0;
for (uint8_t address = 1; address <= 127; address++) {
chirp_sensor[chirp_found_sensors].version = 0;
chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address);
delay(2);
chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address);
if(chirp_sensor[chirp_found_sensors].version > 0) {
AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "CHIRP:", address);
if(chirp_found_sensors<CHIRP_MAX_SENSOR_COUNT){
chirp_sensor[chirp_found_sensors].address = address; // push next sensor, as long as there is space in the array
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CHIRP: fw %u"), chirp_sensor[chirp_found_sensors].version);
}
chirp_found_sensors++;
}
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Found %u CHIRP sensor(s)."), chirp_found_sensors);
if (chirp_found_sensors == 0) {return false;}
else {return true;}
}
/********************************************************************************************/
void ChirpDetect(void)
void ChirpDetect(void)
{
  if (chirp_next_job > 0) {
    return;
  }
  DEBUG_SENSOR_LOG(PSTR("CHIRP: scan will start ..."));
  if (ChirpScan()) {
    uint8_t chirp_model = 0;  // TODO: ??
    GetTextIndexed(chirp_name, sizeof(chirp_name), chirp_model, kChirpTypes);
  }
if (chirp_next_job > 0) {
return;
}
DEBUG_SENSOR_LOG(PSTR("CHIRP: scan will start ..."));
if (ChirpScan()) {
uint8_t chirp_model = 0; // TODO: ??
GetTextIndexed(chirp_name, sizeof(chirp_name), chirp_model, kChirpTypes);
}
}
/********************************************************************************************/
void ChirpServiceAllSensors(uint8_t job){
  for (uint32_t i = 0; i < chirp_found_sensors; i++) {
    if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { 
      DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare for sensor at address 0x%x"), chirp_sensor[i].address);
      switch(job){
        case 0:
        ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_CAPACITANCE);
        break;
        case 1:
        chirp_sensor[i].moisture = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address);
        break;
        case 2:
        ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_TEMPERATURE);
        break;
        case 3:
        chirp_sensor[i].temperature = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address);
        break;
        case 4:
        ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_MEASURE_LIGHT);
        break;
        case 5:
        ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_LIGHT);
        break;
        case 6:
        chirp_sensor[i].light = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address);
        break;
        default:
        break;
      }
    }
  }  
void ChirpServiceAllSensors(uint8_t job){
for (uint32_t i = 0; i < chirp_found_sensors; i++) {
if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) {
DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare for sensor at address 0x%x"), chirp_sensor[i].address);
switch(job){
case 0:
ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_CAPACITANCE);
break;
case 1:
chirp_sensor[i].moisture = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address);
break;
case 2:
ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_TEMPERATURE);
break;
case 3:
chirp_sensor[i].temperature = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address);
break;
case 4:
ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_MEASURE_LIGHT);
break;
case 5:
ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_LIGHT);
break;
case 6:
chirp_sensor[i].light = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address);
break;
default:
break;
}
}
}
}
/********************************************************************************************/
void ChirpEvery100MSecond(void)
void ChirpEvery100MSecond(void)
{
  // DEBUG_SENSOR_LOG(PSTR("CHIRP: every second"));
  if(chirp_timeout_count == 0) {    //countdown complete, now do something
      switch(chirp_next_job) {
          case 0:                   //this should only be called after driver initialization
          DEBUG_SENSOR_LOG(PSTR( "CHIRP: reset all"));
          ChirpResetAll();
          chirp_timeout_count = 10; // wait a second
          chirp_next_job++;
          break;
          case 1:                   // auto-sleep-wake seems to expose a fundamental I2C-problem of the sensor and is deactivated
          // DEBUG_SENSOR_LOG(PSTR("CHIRP: auto-wake all"));
          // ChirpAutoWakeAll();       // this is only a wake-up call at the start of next read cycle
          chirp_next_job++;         // go on, next job should start in a second
          break;
          case 2:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read"));
          ChirpServiceAllSensors(0);  
          chirp_timeout_count = 11;  // wait 1.1 seconds, 
          chirp_next_job++;
          break;
          case 3:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read"));
          ChirpServiceAllSensors(1); 
          chirp_next_job++;
          break;
          case 4:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read - 2nd"));
          ChirpServiceAllSensors(0); 
          chirp_timeout_count = 11;  // wait 1.1 seconds, 
          chirp_next_job++;
          break;
          case 5:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read - 2nd"));
          ChirpServiceAllSensors(1); 
          chirp_next_job++;
          break;
          case 6:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read"));
          ChirpServiceAllSensors(2); 
          chirp_timeout_count = 11;  // wait 1.1 seconds, 
          chirp_next_job++;
          break;
          case 7:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read"));
          ChirpServiceAllSensors(3); 
          chirp_next_job++;
          break;
          case 8:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read - 2nd"));
          ChirpServiceAllSensors(2);
          chirp_timeout_count = 11;  // wait 1.1 seconds, 
          chirp_next_job++;
          break;
          case 9:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read - 2nd"));
          ChirpServiceAllSensors(3);
          chirp_next_job++;
          break;       
          case 10:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: start light measure process"));
          ChirpServiceAllSensors(4);
          chirp_timeout_count = 90;  // wait 9 seconds,  
          chirp_next_job++;
          break;
          case 11:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare light read"));
          ChirpServiceAllSensors(5);
          chirp_timeout_count = 11;  // wait 1.1 seconds, 
          chirp_next_job++;
          break;
          case 12:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: finish light read"));
          ChirpServiceAllSensors(6);
          chirp_next_job++;
          break;     
          case 13:
          DEBUG_SENSOR_LOG(PSTR("CHIRP: paused, waiting for TELE"));
          break;
          case 14:
          if (Settings.tele_period > 16){
              chirp_timeout_count = (Settings.tele_period - 17) * 10;  // sync it with the TELEPERIOD, we need about up to 17 seconds to measure
              DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout: %u, tele: %u"), chirp_timeout_count, Settings.tele_period);
            }
          else{
            AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !"));
          }
          chirp_next_job = 1;                                 // back to step 1
          break;
      }
  }
  else {
      chirp_timeout_count--;         // count down
  }
// DEBUG_SENSOR_LOG(PSTR("CHIRP: every second"));
if(chirp_timeout_count == 0) { //countdown complete, now do something
switch(chirp_next_job) {
case 0: //this should only be called after driver initialization
DEBUG_SENSOR_LOG(PSTR("CHIRP: reset all"));
ChirpResetAll();
chirp_timeout_count = 10; // wait a second
chirp_next_job++;
break;
case 1: // auto-sleep-wake seems to expose a fundamental I2C-problem of the sensor and is deactivated
// DEBUG_SENSOR_LOG(PSTR("CHIRP: auto-wake all"));
// ChirpAutoWakeAll(); // this is only a wake-up call at the start of next read cycle
chirp_next_job++; // go on, next job should start in a second
break;
case 2:
DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read"));
ChirpServiceAllSensors(0);
chirp_timeout_count = 11; // wait 1.1 seconds,
chirp_next_job++;
break;
case 3:
DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read"));
ChirpServiceAllSensors(1);
chirp_next_job++;
break;
case 4:
DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read - 2nd"));
ChirpServiceAllSensors(0);
chirp_timeout_count = 11; // wait 1.1 seconds,
chirp_next_job++;
break;
case 5:
DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read - 2nd"));
ChirpServiceAllSensors(1);
chirp_next_job++;
break;
case 6:
DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read"));
ChirpServiceAllSensors(2);
chirp_timeout_count = 11; // wait 1.1 seconds,
chirp_next_job++;
break;
case 7:
DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read"));
ChirpServiceAllSensors(3);
chirp_next_job++;
break;
case 8:
DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read - 2nd"));
ChirpServiceAllSensors(2);
chirp_timeout_count = 11; // wait 1.1 seconds,
chirp_next_job++;
break;
case 9:
DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read - 2nd"));
ChirpServiceAllSensors(3);
chirp_next_job++;
break;
case 10:
DEBUG_SENSOR_LOG(PSTR("CHIRP: start light measure process"));
ChirpServiceAllSensors(4);
chirp_timeout_count = 90; // wait 9 seconds,
chirp_next_job++;
break;
case 11:
DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare light read"));
ChirpServiceAllSensors(5);
chirp_timeout_count = 11; // wait 1.1 seconds,
chirp_next_job++;
break;
case 12:
DEBUG_SENSOR_LOG(PSTR("CHIRP: finish light read"));
ChirpServiceAllSensors(6);
chirp_next_job++;
break;
case 13:
DEBUG_SENSOR_LOG(PSTR("CHIRP: paused, waiting for TELE"));
break;
case 14:
if (Settings.tele_period > 16){
chirp_timeout_count = (Settings.tele_period - 17) * 10; // sync it with the TELEPERIOD, we need about up to 17 seconds to measure
DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout: %u, tele: %u"), chirp_timeout_count, Settings.tele_period);
}
else{
AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !"));
}
chirp_next_job = 1; // back to step 1
break;
}
}
else {
chirp_timeout_count--; // count down
}
}
/********************************************************************************************/
// normaly in i18n.h
// normaly in i18n.h
#define D_JSON_MOISTURE "Moisture"
#define D_JSON_MOISTURE "Moisture"
#define D_JSON_DARKNESS "Darkness"
#ifdef USE_WEBSERVER
  // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#ifdef USE_WEBSERVER
// {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
 const char HTTP_SNS_MOISTURE[] PROGMEM = "{s} " D_JSON_MOISTURE ": {m}%s %{e}";
 const char HTTP_SNS_CHIRPVER[] PROGMEM = "{s} CHIRP-sensor %u at address: {m}0x%x{e}"
                                          "{s} FW-version: {m}%s {e}";                                                                                            ;
 const char HTTP_SNS_CHIRPSLEEP[] PROGMEM = "{s} {m} is sleeping ...{e}";
#endif  // USE_WEBSERVER
const char HTTP_SNS_MOISTURE[] PROGMEM = "{s} " D_JSON_MOISTURE "{m}%s %{e}";
const char HTTP_SNS_DARKNESS[] PROGMEM = "{s} " D_JSON_DARKNESS "{m}%s %{e}";
const char HTTP_SNS_CHIRPVER[] PROGMEM = "{s} CHIRP-sensor %u at address{m}0x%x{e}"
"{s} FW-version{m}%s {e}"; ;
const char HTTP_SNS_CHIRPSLEEP[] PROGMEM = "{s} {m} is sleeping ...{e}";
#endif // USE_WEBSERVER
/********************************************************************************************/
void ChirpShow(bool json)
void ChirpShow(bool json)
{
  for (uint32_t i = 0; i < chirp_found_sensors; i++) {
    if (chirp_sensor[i].version) {
      // convert double values to string
      char str_moisture[33];      
      dtostrfd(chirp_sensor[i].moisture, 0, str_moisture);
      char str_temperature[33];
      double t_temperature = ((double) chirp_sensor[i].temperature )/10.0;   
      dtostrfd(t_temperature, Settings.flag2.temperature_resolution, str_temperature);
      char str_light[33];      
      dtostrfd(chirp_sensor[i].light, 0, str_light);
      char str_version[33];      
      dtostrfd(chirp_sensor[i].version, 0, str_version);
      if (json) {
        if(!chirp_sensor[i].explicitSleep){
          ResponseAppend_P(PSTR(",\"%s%u\":{\"" D_JSON_MOISTURE "\":%s,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_ILLUMINANCE "\":\"%s}"),
          chirp_name, i, str_moisture, str_temperature, str_light);}
        else {
          ResponseAppend_P(PSTR(",\"%s%u\":{\"sleeping\"}"),
          chirp_name, i);
        }
  #ifdef USE_DOMOTICZ
      if (0 == tele_period) {
              DomoticzTempHumSensor(str_temperature, str_moisture);
              DomoticzSensor(DZ_ILLUMINANCE,chirp_sensor[i].light);
        }
  #endif  // USE_DOMOTICZ
  #ifdef USE_WEBSERVER
      } else {
        WSContentSend_PD(HTTP_SNS_CHIRPVER, i, chirp_sensor[i].address, str_version);
        if (chirp_sensor[i].explicitSleep){
          WSContentSend_PD(HTTP_SNS_CHIRPSLEEP);
        }
        else {
          WSContentSend_PD(HTTP_SNS_MOISTURE, str_moisture);
          WSContentSend_PD(HTTP_SNS_ILLUMINANCE, " ", chirp_sensor[i].light);
          WSContentSend_PD(HTTP_SNS_TEMP, " ",str_temperature, TempUnit());   
        }
  
  #endif  // USE_WEBSERVER
      }
    }
  }  
for (uint32_t i = 0; i < chirp_found_sensors; i++) {
if (chirp_sensor[i].version) {
// convert double values to string
char str_moisture[33];
dtostrfd(chirp_sensor[i].moisture, 0, str_moisture);
char str_temperature[33];
double t_temperature = ((double) chirp_sensor[i].temperature )/10.0;
dtostrfd(t_temperature, Settings.flag2.temperature_resolution, str_temperature);
char str_light[33];
dtostrfd(chirp_sensor[i].light, 0, str_light);
char str_version[33];
dtostrfd(chirp_sensor[i].version, 0, str_version);
if (json) {
if(!chirp_sensor[i].explicitSleep) {
ResponseAppend_P(PSTR(",\"%s%u\":{\"" D_JSON_MOISTURE "\":%s"),chirp_name, i, str_moisture);
if(chirp_sensor[i].temperature!=-1){ // this is the error code -> no temperature
ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"),str_temperature);
}
ResponseAppend_P(PSTR(",\"" D_JSON_DARKNESS "\":\"%s}"),str_light);
}
else {
ResponseAppend_P(PSTR(",\"%s%u\":{\"sleeping\"}"),chirp_name, i);
}
#ifdef USE_DOMOTICZ
if (0 == tele_period) {
DomoticzTempHumSensor(str_temperature, str_moisture);
DomoticzSensor(DZ_ILLUMINANCE,chirp_sensor[i].light);
}
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
WSContentSend_PD(HTTP_SNS_CHIRPVER, i, chirp_sensor[i].address, str_version);
if (chirp_sensor[i].explicitSleep){
WSContentSend_PD(HTTP_SNS_CHIRPSLEEP);
}
else {
WSContentSend_PD(HTTP_SNS_MOISTURE, str_moisture);
WSContentSend_PD(HTTP_SNS_DARKNESS, str_light);
if(chirp_sensor[i].temperature!=-1){ // this is the error code -> no temperature
WSContentSend_PD(HTTP_SNS_TEMP, " ",str_temperature, TempUnit());
}
}
#endif // USE_WEBSERVER
}
}
}
}
/*********************************************************************************************\
 * check the Chirp commands
* check the Chirp commands
\*********************************************************************************************/
bool ChirpCmd(void) {
  char command[CMDSZ];
  bool serviced = true;
  uint8_t disp_len = strlen(D_CMND_CHIRP);
bool ChirpCmd(void) {
char command[CMDSZ];
bool serviced = true;
uint8_t disp_len = strlen(D_CMND_CHIRP);
  if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_CHIRP), disp_len)) {  // prefix
    int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kCHIRP_Commands);
if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_CHIRP), disp_len)) { // prefix
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kCHIRP_Commands);
    switch (command_code) {
      case CMND_CHIRP_SELECT:
      case CMND_CHIRP_SET:
        if (XdrvMailbox.data_len > 0) {
          if (command_code == CMND_CHIRP_SELECT)  { ChirpSelect(XdrvMailbox.payload); }                       //select active sensor, i.e. for wake, sleep or reset
          if (command_code == CMND_CHIRP_SET)     { ChirpSet((uint8_t)XdrvMailbox.payload); }                 //set and change I2C-address of selected sensor
        Response_P(S_JSON_CHIRP_COMMAND_NVALUE, command, XdrvMailbox.payload);
        }
        else {
          if (command_code == CMND_CHIRP_SELECT)  { ChirpSelect(255); }                                       //show active sensor
        Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload);
        }     
        break;
      case CMND_CHIRP_SCAN: 
      case CMND_CHIRP_SLEEP:
      case CMND_CHIRP_WAKE:
      case CMND_CHIRP_RESET:
        if (command_code == CMND_CHIRP_SCAN)     {  chirp_next_job = 0;
                                                    ChirpDetect(); }                                            // this will re-init the sensor array
        if (command_code == CMND_CHIRP_SLEEP)    {  chirp_sensor[chirp_current].explicitSleep = true;         // we do not touch this sensor in the read functions
                                                    ChirpSleep(chirp_sensor[chirp_current].address); }
        if (command_code == CMND_CHIRP_WAKE)     {  chirp_sensor[chirp_current].explicitSleep = false;        // back in action
                                                    ChirpReadVersion(chirp_sensor[chirp_current].address); }  // just use read version as wakeup call                                         
        if (command_code == CMND_CHIRP_RESET)    { ChirpReset(chirp_sensor[chirp_current].address); }
        Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload);
        break;
      default:
        // else for Unknown command
        serviced = false;
      break;
    }
  }
  return serviced;
switch (command_code) {
case CMND_CHIRP_SELECT:
case CMND_CHIRP_SET:
if (XdrvMailbox.data_len > 0) {
if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(XdrvMailbox.payload); } //select active sensor, i.e. for wake, sleep or reset
if (command_code == CMND_CHIRP_SET) { ChirpSet((uint8_t)XdrvMailbox.payload); } //set and change I2C-address of selected sensor
Response_P(S_JSON_CHIRP_COMMAND_NVALUE, command, XdrvMailbox.payload);
}
else {
if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(255); } //show active sensor
Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload);
}
break;
case CMND_CHIRP_SCAN:
case CMND_CHIRP_SLEEP:
case CMND_CHIRP_WAKE:
case CMND_CHIRP_RESET:
if (command_code == CMND_CHIRP_SCAN) { chirp_next_job = 0;
ChirpDetect(); } // this will re-init the sensor array
if (command_code == CMND_CHIRP_SLEEP) { chirp_sensor[chirp_current].explicitSleep = true; // we do not touch this sensor in the read functions
ChirpSleep(chirp_sensor[chirp_current].address); }
if (command_code == CMND_CHIRP_WAKE) { chirp_sensor[chirp_current].explicitSleep = false; // back in action
ChirpReadVersion(chirp_sensor[chirp_current].address); } // just use read version as wakeup call
if (command_code == CMND_CHIRP_RESET) { ChirpReset(chirp_sensor[chirp_current].address); }
Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload);
break;
default:
// else for Unknown command
serviced = false;
break;
}
}
return serviced;
}
/*********************************************************************************************\
 * Interface
* Interface
\*********************************************************************************************/
bool Xsns48(uint8_t function)
bool Xsns48(uint8_t function)
{
  bool result = false;
bool result = false;
  if (i2c_flg) {  
    switch (function) {
      case FUNC_INIT:
        ChirpDetect();         // We can call CHIRPSCAN later to re-detect
        break;
      case FUNC_EVERY_100_MSECOND:
        if(chirp_found_sensors > 0){
          ChirpEvery100MSecond();
        }    
        break;
      case FUNC_COMMAND:
        result = ChirpCmd();  
        break;
      case FUNC_JSON_APPEND:
        ChirpShow(1);
        chirp_next_job = 14; // TELE done, now compute time for next measure cycle
        break;
#ifdef USE_WEBSERVER
      case FUNC_WEB_SENSOR:
        ChirpShow(0);
        break;
#endif  // USE_WEBSERVER
    }
  }
  return result;
if (i2c_flg) {
switch (function) {
case FUNC_INIT:
ChirpDetect(); // We can call CHIRPSCAN later to re-detect
break;
case FUNC_EVERY_100_MSECOND:
if(chirp_found_sensors > 0){
ChirpEvery100MSecond();
}
break;
case FUNC_COMMAND:
result = ChirpCmd();
break;
case FUNC_JSON_APPEND:
ChirpShow(1);
chirp_next_job = 14; // TELE done, now compute time for next measure cycle
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
ChirpShow(0);
break;
#endif // USE_WEBSERVER
}
}
return result;
}
#endif  // USE_CHIRP
#endif  // USE_I2C
#endif // USE_CHIRP
#endif // USE_I2C