mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-23 02:36:35 +00:00
Oops
This commit is contained in:
parent
ebeb4ea7ff
commit
59148b8924
@ -1,407 +0,0 @@
|
||||
/*
|
||||
xdrv_98_product_test.ino - Product test for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2021 Theo Arends and Adrian Scillato
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
//#define PRODUCT_TEST // Enable this to include Tuya product test protocol
|
||||
#define PRODUCT_TEST_DEBUG // Enable to compile status messages as shown below
|
||||
|
||||
#define PRODUCT_TEST_SSID "FACILITY_SSID" // <-- CHANGE THIS TO YOURS
|
||||
|
||||
#define PRODUCT_VOLTAGE "220.0" // Volt
|
||||
#define PRODUCT_CURRENT "392.0" // milliAmpere
|
||||
#define PRODUCT_POWER "87.0" // Watt
|
||||
#define PRODUCT_CALIBRATE_CYCLE 20 // Seconds - Need at least 8 seconds
|
||||
|
||||
#define PRODUCT_SCAN_INTERVAL 8 // Seconds
|
||||
#define PRODUCT_SCAN_RETRY 4 // Scan retry count
|
||||
|
||||
#define PRODUCT_TEST_VERSION "3.2"
|
||||
|
||||
/*********************************************************************************************\
|
||||
This code is supported starting with Tasmota version 9.2.0.
|
||||
The product test will run at power on when both SSids are empty.
|
||||
The test can be restarted by power cycling off then on.
|
||||
|
||||
*** Flash firmware
|
||||
Write binary firmware.bin
|
||||
esptool.py --port COM4 write_flash -fs 1MB -fm dout 0x0 firmware.bin
|
||||
|
||||
*** Typical succesful product test with succesful wifi scan
|
||||
Initial Power ON
|
||||
00:00:00 CFG: Use defaults
|
||||
00:00:00 TST: Product test v3.0
|
||||
00:00:00 TST: 1/6 Product test started using ssid tuya_mdev_test1
|
||||
00:00:00 SRC: Restart
|
||||
00:00:00 Project sonoff Sonoff Version 6.4.1.19(basic)-2_4_2
|
||||
00:00:00 CFG: Saved to flash at FA, Count 2, Bytes 3584
|
||||
00:00:00 WIF: WifiManager active for 3 minutes
|
||||
00:00:00 WIF: Wifimanager set AccessPoint
|
||||
00:00:00 HTP: Web server active on sonoff-2927 with IP address 192.168.4.1
|
||||
00:00:01 CFG: Saved to flash at F9, Count 3, Bytes 3584
|
||||
00:00:04 TST: Scanned SSId1 indebuurt1
|
||||
00:00:04 TST: Scanned SSId2 indebuurt2
|
||||
00:00:04 TST: Scanned SSId3 indebuurt3
|
||||
00:00:04 TST: Scanned SSId4 tuya_mdev_test1
|
||||
00:00:04 TST: 2/6 Wifi found
|
||||
00:00:13 APP: Boot Count 1
|
||||
Button pressed
|
||||
00:00:15 TST: 3/6 Button pressed first time - Toggle relay and led once
|
||||
00:00:15 APP: Button1 immediate
|
||||
00:00:15 SRC: Button
|
||||
00:00:15 RSL: stat/sonoff/RESULT = {"POWER":"ON"}
|
||||
00:00:15 RSL: stat/sonoff/POWER = ON
|
||||
Button pressed again
|
||||
00:00:20 TST: 4/6 Button pressed second time - Toggle relay and led three times
|
||||
00:00:21 RSL: stat/sonoff/RESULT = {"POWER":"OFF"}
|
||||
00:00:21 RSL: stat/sonoff/POWER = OFF
|
||||
00:00:22 RSL: stat/sonoff/RESULT = {"POWER":"ON"}
|
||||
00:00:22 RSL: stat/sonoff/POWER = ON
|
||||
00:00:23 RSL: stat/sonoff/RESULT = {"POWER":"OFF"}
|
||||
00:00:23 RSL: stat/sonoff/POWER = OFF
|
||||
00:00:24 TST: 5/6 Button test finished - Start calibration
|
||||
00:00:27 CFG: Saved to flash at FB, Count 4, Bytes 3584
|
||||
00:00:27 TST: 6/6 Product test finished - Save to power off
|
||||
Device can and should be powered off now as it is in an unknown state
|
||||
|
||||
*** Restore Product test prerequisite
|
||||
Power off device
|
||||
\*********************************************************************************************/
|
||||
|
||||
#ifdef PRODUCT_TEST
|
||||
/*********************************************************************************************\
|
||||
* Product test procedure support
|
||||
*
|
||||
* - Initiate test if power on and no user SSIDs configured
|
||||
* - Init test environment (Led OFF, flash save off)
|
||||
* - Wait for wifi scan to find PRODUCT_TEST_SSID then start flashing led every 250 mSec
|
||||
* - Wait for user press button then turn relay and led ON
|
||||
* - Wait for user press button again then toggle relay and led 3 times and increment bootcount
|
||||
* - If energy monitoring is detected calibrate Voltage, Current and Power
|
||||
* - Wait for power down
|
||||
\*********************************************************************************************/
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Examples to test a particular device:
|
||||
*
|
||||
* Set in user_config_override.h the following:
|
||||
*
|
||||
* #undef MODULE
|
||||
* #define MODULE SONOFF_BASIC // [Module] Select default model (the list is kModuleNiceList() in file tasmota_template.h)
|
||||
* #undef FALLBACK_MODULE
|
||||
* #define FALLBACK_MODULE SONOFF_BASIC // to Select the default model as FALLBACK when the user does a RESET 1
|
||||
*
|
||||
* or
|
||||
*
|
||||
* #define USER_TEMPLATE "{\"NAME\":\"Shelly 2.5\",\"GPIO\":[320,0,32,0,224,193,0,0,640,192,608,225,3456,4736],\"FLAG\":0,\"BASE\":18}" // [Template] Set JSON template
|
||||
*
|
||||
* For ESP32 examples check user_config.h
|
||||
*
|
||||
\*********************************************************************************************/
|
||||
|
||||
|
||||
#define XDRV_98 98
|
||||
|
||||
enum ProductTestStates { PROD_TEST_IDLE, PROD_TEST_START, PROD_TEST_WIFI, PROD_TEST_BUTTON, PROD_TEST_CALIBRATE, PROD_TEST_DONE };
|
||||
|
||||
struct PRODUCTTEST {
|
||||
uint8_t state = PROD_TEST_IDLE; // No product test
|
||||
uint8_t blink = 3; // Toggle relay three times
|
||||
uint8_t button_step = 0; // Either initial power toggle or end of test
|
||||
uint8_t last_button = 0; // Store initial power toggle last button state
|
||||
uint8_t scan_interval = 2; // Seconds after power on
|
||||
uint8_t scan_retry = PRODUCT_SCAN_RETRY +1;
|
||||
uint8_t calibrate_state = PRODUCT_CALIBRATE_CYCLE; // Calibrate cycle max seconds
|
||||
uint8_t calibrate_timer = 0; // 100mS timer
|
||||
|
||||
uint16_t save_data_counter;
|
||||
uint8_t ledstate;
|
||||
uint8_t button_single;
|
||||
uint8_t global_state;
|
||||
} prodtest;
|
||||
|
||||
bool ProductTestTrigger(void)
|
||||
{
|
||||
// AddLog(LOG_LEVEL_INFO, PSTR("TST: resetInfo %d, sta_ssid1 (%s), sta_ssid2 (%s), save_flag %d"), resetInfo.reason, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), Settings.save_flag);
|
||||
|
||||
if (((resetInfo.reason == REASON_DEFAULT_RST) || (resetInfo.reason == REASON_EXT_SYS_RST)) && // Power on
|
||||
!strlen(SettingsText(SET_STASSID1)) && // No configured SSID1
|
||||
!strlen(SettingsText(SET_STASSID2)) // No configured SSID2
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProductTestSsidFound(void)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (prodtest.scan_interval) {
|
||||
prodtest.scan_interval--;
|
||||
if (!prodtest.scan_interval) {
|
||||
prodtest.scan_retry--;
|
||||
if (prodtest.scan_retry) {
|
||||
prodtest.scan_interval = PRODUCT_SCAN_INTERVAL;
|
||||
int n = WiFi.scanNetworks();
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("TST: Scanned SSId%d %s"), i +1, WiFi.SSID(i).c_str());
|
||||
#endif
|
||||
if (!strcasecmp_P(WiFi.SSID(i).c_str(), PSTR(PRODUCT_TEST_SSID))) { result = true; }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Wifi.config_type) {
|
||||
TasmotaGlobal.blinks = 255; // Re-enable wifi config blinks
|
||||
}
|
||||
Settings.ledstate = prodtest.ledstate;
|
||||
Settings.flag.global_state = prodtest.global_state;
|
||||
TasmotaGlobal.save_data_counter = prodtest.save_data_counter;
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("TST: 6/6 Product test failed"));
|
||||
#endif
|
||||
prodtest.state = PROD_TEST_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ProductTestLedOff(void)
|
||||
{
|
||||
TasmotaGlobal.blinks = 0; // No blinks
|
||||
SetLedLink(0);
|
||||
SetLedPower(0);
|
||||
}
|
||||
|
||||
void ProductTestInit(void)
|
||||
{
|
||||
if (ProductTestTrigger()) {
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
TasmotaGlobal.seriallog_level = 3; // Enable more serial logging
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("TST: Product test v" PRODUCT_TEST_VERSION));
|
||||
#endif
|
||||
// Prepare for product test procedure
|
||||
prodtest.save_data_counter = TasmotaGlobal.save_data_counter;
|
||||
TasmotaGlobal.save_data_counter = 32000; // Stop auto saving data - Updating Settings
|
||||
prodtest.global_state = Settings.flag.global_state;
|
||||
Settings.flag.global_state = 1; // Disable global state (wifi and mqtt) led blinks as it interferes with the test protocol
|
||||
prodtest.ledstate = Settings.ledstate;
|
||||
Settings.ledstate = 0; // Disable led power state as it interferes with the test protocol
|
||||
|
||||
ProductTestLedOff(); // Signal start of test
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("TST: 1/6 Product test started using ssid " PRODUCT_TEST_SSID));
|
||||
#endif // PRODUCT_TEST_DEBUG
|
||||
prodtest.state = PROD_TEST_START;
|
||||
} else {
|
||||
prodtest.state = PROD_TEST_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProductTestButton(void)
|
||||
{
|
||||
bool result = false; // Ignore
|
||||
|
||||
if (prodtest.state > PROD_TEST_IDLE) {
|
||||
result = true; // Disable button processing
|
||||
|
||||
if (PROD_TEST_WIFI == prodtest.state) {
|
||||
if ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == prodtest.last_button)) { // Button pressed
|
||||
if (0 == prodtest.button_step) {
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("TST: 3/6 Button pressed first time - Toggle relay and led once"));
|
||||
#endif
|
||||
ProductTestLedOff(); // Signal button pressed and activate relay
|
||||
Settings.ledstate = 1; // Enable led power state
|
||||
prodtest.button_step++;
|
||||
result = false; // Process normal button press which will turn relay and led on
|
||||
} else {
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("TST: 4/6 Button pressed second time - Toggle relay and led three times"));
|
||||
#endif
|
||||
prodtest.state = PROD_TEST_BUTTON;
|
||||
result = true; // Disable further button processing
|
||||
}
|
||||
}
|
||||
prodtest.last_button = XdrvMailbox.payload;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ProductTestEverySecond(void)
|
||||
{
|
||||
if (PROD_TEST_IDLE == prodtest.state) {
|
||||
return;
|
||||
}
|
||||
else if (PROD_TEST_START == prodtest.state) {
|
||||
TasmotaGlobal.blinks = 0; // Disable blinks initiated by WifiManager
|
||||
if (ProductTestSsidFound()) {
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("TST: 2/6 Wifi found"));
|
||||
#endif
|
||||
TasmotaGlobal.sleep = 0; // Disable sleep
|
||||
TasmotaGlobal.restart_flag = 0; // No restart and enable blinks
|
||||
TasmotaGlobal.ota_state_flag = 0; // No OTA and enable blinks
|
||||
|
||||
prodtest.button_single = Settings.flag.button_single;
|
||||
Settings.flag.button_single = 1; // Allow only single press
|
||||
|
||||
TasmotaGlobal.blinks = 255; // Signal wifi connection with blinks
|
||||
prodtest.state = PROD_TEST_WIFI;
|
||||
}
|
||||
}
|
||||
else if (PROD_TEST_BUTTON == prodtest.state) {
|
||||
if (prodtest.blink) {
|
||||
prodtest.blink--;
|
||||
ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE); // Toggle relay and led
|
||||
} else {
|
||||
if (TasmotaGlobal.energy_driver != ENERGY_NONE) {
|
||||
prodtest.state = PROD_TEST_CALIBRATE;
|
||||
} else {
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("TST: 5/6 Button test finished"));
|
||||
#endif
|
||||
prodtest.state = PROD_TEST_DONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PROD_TEST_CALIBRATE == prodtest.state) {
|
||||
if (prodtest.calibrate_state) {
|
||||
prodtest.calibrate_state--;
|
||||
if (PRODUCT_CALIBRATE_CYCLE -1 == prodtest.calibrate_state) {
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("TST: 5/6 Start calibration"));
|
||||
#endif
|
||||
ExecuteCommandPower(1, POWER_ON, SRC_IGNORE); // On relay and led - start energy measurment
|
||||
}
|
||||
else if ((prodtest.calibrate_state < PRODUCT_CALIBRATE_CYCLE -4) && (prodtest.calibrate_state > 1)) {
|
||||
// Allow a few seconds before starting calibration
|
||||
uint8_t all_done = 0;
|
||||
if (HLW_PREF_PULSE == Settings.energy_power_calibration) {
|
||||
char set_value[] = PRODUCT_POWER;
|
||||
XdrvMailbox.data = set_value;
|
||||
XdrvMailbox.data_len = sizeof(set_value);
|
||||
Energy.command_code = CMND_POWERSET;
|
||||
HlwCommand(); // Get calibrated setting
|
||||
} else {
|
||||
all_done++;
|
||||
}
|
||||
if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) {
|
||||
char set_value[] = PRODUCT_VOLTAGE;
|
||||
XdrvMailbox.data = set_value;
|
||||
XdrvMailbox.data_len = sizeof(set_value);
|
||||
Energy.command_code = CMND_VOLTAGESET;
|
||||
HlwCommand(); // Get calibrated setting
|
||||
} else {
|
||||
all_done++;
|
||||
}
|
||||
if (HLW_IREF_PULSE == Settings.energy_current_calibration) {
|
||||
char set_value[] = PRODUCT_CURRENT;
|
||||
XdrvMailbox.data = set_value;
|
||||
XdrvMailbox.data_len = sizeof(set_value);
|
||||
Energy.command_code = CMND_CURRENTSET;
|
||||
HlwCommand(); // Get calibrated setting
|
||||
} else {
|
||||
all_done++;
|
||||
}
|
||||
if (3 == all_done) {
|
||||
prodtest.calibrate_state = 2;
|
||||
}
|
||||
}
|
||||
else if (1 == prodtest.calibrate_state) {
|
||||
ExecuteCommandPower(1, POWER_OFF, SRC_IGNORE); // Off relay and led - stop energy measurment
|
||||
}
|
||||
else if (0 == prodtest.calibrate_state) {
|
||||
// Save calibration data
|
||||
uint32_t power_calibration = Settings.energy_power_calibration;
|
||||
uint32_t voltage_calibration = Settings.energy_voltage_calibration;
|
||||
uint32_t current_calibration = Settings.energy_current_calibration;
|
||||
SettingsDefaultSet1(); // Reset any Settings change
|
||||
SettingsDefaultSet2(); // Init default settings
|
||||
// Store calibration data
|
||||
Settings.energy_power_calibration = power_calibration;
|
||||
Settings.energy_voltage_calibration = voltage_calibration;
|
||||
Settings.energy_current_calibration = current_calibration;
|
||||
SettingsSave(2);
|
||||
TasmotaGlobal.save_data_counter = 32000; // Stop auto saving data - Updating Settings
|
||||
}
|
||||
} else {
|
||||
prodtest.state = PROD_TEST_DONE;
|
||||
SetLedLink(1); // Wifi led on - Succesful calibration
|
||||
}
|
||||
}
|
||||
else if (PROD_TEST_DONE == prodtest.state) {
|
||||
#ifdef PRODUCT_TEST_DEBUG
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("TST: 6/6 Product test finished - Save to power off"));
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
OsWatchLoop(); // Feed OsWatch timer to prevent restart
|
||||
delay(50); // Satisfy SDK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProductTestEvery100mSecond(void)
|
||||
{
|
||||
if (PROD_TEST_CALIBRATE == prodtest.state) {
|
||||
// Blink wifi led 2 flashes every 3 seconds - Calibrating
|
||||
prodtest.calibrate_timer++;
|
||||
if (1 == prodtest.calibrate_timer) {
|
||||
SetLedLink(0); // Wifi led off
|
||||
}
|
||||
else if (12 == prodtest.calibrate_timer) {
|
||||
SetLedLink(1); // Wifi led on
|
||||
}
|
||||
else if (16 == prodtest.calibrate_timer) {
|
||||
prodtest.calibrate_timer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xdrv98(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_EVERY_100_MSECOND:
|
||||
ProductTestEvery100mSecond();
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
ProductTestEverySecond();
|
||||
break;
|
||||
case FUNC_BUTTON_PRESSED:
|
||||
result = ProductTestButton();
|
||||
break;
|
||||
case FUNC_PRE_INIT:
|
||||
ProductTestInit();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // PRODUCT_TEST
|
@ -1,357 +0,0 @@
|
||||
/*
|
||||
xsns_31_ccs811_v2.ino - CCS811 gas and air quality sensor support for Tasmota
|
||||
|
||||
Copyright (C) 2021 Gerhard Mutz 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/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_I2C
|
||||
#ifdef USE_CCS811_V2_TEST
|
||||
/*********************************************************************************************\
|
||||
* CCS811 - Gas (TVOC - Total Volatile Organic Compounds) and Air Quality (CO2)
|
||||
*
|
||||
* Source: Adafruit
|
||||
*
|
||||
* This driver supports one to two devices at a time at
|
||||
* addressses 0x5A or/and 0x5B
|
||||
* - for I2C address 0x5A, connect ADDR to GND
|
||||
* - for I2C address 0x5B, connect ADDR to VCC
|
||||
* NOTE:
|
||||
* - Wake must be connected to GND (no sleep mode supported!)
|
||||
* - depending on the breakout bouard, SDA & SCL may require
|
||||
* pull-ups to VCC, e.g. 4k7R
|
||||
*
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_31 31
|
||||
#define XI2C_24 24 // See I2CDEVICES.md
|
||||
|
||||
#define EVERYNSECONDS 5
|
||||
#define RESETCOUNT 6
|
||||
|
||||
#include "Adafruit_CCS811.h"
|
||||
|
||||
uint8_t CCS811_addresses[] = { CCS811_ADDRESS, (CCS811_ADDRESS + 1) };
|
||||
#define MAXDEVICECOUNT (sizeof( CCS811_addresses) / sizeof(uint8_t))
|
||||
|
||||
typedef struct {
|
||||
uint8_t address;
|
||||
uint8_t device_found;
|
||||
uint8_t device_index;
|
||||
uint8_t device_ready;
|
||||
Adafruit_CCS811 ccsinstance;
|
||||
uint16_t eCO2;
|
||||
uint16_t TVOC;
|
||||
uint8_t refresh_count;
|
||||
uint8_t reset_count;
|
||||
} CCS811DATA;
|
||||
|
||||
uint8_t CCS811_devices_found = 0;
|
||||
CCS811DATA ccsd[ MAXDEVICECOUNT];
|
||||
CCS811DATA * pccsd;
|
||||
uint32_t i;
|
||||
|
||||
|
||||
#define D_CMND_CCS811 "CCS811"
|
||||
|
||||
const char S_JSON_CCS811_COMMAND_XVALUE[] PROGMEM = "{\"" D_CMND_CCS811 "%s\":0x%02x}";
|
||||
const char S_JSON_CCS811_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_CCS811 "%s\":%u}";
|
||||
const char S_JSON_CCS811_COMMAND_HWVERSION[] PROGMEM = "{\"" D_CMND_CCS811 "%s\":\"%x\"}";
|
||||
const char S_JSON_CCS811_COMMAND_FWAPPVERSION[] PROGMEM = "{\"" D_CMND_CCS811 "%s\":\"%x.%x.%x\"}";
|
||||
|
||||
const char S_JSON_CCS811_COMMAND[] PROGMEM = "{\"" D_CMND_CCS811 "%s\"}";
|
||||
const char kCCS811_Commands[] PROGMEM = "HW|FWApp|Baseline";
|
||||
|
||||
enum CCS811_Commands { // commands useable in console or rules
|
||||
CMND_CCS811_HW,
|
||||
CMND_CCS811_FWAPP,
|
||||
CMND_CCS811_BASELINE
|
||||
};
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void CCS811Detect(void)
|
||||
{
|
||||
if (!CCS811_devices_found) {
|
||||
memset( ccsd, 0, sizeof( ccsd));
|
||||
}
|
||||
int active_index = 1;
|
||||
for (i = 0, pccsd = ccsd; i < MAXDEVICECOUNT; i++, pccsd++) {
|
||||
pccsd->address = CCS811_addresses[ i];
|
||||
if (I2cActive( pccsd->address)) { continue; }
|
||||
if (!pccsd->ccsinstance.begin(pccsd->address)) {
|
||||
pccsd->device_found = 1;
|
||||
CCS811_devices_found += 1;
|
||||
I2cSetActiveFound( pccsd->address, "CCS811");
|
||||
pccsd->device_index = active_index;
|
||||
active_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCS811Update(void) // Perform every n second
|
||||
{
|
||||
for (i = 0, pccsd = ccsd; i < MAXDEVICECOUNT; i++, pccsd++) {
|
||||
|
||||
if (!pccsd->device_found)
|
||||
continue;
|
||||
|
||||
pccsd->refresh_count++;
|
||||
if (pccsd->refresh_count >= EVERYNSECONDS) {
|
||||
pccsd->refresh_count = 0;
|
||||
pccsd->device_ready = 0;
|
||||
if (pccsd->ccsinstance.available()) {
|
||||
if (!pccsd->ccsinstance.readData()){
|
||||
pccsd->TVOC = pccsd->ccsinstance.getTVOC();
|
||||
pccsd->eCO2 = pccsd->ccsinstance.geteCO2();
|
||||
pccsd->device_ready = 1;
|
||||
if ((TasmotaGlobal.global_update) &&
|
||||
(TasmotaGlobal.humidity > 0) &&
|
||||
(!isnan(TasmotaGlobal.temperature_celsius))) {
|
||||
pccsd->ccsinstance.setEnvironmentalData((uint8_t)TasmotaGlobal.humidity,
|
||||
TasmotaGlobal.temperature_celsius);
|
||||
}
|
||||
pccsd->reset_count = 0;
|
||||
}
|
||||
} else {
|
||||
// failed, count up
|
||||
pccsd->reset_count++;
|
||||
if (pccsd->reset_count > RESETCOUNT) {
|
||||
// after 30 seconds, restart
|
||||
pccsd->ccsinstance.begin( pccsd->address);
|
||||
pccsd->reset_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// no methods available in Adafruit library to read version data or
|
||||
// read/set the baseline value, so we need to emulate the private methods
|
||||
|
||||
void CCS811ReadMailboxValue( uint8_t address, uint8_t mailbox, byte * pbuf, uint8_t buflen)
|
||||
{
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(mailbox);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom(address, buflen);
|
||||
for (uint8_t i = 0; i < buflen; i++) {
|
||||
*(pbuf + i) = Wire.read();
|
||||
#ifdef CCS811_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR( D_LOG_DEBUG D_CMND_CCS811 " reading byte %u: 0%02x / %u"), i, *(pbuf + i), *(pbuf + i));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CCS811WriteMailboxValue(uint8_t address, uint8_t mailbox, byte * pbuf, uint8_t buflen)
|
||||
{
|
||||
#ifdef CCS811_DEBUG
|
||||
for (uint8_t i = 0; i < buflen; i++) {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR( D_LOG_DEBUG D_CMND_CCS811 " writing byte %u: 0%02x / %u"), i, *(pbuf + i), *(pbuf + i));
|
||||
}
|
||||
#endif
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write((uint8_t)mailbox);
|
||||
Wire.write(pbuf, buflen);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Command Sensor31
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool CCS811CommandSensor()
|
||||
{
|
||||
|
||||
char command[CMDSZ];
|
||||
bool serviced = true;
|
||||
uint8_t prefix_len = strlen(D_CMND_CCS811);
|
||||
|
||||
if ((!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_CCS811), prefix_len)) &&
|
||||
(XdrvMailbox.index <= CCS811_devices_found)) { // check prefix and device index
|
||||
|
||||
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + prefix_len, kCCS811_Commands);
|
||||
|
||||
// select device data matching the index
|
||||
CCS811DATA * pccsd_command;
|
||||
for (i = 0, pccsd = ccsd; i < MAXDEVICECOUNT; i++, pccsd++) {
|
||||
if (pccsd->device_index == XdrvMailbox.index) {
|
||||
pccsd_command = pccsd;
|
||||
#ifdef CCS811_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR( D_LOG_DEBUG D_CMND_CCS811 " I2C Address: 0%02x"), pccsd_command->address);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pccsd_command) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (command_code) {
|
||||
|
||||
case CMND_CCS811_HW:
|
||||
{
|
||||
byte CCS811_hw_version;
|
||||
CCS811ReadMailboxValue( pccsd_command->address,
|
||||
CCS811_HW_VERSION,
|
||||
&CCS811_hw_version,
|
||||
sizeof(CCS811_hw_version));
|
||||
Response_P(S_JSON_CCS811_COMMAND_HWVERSION, command, CCS811_hw_version);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMND_CCS811_FWAPP:
|
||||
{
|
||||
byte CCS811_fw_app_version[2];
|
||||
CCS811ReadMailboxValue( pccsd_command->address,
|
||||
CCS811_FW_APP_VERSION,
|
||||
CCS811_fw_app_version,
|
||||
(sizeof(CCS811_fw_app_version) / sizeof(byte)));
|
||||
Response_P(S_JSON_CCS811_COMMAND_FWAPPVERSION,
|
||||
command,
|
||||
(CCS811_fw_app_version[0] >> 4), // major
|
||||
(CCS811_fw_app_version[0] & 0xF), // minor
|
||||
CCS811_fw_app_version[1]); // build
|
||||
}
|
||||
break;
|
||||
|
||||
case CMND_CCS811_BASELINE:
|
||||
{
|
||||
byte CCS811_baseline[2];
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
CCS811_baseline[0] = (XdrvMailbox.payload & 0xFF00) >> 8;
|
||||
CCS811_baseline[1] = XdrvMailbox.payload & 0xFF;
|
||||
CCS811WriteMailboxValue( pccsd_command->address,
|
||||
CCS811_BASELINE,
|
||||
CCS811_baseline,
|
||||
(sizeof(CCS811_baseline) / sizeof(byte)));
|
||||
} else {
|
||||
CCS811ReadMailboxValue( pccsd_command->address,
|
||||
CCS811_BASELINE,
|
||||
CCS811_baseline,
|
||||
(sizeof(CCS811_baseline) / sizeof(byte)));
|
||||
}
|
||||
Response_P(S_JSON_CCS811_COMMAND_NVALUE,
|
||||
command,
|
||||
((CCS811_baseline[0] << 8) + CCS811_baseline[1]));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// else for Unknown command
|
||||
serviced = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return serviced;
|
||||
}
|
||||
|
||||
|
||||
const char HTTP_SNS_CCS811[] PROGMEM =
|
||||
"{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
"{s}%s " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}";
|
||||
|
||||
const char * devicenamelist[] PROGMEM = { "CCS811", "CCS811_1", "CCS811_2" };
|
||||
|
||||
void CCS811Show(bool json)
|
||||
{
|
||||
uint8_t ready_count = 0;
|
||||
for (i = 0, pccsd = ccsd; i < MAXDEVICECOUNT; i++, pccsd++) {
|
||||
if ((pccsd->device_found) && (pccsd->device_ready)) {
|
||||
ready_count += 1;
|
||||
}
|
||||
}
|
||||
if (!ready_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
// in upcoming loops use either one device name
|
||||
// with no index or or two names with index
|
||||
const char ** pdevicename;
|
||||
const char ** pdevicename_first = devicenamelist;
|
||||
if (ready_count > 1) {
|
||||
pdevicename_first++;
|
||||
}
|
||||
|
||||
if (json) {
|
||||
for (i = 0, pccsd = ccsd, pdevicename = pdevicename_first; i < MAXDEVICECOUNT; i++, pccsd++) {
|
||||
if (pccsd->device_ready) {
|
||||
ResponseAppend_P( PSTR(",\"%s\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d}"),
|
||||
*pdevicename,
|
||||
pccsd->eCO2,
|
||||
pccsd->TVOC);
|
||||
pdevicename++;
|
||||
}
|
||||
}
|
||||
#ifdef USE_DOMOTICZ
|
||||
if (0 == TasmotaGlobal.tele_period) {
|
||||
if (pccsd->device_ready) {
|
||||
pccsd = ccsd;
|
||||
DomoticzSensor(DZ_AIRQUALITY, pccsd->eCO2);
|
||||
}
|
||||
}
|
||||
#endif // USE_DOMOTICZ
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
for (i = 0, pccsd = ccsd, pdevicename = pdevicename_first; i < MAXDEVICECOUNT; i++, pccsd++) {
|
||||
if (pccsd->device_ready) {
|
||||
WSContentSend_PD( HTTP_SNS_CCS811,
|
||||
*pdevicename, pccsd->eCO2,
|
||||
*pdevicename, pccsd->TVOC);
|
||||
pdevicename++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns31(uint8_t function)
|
||||
{
|
||||
if (!I2cEnabled(XI2C_24)) { return false; }
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (FUNC_INIT == function) {
|
||||
CCS811Detect();
|
||||
}
|
||||
else if (CCS811_devices_found) {
|
||||
switch (function) {
|
||||
case FUNC_EVERY_SECOND:
|
||||
CCS811Update();
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = CCS811CommandSensor();
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
CCS811Show(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
CCS811Show(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_CCS811_V2
|
||||
#endif // USE_I2C
|
Loading…
x
Reference in New Issue
Block a user