Add support for HM330X

Add support for HM330X SeedStudio Grove Particule sensor (#13250)
This commit is contained in:
Theo Arends 2021-10-08 13:59:11 +02:00
parent eaeacbfa93
commit a466c5abf3
35 changed files with 415 additions and 9 deletions

View File

@ -160,6 +160,8 @@ m = minimal, l = lite, t = tasmota, k = knx, s = sensors, i = ir, d = display
| USE_SEESAW_SOIL | - | - | - / - | - | - | - | - |
| USE_TOF10120 | - | - | - / - | - | - | - | - |
| USE_AM2320 | - | - | - / - | - | - | - | - |
| USE_T67XX | - | - | - / - | - | - | - | - |
| USE_HM330X | - | - | - / - | - | - | - | - |
| | | | | | | | |
| Feature or Sensor | m | l | t | k | s | i | d | Remarks
| USE_SPI | - | - | - / - | - | - | - | x |

View File

@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- Commands ``EnergyUsage`` and ``EnergyExport`` to (re)set energy usage and export values
- Berry add module ``import persist``
- Support for BL0942 energy monitor (#13259)
- Support for HM330X SeedStudio Grove Particule sensor (#13250)
### Breaking Changed
- ESP32 LVGL updated to v8.0.2

View File

@ -96,3 +96,4 @@ Index | Define | Driver | Device | Address(es) | Description
60 | USE_AM2320 | xsns_88 | AM2320 | 0x5C | Temperature and Humidity sensor
61 | USE_T67XX | xsns_89 | T67XX | 0x15 | CO2 sensor
62 | USE_SCD40 | xsns_92 | SCD40 | 0x62 | CO2 sensor Sensirion SCD40/SCD41
63 | USE_HM330X | xsns_93 | HM330X | 0x40 | Particule sensor

View File

@ -126,6 +126,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
- Support for Sensirion SCD40/SCD41 CO2 sensor [#13139](https://github.com/arendst/Tasmota/issues/13139)
- Support for BL0939 energy monitor as used in ESP32 based Sonoff Dual R3 V2 Pow [#13195](https://github.com/arendst/Tasmota/issues/13195)
- Support for BL0942 energy monitor [#13259](https://github.com/arendst/Tasmota/issues/13259)
- Support for HM330X SeedStudio Grove Particule sensor [#13250](https://github.com/arendst/Tasmota/issues/13250)
- Initial support for Tasmota Mesh (TasMesh) providing node/broker communication using ESP-NOW [#11939](https://github.com/arendst/Tasmota/issues/11939)
- Initial support for Wi-Fi extender [#12784](https://github.com/arendst/Tasmota/issues/12784)
- Rule event support as JSON payload [#12496](https://github.com/arendst/Tasmota/issues/12496)

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -711,6 +711,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 TX"
#define D_SENSOR_PN532_RX "PN532 RX"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -710,6 +710,7 @@
#define D_SENSOR_CSE7761_RX "CSE7761 - RX"
#define D_SENSOR_CSE7766_TX "CSE7766 - TX"
#define D_SENSOR_CSE7766_RX "CSE7766 - RX"
#define D_SENSOR_HM330X_SET "HM330X - SET"
#define D_SENSOR_PN532_TX "PN532 - TX"
#define D_SENSOR_PN532_RX "PN532 - RX"
#define D_SENSOR_SM16716_CLK "SM16716 - CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -712,6 +712,7 @@
#define D_SENSOR_CSE7766_RX "CSE7766 Rx"
#define D_SENSOR_BL0939_RX "BL0939 Rx"
#define D_SENSOR_BL0942_RX "BL0942 Rx"
#define D_SENSOR_HM330X_SET "HM330X SET"
#define D_SENSOR_PN532_TX "PN532 Tx"
#define D_SENSOR_PN532_RX "PN532 Rx"
#define D_SENSOR_SM16716_CLK "SM16716 CLK"

View File

@ -653,6 +653,11 @@
// #define USE_BM8563 // [I2cDriver59] Enable BM8563 RTC - found in M5Stack - support both I2C buses on ESP32 (I2C address 0x51) (+2k5 code)
// #define USE_AM2320 // [I2cDriver60] Enable AM2320 temperature and humidity Sensor (I2C address 0x5C) (+1k code)
// #define USE_T67XX // [I2cDriver61] Enable Telaire T67XX CO2 sensor (I2C address 0x15) (+1k3 code)
// #define USE_HM330X // [I2cDriver63] Enable support for SeedStudio Grove Particule sensor (I2C address 0x40) (+1k5 code)
// #define HM330X_DEFAULT_ADDRESS 0x40 // Option: change default I2C address for HM330X used in SeedSTudio Particucle Sensor
// #define HM330X_WARMUP_DELAY 30 // Option: change warmup delay during which data are not read from sensor after a power up
// #define HM330X_HIDE_OUT_OF_DATE false // Option: change to true to hide data from web GUI and SENSOR while sensor is asleep
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
#define USE_DISPLAY_LCD // [DisplayModel 1] [I2cDriver3] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code)

View File

@ -771,7 +771,9 @@ void ResponseAppendFeatures(void)
#if defined(USE_I2C) && defined(USE_SCD40)
feature8 |= 0x00004000; // xsns_92_scd40.ino
#endif
// feature8 |= 0x00008000;
#if defined(USE_I2C) && defined(USE_HM330X)
feature8 |= 0x00008000;
#endif
// feature8 |= 0x00010000;
// feature8 |= 0x00020000;

View File

@ -174,6 +174,7 @@ enum UserSelectablePins {
GPIO_VINDRIKTNING_RX, // IKEA VINDRIKTNING Serial interface
GPIO_BL0939_RX, // BL0939 Serial interface (Dual R3 v2)
GPIO_BL0942_RX, // BL0942 Serial interface
GPIO_HM330X_SET, // HM330X SET pin (sleep when low)
GPIO_SENSOR_END };
enum ProgramSelectablePins {
@ -367,7 +368,8 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_HRG15_TX "|" D_SENSOR_HRG15_RX "|"
D_SENSOR_VINDRIKTNING_RX "|"
D_SENSOR_BL0939_RX "|"
D_SENSOR_BL0942_RX
D_SENSOR_BL0942_RX "|"
D_SENSOR_HM330X_SET
;
const char kSensorNamesFixed[] PROGMEM =
@ -755,6 +757,12 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_PMS5003_TX), // Plantower PMS5003 Serial interface
AGPIO(GPIO_PMS5003_RX), // Plantower PMS5003 Serial interface
#endif
#ifdef USE_VINDRIKTNING
AGPIO(GPIO_VINDRIKTNING_RX),
#endif
#ifdef USE_HM330X
AGPIO(GPIO_HM330X_SET), // HM330X Sleep pin (active low)
#endif
#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) || defined(USE_WS2300_WIND_SENSOR)
AGPIO(GPIO_TX2X_TXD_BLACK), // TX20/TX23 Transmission Pin
#endif
@ -805,9 +813,6 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_HRG15_TX),
AGPIO(GPIO_HRG15_RX),
#endif
#ifdef USE_VINDRIKTNING
AGPIO(GPIO_VINDRIKTNING_RX),
#endif
/*-------------------------------------------------------------------------------------------*\
* Other sensors

361
tasmota/xsns_93_hm330x.ino Normal file
View File

@ -0,0 +1,361 @@
/*
xsns_92_hm330x.ino - Driver for Seedstudio Grove HM3301 particle sensor
Copyright (C) 2021 Barbudor, SeedStudio
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_HM330X
/**************************************************************************************************
* Driver for Particule sensor SeedStudio Grove HM3301
* ================================================================================================
* This driver use the I2C mode. Only address 0x40 is supported by the sensor so it is hardcoded in
* the driver without any possibility to change it (without editing and recompiling)
* By default the sensor remains active all the time but no data are provided for the first
* HM330X_WARMUP_DELAY seconds (to allow the sensor to warm up). During warmup, no data are
* returned in the GUI or through SENSOR message in order to provide invalid data. In case
* of any doubt, logs with level 3 will confirm right at boot if the sensor has been detected.
*
* Low power / life extension
* --------------------------
* It is possible to connect a GPIO to pin SET of the module and assign it to "HM330X SET" for
* automatic sleep in order to extend the life time of the sensor. When this is used, activity of
* the sensor in synced with Teleperiod as follow
* 1) At start, sensor is immediately enabled by setting "HM330X SET" HI and start warmup
* If any Telemetry occurs before the end of the warmup, no data are provided and warmup
* goes on.
* 2) After HM330X_WARMUP_DELAY, data are available and reported in web and SENSOR
* 3) Immediately after the next teleperiod SENSOR message, if Teleperiod is greater than
* (HM330X_WARMUP_DELAY + 2) the sensor is put to sleep by taking the pin "HM330X SET" LOW
* and a new cycle start
* New cycle
* 4) (HM330X_WARMUP_DELAY + 2) seconds before the next teleperiod, the sensor is awaken by taking
* the pin "HM330X SET" HI for warmup. No data are available yet
* 5) 2 seconds before Telemetry, warmup is completed and data are available to Web until
* Telemetry messages are sent
* 6) Back to 2)
*
* Overridables (in user_config_override.h)
* ----------------------------------------
* HM330X_DEFAULT_ADDRESS 0x40 in case SeedStudio makes this changeable in the future
* HM330X_WARMUP_DELAY 30 time for warmup, data are not pulled before that delay
* HM330X_HIDE_OUT_OF_DATE false if changed to true web data and sensor will not be
* published when outdated
*************************************************************************************************/
//#define HM330X_SIM
#define XSNS_93 93
#define XI2C_63 63 // See I2CDEVICES.md
#ifndef HM330X_DEFAULT_ADDRESS
#define HM330X_DEFAULT_ADDRESS 0x40
#endif
#define HM330X_SELECT_COMM_CMD 0X88
#define HM330X_FRAME_SIZE 29
#ifndef HM330X_WARMUP_DELAY
#define HM330X_WARMUP_DELAY 30 // Ignore HM330X data for XX seconds after start
#endif
#define HM330X_PRE_TELEPERIOD_ENABLE 3 // Sensor made active before teleperiod
#ifndef HM330X_HIDE_OUT_OF_DATE
#define HM330X_HIDE_OUT_OF_DATE false // override to true to disable display when the data are out of date
#endif
enum {
HM330X_NO_ERROR = 0,
HM330X_ERROR_PARAM = -1,
HM330X_ERROR_NOT_FOUND = -2,
HM330X_ERROR_COMM = -3,
HM330X_ERROR_TIMEOUT = -4,
HM330X_ERROR_CHECKSUM = -5,
HM330X_ERROR_MEMALLOC = -6,
HM330X_ERROR_OTHERS = -128
};
struct HM330XFrame {
uint16_t reserved;
uint16_t sensor_num;
uint16_t pm1_0_standard, pm2_5_standard, pm10_0_standard;
uint16_t pm1_0_env, pm2_5_env, pm10_0_env;
uint16_t particles_0_3um, particles_0_5um, particles_1_0um, particles_2_5um, particles_5_0um, particles_10_0um;
uint8_t checksum;
};
enum HM330X_State {
HM330X_SLEEP = 0, // sensor is sleeping
HM330X_WARMUP = 1, // sensor is in warmup
HM330X_ACTIVE = 2 // sensor is active and can be polled for data
};
struct HM330XDATA {
uint8_t warmup_counter; // count for warmup
bool valid; // valid data have been retrieved from the sensor
uint8_t state; // state machine
int8_t set_pin; // pin to set the device asleep or active
struct HM330XFrame rx_buffer;
};
struct HM330XDATA *HM330Xdata = nullptr;
int HM330XUpdate() {
#ifdef HM330X_SIM
HM330Xdata->rx_buffer.pm1_0_standard = 10;
HM330Xdata->rx_buffer.pm2_5_standard = 25;
HM330Xdata->rx_buffer.pm10_0_standard = 100;
HM330Xdata->rx_buffer.pm1_0_env = 1010;
HM330Xdata->rx_buffer.pm2_5_env = 1025;
HM330Xdata->rx_buffer.pm10_0_env = 1100;
HM330Xdata->rx_buffer.particles_0_3um = 2003;
HM330Xdata->rx_buffer.particles_0_5um = 2005;
HM330Xdata->rx_buffer.particles_1_0um = 2010;
HM330Xdata->rx_buffer.particles_2_5um = 2025;
HM330Xdata->rx_buffer.particles_5_0um = 2050;
HM330Xdata->rx_buffer.particles_10_0um = 2100;
#else
uint32_t time_out_count = 0;
#if HM330X_HIDE_OUT_OF_DATE
HM330Xdata->valid = false;
#endif
Wire.requestFrom((uint8_t)HM330X_DEFAULT_ADDRESS, (uint8_t)HM330X_FRAME_SIZE);
while (HM330X_FRAME_SIZE != Wire.available()) {
time_out_count++;
if (time_out_count > 10) {
return HM330X_ERROR_TIMEOUT;
}
delay(1);
}
uint8_t checksum = 0;
uint8_t *data = (uint8_t*)&(HM330Xdata->rx_buffer);
int i = 0;
for (; i < HM330X_FRAME_SIZE-1; i++) {
checksum += data[i] = Wire.read();
}
data[i] = Wire.read();
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, data, HM330X_FRAME_SIZE);
if (checksum != data[i]) {
AddLog(LOG_LEVEL_DEBUG, PSTR("HM3: checksum error"));
return HM330X_ERROR_CHECKSUM;
}
for (i = 0; i < (HM330X_FRAME_SIZE/2); i++) {
uint8_t tmp = data[2*i]; // revert endianness
data[2*i] = data[2*i+1],
data[2*i+1] = tmp;
}
#endif
HM330Xdata->valid = true;
return HM330X_NO_ERROR;
}
int HM330XSendI2CCommand(uint8_t cmd) {
int ret = HM330X_NO_ERROR;
#ifdef HM330X_SIM
#else
Wire.beginTransmission(HM330X_DEFAULT_ADDRESS);
Wire.write(cmd);
ret = Wire.endTransmission();
if (ret != 0) {
ret = HM330X_ERROR_NOT_FOUND;
}
#endif
return ret;
}
int HM330XInit() {
int8_t set_pin = Pin(GPIO_HM330X_SET);
if (set_pin >= 0) {
pinMode(set_pin, OUTPUT);
digitalWrite(set_pin, 1);
delay(5);
}
if (I2cActive(HM330X_DEFAULT_ADDRESS)) {
AddLog(LOG_LEVEL_DEBUG, PSTR("HM3: Address 0x%02X already in use"), HM330X_DEFAULT_ADDRESS);
return HM330X_ERROR_PARAM;
}
if (HM330X_NO_ERROR != HM330XSendI2CCommand(HM330X_SELECT_COMM_CMD)) {
AddLog(LOG_LEVEL_DEBUG, PSTR("HM3: no response from address 0x%02X"), HM330X_DEFAULT_ADDRESS);
return HM330X_ERROR_COMM;
}
HM330Xdata = (HM330XDATA*)calloc(1,sizeof(HM330XDATA));
if (!HM330Xdata) {
AddLog(LOG_LEVEL_DEBUG, PSTR("HM3: out of memory"));
return HM330X_ERROR_MEMALLOC;
}
HM330Xdata->valid = false;
HM330Xdata->set_pin = set_pin;
HM330Xdata->state = HM330X_WARMUP;
HM330Xdata->warmup_counter = HM330X_WARMUP_DELAY;
I2cSetActiveFound(HM330X_DEFAULT_ADDRESS, "HM330X");
return HM330X_NO_ERROR;
}
void HM330XEverySecond() {
uint16_t time_until_tp = Settings->tele_period - TasmotaGlobal.tele_period;
// if (HM330Xdata)
// AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("HM3: EverySec wu:%d, v:%d, s:%d, sp:%d"),
// HM330Xdata->warmup_counter,
// HM330Xdata->valid,
// HM330Xdata->state,
// HM330Xdata->set_pin
// );
// else
// AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("HM3: nullptr"));
if (HM330X_SLEEP == HM330Xdata->state) {
if (time_until_tp < (HM330X_WARMUP_DELAY+HM330X_PRE_TELEPERIOD_ENABLE)) {
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HM3: Exit sleep mode"));
if (HM330Xdata->set_pin >= 0) {
digitalWrite(HM330Xdata->set_pin, 1);
}
HM330Xdata->state = HM330X_WARMUP;
HM330Xdata->warmup_counter = HM330X_WARMUP_DELAY;
#if HM330X_HIDE_OUT_OF_DATE
HM330Xdata->valid = false;
#endif
}
}
if (HM330X_WARMUP == HM330Xdata->state) {
HM330Xdata->warmup_counter--;
if (0 == HM330Xdata->warmup_counter) {
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HM3: Warmup done"));
if (HM330X_NO_ERROR != HM330XSendI2CCommand(HM330X_SELECT_COMM_CMD)) {
AddLog(LOG_LEVEL_DEBUG, PSTR("HM3: no response from address 0x%02X"), HM330X_DEFAULT_ADDRESS);
}
HM330Xdata->state = HM330X_ACTIVE;
}
}
if (HM330X_ACTIVE == HM330Xdata->state) {
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HM3: Update"));
HM330XUpdate();
}
}
void HM330XEnterSleep()
{
if (HM330Xdata->set_pin >= 0 && HM330X_ACTIVE == HM330Xdata->state && Settings->tele_period > (HM330X_WARMUP_DELAY+HM330X_PRE_TELEPERIOD_ENABLE)) {
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HM3: Enter sleep mode"));
digitalWrite(HM330Xdata->set_pin, 0);
HM330Xdata->state = HM330X_SLEEP;
#if HM330X_HIDE_OUT_OF_DATE
HM330Xdata->valid = false;
#endif
}
}
/*********************************************************************************************\
* SNS Interface
\*********************************************************************************************/
const char JSON_HM330X_SNS[] PROGMEM = ",\"HM330X\":{"
"\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,"
"\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d,"
"\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}";
const char HTTP_HM330X_SNS[] PROGMEM =
// "{s}HM330X " D_STANDARD_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"
// "{s}HM330X " D_STANDARD_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"
// "{s}HM330X " D_STANDARD_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"
"{s}HM330X " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"
"{s}HM330X " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"
"{s}HM330X " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"
"{s}HM330X " D_PARTICALS_BEYOND " 0.3 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"
"{s}HM330X " D_PARTICALS_BEYOND " 0.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"
"{s}HM330X " D_PARTICALS_BEYOND " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"
"{s}HM330X " D_PARTICALS_BEYOND " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"
"{s}HM330X " D_PARTICALS_BEYOND " 5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"
"{s}HM330X " D_PARTICALS_BEYOND " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
void HM330XShow(bool json)
{
if (HM330Xdata->valid)
{
if (json) {
ResponseAppend_P(JSON_HM330X_SNS,
HM330Xdata->rx_buffer.pm1_0_standard, HM330Xdata->rx_buffer.pm2_5_standard, HM330Xdata->rx_buffer.pm10_0_standard,
HM330Xdata->rx_buffer.pm1_0_env, HM330Xdata->rx_buffer.pm2_5_env, HM330Xdata->rx_buffer.pm10_0_env,
HM330Xdata->rx_buffer.particles_0_3um, HM330Xdata->rx_buffer.particles_0_5um, HM330Xdata->rx_buffer.particles_1_0um,
HM330Xdata->rx_buffer.particles_2_5um, HM330Xdata->rx_buffer.particles_5_0um, HM330Xdata->rx_buffer.particles_10_0um);
#ifdef USE_DOMOTICZ
if (0 == TasmotaGlobal.tele_period) {
DomoticzSensor(DZ_COUNT, HM330Xdata->rx_buffer.pm1_0_env); // PM1
DomoticzSensor(DZ_VOLTAGE, HM330Xdata->rx_buffer.pm2_5_env); // PM2.5
DomoticzSensor(DZ_CURRENT, HM330Xdata->rx_buffer.pm10_0_env); // PM10
}
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
WSContentSend_PD(HTTP_HM330X_SNS,
// HM330Xdata->rx_buffer.pm1_0_standard, HM330Xdata->rx_buffer.pm2_5_standard, HM330Xdata->rx_buffer.pm10_0_standard,
HM330Xdata->rx_buffer.pm1_0_env, HM330Xdata->rx_buffer.pm2_5_env, HM330Xdata->rx_buffer.pm10_0_env,
HM330Xdata->rx_buffer.particles_0_3um, HM330Xdata->rx_buffer.particles_0_5um, HM330Xdata->rx_buffer.particles_1_0um,
HM330Xdata->rx_buffer.particles_2_5um, HM330Xdata->rx_buffer.particles_5_0um, HM330Xdata->rx_buffer.particles_10_0um);
#endif // USE_WEBSERVER
}
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xsns93(byte function)
{
if (!I2cEnabled(XI2C_63)) { return false; }
bool result = false;
if (FUNC_INIT == function) {
HM330XInit();
}
else if (HM330Xdata) {
switch (function) {
case FUNC_EVERY_SECOND:
HM330XEverySecond();
break;
case FUNC_AFTER_TELEPERIOD:
HM330XEnterSleep();
break;
case FUNC_JSON_APPEND:
HM330XShow(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
HM330XShow(0);
break;
#endif // USE_WEBSERVER
}
}
return result;
}
#endif // USE_HM330X
#endif // USE_I2C

View File

@ -182,8 +182,10 @@ a_setoption = [[
"(Zigbee) Hide bridge topic from zigbee topic (use with SetOption89) (1)",
"(DS18x20) Enable arithmetic mean over teleperiod for JSON temperature (1)",
"(Wifi) Keep wifi in no-sleep mode, prevents some occasional unresponsiveness",
"","",
"","","","",
"(Web) Allow access without referer check",
"(Energy) Show phase information",
"(Debug) Show heap with logging timestamp",
"","","",
"","","","",
"","","","",
"","","",""
@ -256,7 +258,7 @@ a_features = [[
"USE_MPU_ACCEL","USE_TFMINIPLUS","USE_CSE7761","USE_BERRY",
"USE_BM8563","USE_ENERGY_DUMMY","USE_AM2320","USE_T67XX",
"USE_MCP2515","USE_TASMESH","USE_WIFI_RANGE_EXTENDER","USE_INFLUXDB",
"USE_HRG15","USE_VINDRIKTNING","USE_SCD40","",
"USE_HRG15","USE_VINDRIKTNING","USE_SCD40","USE_HM330X",
"","","","",
"","","","",
"","","","",
@ -288,7 +290,7 @@ else:
obj = json.load(fp)
def StartDecode():
print ("\n*** decode-status.py v20210901 by Theo Arends and Jacek Ziolkowski ***")
print ("\n*** decode-status.py v20211008 by Theo Arends and Jacek Ziolkowski ***")
# print("Decoding\n{}".format(obj))