From bf91af0c07e2ee573f5d95173a1b75667124c973 Mon Sep 17 00:00:00 2001 From: Bastian Urschel <56123159+baschdello@users.noreply.github.com> Date: Mon, 26 Feb 2024 23:04:25 +0100 Subject: [PATCH] add support for ams5915 and ams6915 temerature and pressure sensors (#20814) * add support for ams5915 and ams6915 temerature and pressure sensors * improved function namings * optimized detection of sensor * set verbosity level to debug * minor code cleanings * added seconds-counter to minimize i2c bus load, minor code cleanups * add meas_valid in sensor struct to differentiate between sensor present (at tasmota boot) and measurement valid (if sensor was reattached) at runtime * removed global struct and sensor_present, introduced dynamic memory allocation if sensor is present at startup, minor code changes * fixes missing initialisation of variable * corrected the amount of used memory if compiled with AMSX915 support * Update my_user_config.h disable AMSx915 by default --------- Co-authored-by: baschdello Co-authored-by: Bastian --- I2CDEVICES.md | 2 + tasmota/my_user_config.h | 1 + .../tasmota_xsns_sensor/xsns_114_amsx915.ino | 240 ++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 tasmota/tasmota_xsns_sensor/xsns_114_amsx915.ino diff --git a/I2CDEVICES.md b/I2CDEVICES.md index 1a8500954b3f..71e4ff15290e 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -123,5 +123,7 @@ Index | Define | Driver | Device | Address(es) | Bus2 | Descrip 83 | USE_MAX17043 | xsns_110 | MAX17043 | 0x36 | | Fuel-gauge for 3.7 Volt Lipo battery 84 | USE_ENS16x | xsns_111 | ENS16x | 0x52 - 0x53 | | Gas (TVOC, eCO2) and air quality sensor 85 | USE_ENS210 | xsns_112 | ENS210 | 0x43 - 0x44 | | Temperature and humidity sensor + 86 | USE_AMSX915 | xsns_114 | AMS5915 | 0x28 | | Pressure (absolute/differential) and temperature sensor + 86 | USE_AMSX915 | xsns_114 | AMS6915 | 0x28 | | Pressure (absolute/differential) and temperature sensor NOTE: Bus2 supported on ESP32 only. diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 04287c238a4f..df443b6fc9a5 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -738,6 +738,7 @@ // #define DS3231_ENABLE_TEMP // In DS3231 driver, enable the internal temperature sensor // #define USE_BM8563 // [I2cDriver59] Enable BM8563 RTC - found in M5Stack - support both I2C buses on ESP32 (I2C address 0x51) (+2.5k code) // #define USE_PCF85363 // [I2cDriver66] Enable PCF85363 RTC - found Shelly 3EM (I2C address 0x51) (+0k7 code) +// #define USE_AMSX915 // [I2CDriver86] Enable AMS5915/AMS6915 pressure/temperature sensor (+1k2 code) // #define USE_DISPLAY // Add I2C/TM1637/MAX7219 Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/tasmota_xsns_sensor/xsns_114_amsx915.ino b/tasmota/tasmota_xsns_sensor/xsns_114_amsx915.ino new file mode 100644 index 000000000000..4b6a6c9a6303 --- /dev/null +++ b/tasmota/tasmota_xsns_sensor/xsns_114_amsx915.ino @@ -0,0 +1,240 @@ +/* + xsns_114_ams.ino - AMS5915, AMS6915 pressure and temperature sensor support for Tasmota + + Copyright (C) 2023 Bastian Urschel + + 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 . +*/ + +#ifdef USE_I2C +#ifdef USE_AMSX915 +/*********************************************************************************************\ + * AMS5915, AMS6915 - Pressure and Temperature + * + * Source: Bastian Urschel + * + * I2C Address: 0x28 +\*********************************************************************************************/ + +#define XSNS_114 114 +#define XI2C_86 86 // See I2CDEVICES.md + +#ifndef AMSX915_ADDR + #define AMSX915_ADDR 0x28 +#endif + +#define AMSX915_EVERYNSECONDS 5 +#define AMSX915_DEVICE_NAME "AMSx915" +#define AMSX915_LOG "AMS" + +#define PMIN_DEFAULT 0 +#define PMAX_DEFAULT 0 + +#ifndef USE_UFILESYS +#warning "AMSx915 pressure settings cannot be saved persistent due to missing filesystem" +#endif + +/********************************************************************************************/ + +typedef struct amsx915data_s { + uint32_t file_crc32; // To detect file changes + uint16_t file_version; // To detect driver function changes + int16_t pmin; + int16_t pmax; + float pressure; + float temperature; + bool sensor_present; + bool meas_valid; + uint8_t cnt; +} amsx915data_t; +amsx915data_t *amsx915 = nullptr; + +const uint16_t AMSX915_VERSION = 0x0100; // Latest sensor version (See settings deltas below) + +bool Amsx915Command() { + int32_t vals[2]; + ParseParameters(2,(uint32_t *)vals); + if(XdrvMailbox.data_len >= 3 && XdrvMailbox.data_len < 13) { + if (vals[0] >= -32768 && vals[0] < 32768) { + if (vals[1] >= -32768 && vals[1] < 32768) { + amsx915->pmin = (int16_t)vals[0]; // save pmin of sensor + amsx915->pmax = (int16_t)vals[1]; // same with pmax + Amsx915SettingsSave(); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_114, "pressure range set"); + return true; + } + } + } + else if(XdrvMailbox.data_len == 0) { + Response_P(PSTR("{\"" AMSX915_DEVICE_NAME "\": {\"pmin\":%i,\"pmax\":%i}}"), amsx915->pmin, amsx915->pmax); + return true; + } + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_114, "invalid pressure range [-32768..32767]"); + return false; +} + +void Amsx915Detect(void) { + // i2c frontend does not provide any commands/register to detect this sensor type -> request 4 bytes and check for 4 byte response is mandatory + + if (!I2cActive(AMSX915_ADDR)) + { + uint8_t i=0; + while(i++ < 2) { // try 2 times because sensor sometimes not respond at first request + Wire.requestFrom(AMSX915_ADDR, 4); + if(Wire.available() == 4) { + I2cSetActiveFound(AMSX915_ADDR, AMSX915_DEVICE_NAME); + amsx915 = (amsx915data_t *)calloc(1, sizeof(amsx915data_t)); + if (!amsx915) { + AddLog(LOG_LEVEL_ERROR, PSTR(AMSX915_LOG ":@%02X Memory error!"), AMSX915_ADDR); + } + else { + Amsx915SettingsLoad(0); // load config + } + break; + } + } + } +} + +void Amsx915ReadData(void) { + if(amsx915->cnt++ == AMSX915_EVERYNSECONDS) { // try to read sensor every n seconds + amsx915->cnt = 0; + uint8_t buffer[4]; + Wire.requestFrom(AMSX915_ADDR, 4); + if(Wire.available() != 4) { + amsx915->meas_valid = false; + return; + } + buffer[0] = Wire.read(); + buffer[1] = Wire.read(); + buffer[2] = Wire.read(); + buffer[3] = Wire.read(); + + amsx915->pressure = ((256*(buffer[0]&0x3F)+buffer[1])-1638.0f)*((amsx915->pmax)-(amsx915->pmin))/13107+(amsx915->pmin); + amsx915->temperature = (((256.0*buffer[2]+buffer[3])*200.0f)/65536)-50; + amsx915->meas_valid = true; + } +} + +void Amsx915Show(bool json) { + if(amsx915->meas_valid) { + if (json) { + ResponseAppend_P(PSTR(",\"" AMSX915_DEVICE_NAME "\":{\"" D_JSON_TEMPERATURE "\":%1_f,\"" D_JSON_PRESSURE "\":%2_f}"), &amsx915->temperature, &amsx915->pressure); +#ifdef USE_WEBSERVER + } else { + char str_pressure[9]; + dtostrfd(amsx915->pressure, 2, str_pressure); + WSContentSend_PD(HTTP_SNS_PRESSURE, AMSX915_DEVICE_NAME, str_pressure, PressureUnit().c_str()); + WSContentSend_Temp(AMSX915_DEVICE_NAME, amsx915->temperature); +#endif // USE_WEBSERVER + } + } +} + +/*********************************************************************************************\ + * Driver Settings load and save +\*********************************************************************************************/ + +void Amsx915SettingsLoad(bool erase) { + // Called from FUNC_PRE_INIT (erase = 0) once at restart + memset(amsx915, 0x00, sizeof(amsx915data_t)); + amsx915->file_version = AMSX915_VERSION; + amsx915->pmax = PMAX_DEFAULT; + amsx915->pmin = PMIN_DEFAULT; + + // *** End Init default values *** + +#ifndef USE_UFILESYS + AddLog(LOG_LEVEL_INFO, PSTR("CFG: " AMSX915_DEVICE_NAME " defaults as file system not enabled")); +#else + // Try to load sensor config file + char filename[20]; + snprintf_P(filename, sizeof(filename), PSTR(TASM_FILE_SENSOR), XSNS_114); + + if (erase) { + TfsDeleteFile(filename); // Use defaults + } + else if (TfsLoadFile(filename, (uint8_t*)amsx915, sizeof(amsx915data_t))) { + if (amsx915->file_version != AMSX915_VERSION) { // Fix version dependent changes + // Set current version and save settings + amsx915->file_version = AMSX915_VERSION; + Amsx915SettingsSave(); + } + AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: " AMSX915_DEVICE_NAME " config loaded from file")); + } + else { + // File system not ready: No flash space reserved for file system + AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: " AMSX915_DEVICE_NAME " use defaults as file system not ready or file not found")); + } +#endif // USE_UFILESYS +} + +void Amsx915SettingsSave(void) { + // Called from FUNC_SAVE_SETTINGS every SaveData second and at restart +#ifdef USE_UFILESYS + uint32_t crc32 = GetCfgCrc32((uint8_t*)amsx915 +4, sizeof(amsx915data_t) -4); // Skip crc32 + if (crc32 != amsx915->file_crc32) { + // Try to save sensor config file + amsx915->file_crc32 = crc32; + + char filename[20]; + snprintf_P(filename, sizeof(filename), PSTR(TASM_FILE_SENSOR), XSNS_114); + + if (TfsSaveFile(filename, (const uint8_t*)amsx915, sizeof(amsx915data_t))) { + AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: " AMSX915_DEVICE_NAME " Settings saved to file")); + } else { + // File system not ready: No flash space reserved for file system + AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: ERROR " AMSX915_DEVICE_NAME " file system not ready or unable to save file")); + } + } +#endif // USE_UFILESYS +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns114(uint32_t function) { + if (!I2cEnabled(XI2C_86)) { return false; } + + bool result = false; + + if (function == FUNC_INIT) { + Amsx915Detect(); + } + if(amsx915) { + switch(function) { + case FUNC_EVERY_SECOND: + Amsx915ReadData(); + break; + case FUNC_COMMAND_SENSOR: + if(XSNS_114 == XdrvMailbox.index) { + result = Amsx915Command(); + } + break; + case FUNC_JSON_APPEND: + Amsx915Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Amsx915Show(0); + break; + #endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_AMSX915 +#endif // USE_I2C