Compare commits

...

18 Commits

Author SHA1 Message Date
legchenkov e5ccd23e4b
Merge 9fec5bddc1 into 4aa2da3eb2 2024-04-27 10:54:31 +02:00
bovirus 4aa2da3eb2
Update Italian language (#21283) 2024-04-26 16:52:35 +02:00
fb-pilot e2a08d5d01
Add files via upload (#21262)
fix display multiple devices and add more options
2024-04-26 16:52:06 +02:00
Jason2866 41970f7d62
Disable psram check to avoid "blinking" of GPIO 16/17 at startup (#21282)
* add `DISABLE_PSRAMCHECK`

* remove code before IDF 5

* add no psram env
2024-04-25 20:19:20 +02:00
Jason2866 eef4ff389f
Delete IDF 4.4 based i2s code (#21188)
* Delete tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino

* Delete tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino

* Delete tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino
2024-04-25 13:41:47 +02:00
s-hadinger 1e64eaddf3
Berry `webserver.content_close()` (#21276) 2024-04-25 12:50:43 +02:00
s-hadinger 466652549a
Fix faulty printing of IPv4 2024-04-25 11:23:27 +02:00
s-hadinger 2a35f325b8
Put back wifi IPv6 workaround (#21274) 2024-04-25 10:28:42 +02:00
s-hadinger 469492a41a
Berry `web_add_handler` called before `Webserver` is initialized (#21272) 2024-04-25 09:08:24 +02:00
legchenkov 9fec5bddc1
Add files via upload 2024-01-02 02:20:11 +01:00
legchenkov e98dac0f82
Merge pull request #1 from legchenkov/legchenkov-translate
Update ru_RU.h
2024-01-02 02:15:54 +01:00
legchenkov e273dea414
Update ru_RU.h
Partial translation update
2024-01-02 02:05:57 +01:00
legchenkov a0ab05f912
Add files via upload 2024-01-02 01:38:35 +01:00
legchenkov e50833b545
Delete tasmota/language/ru_RU.h
Will go to separate PR
2024-01-02 01:18:00 +01:00
legchenkov 1fbd022e3f
Update ru_RU.h
Translated Dew Point to russian Точка росы for sensors
2023-11-22 19:38:27 +01:00
legchenkov 1d24a40a44
Update xsns_63_aht1x.ino
The reset command is aligned with the AHT10/20/30 datasheet.
2023-11-05 13:10:27 +01:00
legchenkov e82fff6682
Update xsns_63_aht1x.ino 2023-10-29 20:47:20 +01:00
legchenkov 3f26cbe7ba
Update xsns_63_aht1x.ino
Support for AHT30 added. Fixed command for loading factory calibrated data and reset command, added needed delay after read command and increased measurement period to reduce heating of sensor.
2023-10-29 20:09:10 +01:00
13 changed files with 379 additions and 1414 deletions

View File

@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Added
- esp32_partition_app3904k_fs3392k partition scheme for 8MB ESP32S3
- TCP Tx En GPIO type
- Berry `webserver.content_close()`
### Breaking Changed
- ESP32-C3 OTA binary name from `tasmota32c3cdc.bin` to `tasmota32c3.bin` with USB HWCDC and fallback to serial (#21212)
@ -22,6 +23,8 @@ All notable changes to this project will be documented in this file.
- LVGL restore `lv_palette` functions
- IPv6 support in safeboot
- LVGL fix memory allocation of flush buffers
- Berry `web_add_handler` called before `Webserver` is initialized
- Put back wifi IPv6 workaround
### Removed
- LVGL disabled vector graphics

View File

@ -25,6 +25,7 @@ extern int w_webserver_content_response(bvm *vm);
extern int w_webserver_content_send_style(bvm *vm);
extern int w_webserver_content_flush(bvm *vm);
extern int w_webserver_content_stop(bvm *vm);
extern int w_webserver_content_close(bvm *vm);
extern int w_webserver_content_button(bvm *vm);
extern int w_webserver_html_escape(bvm *vm);
@ -153,6 +154,7 @@ module webserver (scope: global) {
content_open, func(w_webserver_content_open)
content_start, func(w_webserver_content_start)
content_stop, func(w_webserver_content_stop)
content_close, func(w_webserver_content_close)
content_button, func(w_webserver_content_button)
html_escape, func(w_webserver_html_escape)

View File

@ -20,6 +20,17 @@ build_flags = ${env:tasmota32_base.build_flags}
-DUSE_WIFI_RANGE_EXTENDER_NAPT
-DOTA_URL='""'
; remove PSRAM support -> avoid "blinking" of GPIO 16/17 at boot
[env:tasmota32-nopsram]
extends = env:tasmota32
build_unflags = ${env:tasmota32_base.build_unflags}
-DBOARD_HAS_PSRAM
build_flags = ${env:tasmota32_base.build_flags}
-DFIRMWARE_TASMOTA32
-DDISABLE_PSRAMCHECK
-DCODE_IMAGE_STR='"tasmota32-nopsram"'
-DOTA_URL='""'
[env:tasmota32s3-file]
extends = env:tasmota32_base
board = esp32s3-qio_qspi

View File

@ -28,7 +28,7 @@
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
* Use online command Prefix to translate cmnd, stat and tele.
*
* Updated until v9.4.0.1 - Last update 27.03.2024
* Updated until v9.4.0.1 - Last update 26.04.2024
\*********************************************************************/
#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
@ -908,7 +908,7 @@
#define D_SENSOR_ETH_PHY_MDC "ETH - MDC"
#define D_SENSOR_ETH_PHY_MDIO "ETH - MDIO"
#define D_SENSOR_TCP_TXD "TCP - TX"
#define D_SENSOR_TCP_TXD_EN "TCP Tx En"
#define D_SENSOR_TCP_TXD_EN "TCP - TX En"
#define D_SENSOR_TCP_RXD "TCP - RX"
#define D_SENSOR_IEM3000_TX "iEM3000 - TX"
#define D_SENSOR_IEM3000_RX "iEM3000 - RX"

View File

@ -40,27 +40,23 @@ const static char kWifiPhyMode[] PROGMEM = "low rate|11b|11g|HT20|HT40|HE20"; //
#endif
// See libraries\ESP32\examples\ResetReason.ino
#if ESP_IDF_VERSION_MAJOR > 3 // IDF 4+
#include "esp_chip_info.h"
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2
#include "esp32c2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6
#include "esp32c6/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32H2 // ESP32-H2
#include "esp32h2/rom/rtc.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/rtc.h"
#include "esp_chip_info.h"
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2
#include "esp32c2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6
#include "esp32c6/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32H2 // ESP32-H2
#include "esp32h2/rom/rtc.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
// Set the Stacksize for Arduino core. Default is 8192, some builds may need a bigger one
@ -204,12 +200,7 @@ void NvsInfo(void) {
// See Esp.cpp
#include "Esp.h"
#if ESP_IDF_VERSION_MAJOR >= 5
// esp_spi_flash.h is deprecated, please use spi_flash_mmap.h instead
#include "spi_flash_mmap.h"
#else
#include "esp_spi_flash.h"
#endif
#include "spi_flash_mmap.h"
#include <memory>
#include <soc/soc.h>
#include <soc/efuse_reg.h>
@ -220,39 +211,34 @@ extern "C" {
}
#include "esp_system.h"
#include "esp_flash.h"
#if ESP_IDF_VERSION_MAJOR > 3 // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing magic flash size and spi mode
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing magic flash size and spi mode
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32s3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2
#include "esp32c2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c2 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6
#include "esp32c6/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c6 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32H2 // ESP32-H2
#include "esp32h2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32h2 is located at 0x0000
#else
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing magic flash size and spi mode
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing magic flash size and spi mode
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32s3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2
#include "esp32c2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c2 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6
#include "esp32c6/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c6 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32H2 // ESP32-H2
#include "esp32h2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32h2 is located at 0x0000
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000
#endif
#if ESP_IDF_VERSION_MAJOR >= 5
#include "bootloader_common.h"
#endif
#include "bootloader_common.h"
uint32_t EspProgramSize(const char *label) {
const esp_partition_t *part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, label);
if (!part) {
@ -462,7 +448,7 @@ uint32_t ESP_getFlashChipMagicSize(void) {
}
uint32_t ESP_magicFlashChipSize(uint8_t spi_size) {
/*
/*
switch(spi_size & 0x0F) {
case 0x0: // 8 MBit (1MB)
return 1048576;
@ -477,7 +463,7 @@ uint32_t ESP_magicFlashChipSize(uint8_t spi_size) {
case 0x5: // 256 MBit (32MB)
return 33554432;
default: // fail so return (1KB)
return 1024;
return 1024;
}
*/
// When spi_size is bigger than 11 will return 0 (0x100000000 = 0x00000000)
@ -590,14 +576,10 @@ extern "C" {
// `psramFound()` can return true even if no PSRAM is actually installed
// This new version also checks `esp_spiram_is_initialized` to know if the PSRAM is initialized
bool FoundPSRAM(void) {
#if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6
#if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || DISABLE_PSRAMCHECK
return psramFound();
#else
#if ESP_IDF_VERSION_MAJOR >= 5
return psramFound() && esp_psram_is_initialized();
#else
return psramFound() && esp_spiram_is_initialized();
#endif
return psramFound() && esp_psram_is_initialized();
#endif
}
@ -774,9 +756,6 @@ typedef struct {
- Peripherals include capacitive touch sensors, Hall sensor, SD card interface, Ethernet, high-speed SPI, UART, I2S and I2C
*/
#ifdef CONFIG_IDF_TARGET_ESP32
#if (ESP_IDF_VERSION_MAJOR < 5)
pkg_version = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG) & 0x7;
#endif
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d"), chip_info.model, chip_revision, chip_info.cores);
@ -816,9 +795,6 @@ typedef struct {
- Availability of common cloud connectivity agents and common product features shortens the time to market
*/
#ifdef CONFIG_IDF_TARGET_ESP32S2
#if (ESP_IDF_VERSION_MAJOR < 5)
pkg_version = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_FLASH_VERSION) & 0xF;
#endif
uint32_t psram_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PSRAM_VERSION);
pkg_version += ((psram_ver & 0xF) * 100);
@ -846,9 +822,6 @@ typedef struct {
- Rich set of peripheral interfaces and GPIOs, ideal for various scenarios and complex applications
*/
#ifdef CONFIG_IDF_TARGET_ESP32C3
#if (ESP_IDF_VERSION_MAJOR < 5)
pkg_version = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION) & 0x7;
#endif
switch (pkg_version) {
case 0: return F("ESP32-C3"); // Max 160MHz, Single core, QFN 5*5, ESP32-C3-WROOM-02, ESP32-C3-DevKitC-02
// case 1: return F("ESP32-C3FH4"); // Max 160MHz, Single core, QFN 5*5, 4MB embedded flash, ESP32-C3-MINI-1, ESP32-C3-DevKitM-1
@ -872,12 +845,10 @@ typedef struct {
- Reliable security features ensured by RSA-based secure boot, AES-XTS-based flash encryption, the innovative digital signature and the HMAC peripheral, World Controller
*/
#ifdef CONFIG_IDF_TARGET_ESP32S3
#if (ESP_IDF_VERSION_MAJOR >= 5)
switch (pkg_version) {
case 0: return F("ESP32-S3"); // QFN56
case 1: return F("ESP32-S3-PICO-1"); // LGA56
}
#endif
#endif // CONFIG_IDF_TARGET_ESP32S3
return F("ESP32-S3"); // Max 240MHz, Dual core, QFN 7*7, ESP32-S3-WROOM-1, ESP32-S3-DevKitC-1
}
@ -989,29 +960,13 @@ String WifiGetPhyMode(void) {
* Thanks to DigitalAlchemist
\*********************************************************************************************/
#if ESP_IDF_VERSION_MAJOR >= 5
#include <esp_random.h>
#endif
// Based on code from https://raw.githubusercontent.com/espressif/esp-idf/master/components/esp32/hw_random.c
uint32_t HwRandom(void) {
#if ESP_IDF_VERSION_MAJOR >= 5
// See for more info on the HW RNG:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/random.html
return esp_random();
#else
#define _RAND_ADDR 0x3FF75144UL
static uint32_t last_ccount = 0;
uint32_t ccount;
uint32_t result = 0;
do {
ccount = ESP.getCycleCount();
result ^= *(volatile uint32_t *)_RAND_ADDR;
} while (ccount - last_ccount < 64);
last_ccount = ccount;
return result ^ *(volatile uint32_t *)_RAND_ADDR;
#undef _RAND_ADDR
#endif // ESP_IDF_VERSION_MAJOR >= 5
}
/********************************************************************************************/

View File

@ -1469,6 +1469,19 @@ void WifiEvents(arduino_event_t *event) {
IPv6isLocal(addr) ? PSTR("Local") : PSTR("Global"), addr.toString(true).c_str());
}
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
// workaround for the race condition in LWIP, see https://github.com/espressif/arduino-esp32/pull/9016#discussion_r1451774885
{
uint32_t i = 5; // try 5 times only
while (esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA)) != ESP_OK) {
delay(1);
if (i-- == 0) {
break;
}
}
}
break;
#endif // USE_IPV6
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
{

View File

@ -1,772 +0,0 @@
/*
xdrv_42_i2s_audio.ino - Audio dac 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/>.
*/
#if ESP8266 || (ESP_IDF_VERSION_MAJOR < 5)
#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX))
/*********************************************************************************************\
* I2S support using an external DAC or a speaker connected to GPIO03 using a transistor
*
* Uses fixed GPIOs for ESP8266:
* I2S Out Data GPIO03 (Rx)
* I2S Out Bit Clock GPIO15
* I2S Out Word Select GPIO02
* I2S In Data GPIO12
* I2S In Bit Clock GPIO13
* I2S In Word Select GPIO14
\*********************************************************************************************/
#define XDRV_42 42
#define USE_I2S_EXTERNAL_DAC 1
//#define USE_I2S_NO_DAC // Add support for transistor-based output without DAC
//#define USE_I2S_WEBRADIO // Add support for web radio
//#define USE_I2S_SAY_TIME // Add support for english speaking clock
//#define USE_I2S_RTTTL // Add support for Rtttl playback
//#define USE_LSB // Add support for LSBJ chips, e.g. TM8211/PT8211
// Microphone support
//#define USE_I2S_MIC // Add support for I2S microphone
//#define MIC_CHANNELS 1 // 1 = mono (I2S_CHANNEL_FMT_ONLY_RIGHT), 2 = stereo (I2S_CHANNEL_FMT_RIGHT_LEFT)
//#define MICSRATE 32000 // Set sample rate
//#define USE_INMP441 // Add support for INMP441 MEMS microphone
//#define MIC_PDM // Set microphone as PDM (only on ESP32)
//#define USE_SHINE // Use MP3 encoding (only on ESP32 with PSRAM)
//#define MP3_MIC_STREAM // Add support for streaming microphone via http (only on ESP32 with PSRAM)
//#define MP3_STREAM_PORT 81 // Choose MP3 stream port (default = 81)
//#define I2S_BRIDGE // Add support for UDP PCM audio bridge
//#define I2S_BRIDGE_PORT 6970 // Set bridge port (default = 6970)
#include "AudioFileSourcePROGMEM.h"
#include "AudioFileSourceID3.h"
#include "AudioGeneratorMP3.h"
#ifdef USE_I2S_NO_DAC
#include "AudioOutputI2SNoDAC.h" // Transistor-driven lower quality connected to RX pin
#else
#include "AudioOutputI2S.h" // External I2S DAC IC
#endif // USE_I2S_NO_DAC
#include <ESP8266SAM.h>
#include "AudioFileSourceFS.h"
#ifdef USE_I2S_SAY_TIME
#include "AudioGeneratorTalkie.h"
#endif // USE_I2S_SAY_TIME
#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorAAC.h"
#ifdef ESP32
#include <driver/i2s.h>
#endif
#undef AUDIO_PWR_ON
#undef AUDIO_PWR_OFF
#define AUDIO_PWR_ON
#define AUDIO_PWR_OFF
#ifdef ESP8266
#define i2s_port_t uint8_t
#endif
#define MODE_MIC 1
#define MODE_SPK 2
#ifndef MICSRATE
#define MICSRATE 32000
#endif
typedef union {
uint8_t data;
struct {
uint8_t master : 1;
uint8_t enabled : 1;
uint8_t swap_mic : 1;
uint8_t mode : 2;
};
} BRIDGE_MODE;
struct AUDIO_I2S_t {
uint8_t is2_volume; // should be in settings
i2s_port_t i2s_port;
int8_t mclk = -1;
int8_t bclk = -1;
int8_t ws = -1;
int8_t dout = -1;
int8_t din = -1;
AudioGeneratorMP3 *mp3 = nullptr;
AudioFileSourceFS *file;
#ifdef USE_I2S_NO_DAC
AudioOutputI2SNoDAC *out;
#else
AudioOutputI2S *out;
#endif // USE_I2S_NO_DAC
AudioFileSourceID3 *id3;
AudioGeneratorMP3 *decoder = NULL;
void *mp3ram = NULL;
#ifdef USE_I2S_WEBRADIO
AudioFileSourceICYStream *ifile = NULL;
AudioFileSourceBuffer *buff = NULL;
char wr_title[64];
void *preallocateBuffer = NULL;
void *preallocateCodec = NULL;
uint32_t retryms = 0;
#endif // USE_I2S_WEBRADIO
#ifdef ESP32
TaskHandle_t mp3_task_h;
TaskHandle_t mic_task_h;
#endif // ESP32
uint32_t mic_size;
uint32_t mic_rate;
uint8_t *mic_buff;
char mic_path[32];
uint8_t mic_channels;
File fwp;
uint8_t mic_stop;
int8_t mic_error;
int8_t mic_mclk = -1;
int8_t mic_bclk = -1;
int8_t mic_ws = -1;
int8_t mic_din = -1;
int8_t mic_dout = -1;
uint8_t mic_gain = 1;
bool use_stream = false;
i2s_port_t mic_port;
#ifdef USE_SHINE
uint32_t recdur;
uint8_t stream_active;
uint8_t stream_enable;
WiFiClient client;
ESP8266WebServer *MP3Server;
#endif
uint8_t mode;
#ifdef I2S_BRIDGE
BRIDGE_MODE bridge_mode;
WiFiUDP i2s_bridge_udp;
WiFiUDP i2s_bridgec_udp;
IPAddress i2s_bridge_ip;
TaskHandle_t i2s_bridge_h;
int8_t ptt_pin = -1;
#endif
} audio_i2s;
#ifndef MIC_CHANNELS
#define MIC_CHANNELS 1
#endif
#ifdef USE_TTGO_WATCH
#undef AUDIO_PWR_ON
#undef AUDIO_PWR_OFF
#define AUDIO_PWR_ON TTGO_audio_power(true);
#define AUDIO_PWR_OFF TTGO_audio_power(false);
#endif // USE_TTGO_WATCH
#ifdef USE_M5STACK_CORE2
// leave this predefined currently
#undef AUDIO_PWR_ON
#undef AUDIO_PWR_OFF
#define AUDIO_PWR_ON Core2AudioPower(true);
#define AUDIO_PWR_OFF Core2AudioPower(false);
#undef DAC_IIS_BCK
#undef DAC_IIS_WS
#undef DAC_IIS_DOUT
#undef DAC_IIS_DIN
#define DAC_IIS_BCK 12
#define DAC_IIS_WS 0
#define DAC_IIS_DOUT 2
#define DAC_IIS_DIN 34
#undef MICSRATE
#define MICSRATE 32000
#undef MIC_CHANNELS
#define MIC_CHANNELS 1
#endif // USE_M5STACK_CORE2
#ifdef ESP32S3_BOX
#undef AUDIO_PWR_ON
#undef AUDIO_PWR_OFF
#define AUDIO_PWR_ON S3boxAudioPower(true);
#define AUDIO_PWR_OFF S3boxAudioPower(false);
#undef MIC_CHANNELS
#define MIC_CHANNELS 2
#endif // ESP32S3_BOX
extern FS *ufsp;
extern FS *ffsp;
#ifdef ESP8266
const int preallocateBufferSize = 5*1024;
const int preallocateCodecSize = 29192; // MP3 codec max mem needed
#endif // ESP8266
#ifdef ESP32
const int preallocateBufferSize = 16*1024;
const int preallocateCodecSize = 29192; // MP3 codec max mem needed
//const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed
#endif // ESP32
enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
#ifdef ESP8266
#define I2S_MCLK_MULTIPLE_128 0
#endif
void sayTime(int hour, int minutes);
void Cmd_MicRec(void);
void Cmd_wav2mp3(void);
void Cmd_Time(void);
#ifdef USE_I2S_RTTTL
void Rtttl(char *buffer);
void Cmd_I2SRtttl(void);
#endif
void copy_micpars(uint32_t port) {
audio_i2s.mic_mclk = audio_i2s.mclk;
audio_i2s.mic_bclk = audio_i2s.bclk;
audio_i2s.mic_ws = audio_i2s.ws;
audio_i2s.mic_dout = audio_i2s.dout;
audio_i2s.mic_din = audio_i2s.din;
audio_i2s.mic_port = (i2s_port_t)port;
}
int32_t I2S_Init_0(void) {
audio_i2s.i2s_port = (i2s_port_t)0;
audio_i2s.mic_port = (i2s_port_t)0;
#if USE_I2S_EXTERNAL_DAC
// use i2s
#if (defined(USE_I2S_NO_DAC) && defined(DAC_IIS_DOUT)) || (defined(DAC_IIS_BCK) && defined(DAC_IIS_WS) && defined(DAC_IIS_DOUT))
audio_i2s.i2s_port = (i2s_port_t)0;
#ifdef USE_I2S_NO_DAC
audio_i2s.out = new AudioOutputI2SNoDAC();
#else
audio_i2s.out = new AudioOutputI2S();
#endif
audio_i2s.bclk = DAC_IIS_BCK;
audio_i2s.ws = DAC_IIS_WS;
audio_i2s.dout = DAC_IIS_DOUT;
audio_i2s.din = DAC_IIS_DIN;
copy_micpars(0);
#else
#ifdef USE_I2S_NO_DAC
if (PinUsed(GPIO_I2S_DOUT)) {
#else
if (PinUsed(GPIO_I2S_BCLK) && PinUsed(GPIO_I2S_WS) && PinUsed(GPIO_I2S_DOUT)) {
#endif // USE_I2S_NO_DAC
audio_i2s.i2s_port = (i2s_port_t)0;
#ifdef USE_I2S_NO_DAC
audio_i2s.out = new AudioOutputI2SNoDAC();
#else
//audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port);
audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000);
#endif // USE_I2S_NO_DAC
audio_i2s.mclk = Pin(GPIO_I2S_MCLK);
audio_i2s.bclk = Pin(GPIO_I2S_BCLK);
audio_i2s.ws = Pin(GPIO_I2S_WS);
audio_i2s.dout = Pin(GPIO_I2S_DOUT);
audio_i2s.din = Pin(GPIO_I2S_DIN);
copy_micpars(0);
// check if 2 ports used, use second for micro
if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DIN, 1)) {
audio_i2s.mic_bclk = -1;
audio_i2s.mic_bclk = Pin(GPIO_I2S_BCLK, 1);
audio_i2s.mic_ws = Pin(GPIO_I2S_WS, 1);
audio_i2s.mic_dout = -1;
audio_i2s.mic_din = Pin(GPIO_I2S_DIN, 1);
audio_i2s.mic_port = (i2s_port_t)1;
}
} else if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DOUT, 1)) {
audio_i2s.i2s_port = (i2s_port_t)1;
#ifdef USE_I2S_NO_DAC
audio_i2s.out = new AudioOutputI2SNoDAC();
#else
//audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port);
audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000);
#endif // USE_I2S_NO_DAC
audio_i2s.mclk = Pin(GPIO_I2S_MCLK, 1);
audio_i2s.bclk = Pin(GPIO_I2S_BCLK, 1);
audio_i2s.ws = Pin(GPIO_I2S_WS, 1);
audio_i2s.dout = Pin(GPIO_I2S_DOUT, 1);
audio_i2s.din = Pin(GPIO_I2S_DIN, 1);
copy_micpars(1);
} else {
return -1;
}
#ifdef ESP8266
// esp8266 have fixed pins
if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout != 3)) {
return -2;
}
#endif // ESP8266
#endif // defined(DAC_IIS_BCK)
audio_i2s.out->SetPinout(audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din);
AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: port=%d, bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.i2s_port, audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din);
if (audio_i2s.mic_port != 0) {
AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S mic: port=%d, bclk=%d, ws=%d, din=%d"), audio_i2s.mic_port, audio_i2s.mic_bclk, audio_i2s.mic_ws, audio_i2s.mic_din);
}
#ifdef USE_I2S_LSB
audio_i2s.out->SetLsbJustified(true);
#endif // Allow supporting LSBJ chips, e.g. TM8211/PT8211
#else
#ifdef USE_I2S_NO_DAC
audio_i2s.out = new AudioOutputI2SNoDAC();
#else
audio_i2s.out = new AudioOutputI2S(0, 1); // Internal DAC port 0
#endif // USE_I2S_NO_DAC
#endif // USE_I2S_EXTERNAL_DAC
audio_i2s.mode = MODE_SPK;
return 0;
}
void I2S_Init(void) {
if (I2S_Init_0()) {
return;
}
#if defined(ESP32) && defined(ESP32S3_BOX)
S3boxInit();
#endif
#ifdef USE_W8960
W8960_Init1();
#endif
audio_i2s.is2_volume = 10;
audio_i2s.out->SetGain(((float)audio_i2s.is2_volume / 100.0) * 4.0);
audio_i2s.out->begin();
audio_i2s.out->stop();
audio_i2s.mp3ram = nullptr;
#ifdef ESP32
if (UsePSRAM()) {
audio_i2s.mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}
#ifdef USE_I2S_WEBRADIO
if (UsePSRAM()) {
audio_i2s.preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
audio_i2s.preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
} else {
audio_i2s.preallocateBuffer = malloc(preallocateBufferSize);
audio_i2s.preallocateCodec = malloc(preallocateCodecSize);
}
if (!audio_i2s.preallocateBuffer || !audio_i2s.preallocateCodec) {
//Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize);
}
#endif // USE_I2S_WEBRADIO
audio_i2s.mic_channels = MIC_CHANNELS;
audio_i2s.mic_rate = MICSRATE;
#ifdef USE_I2S_COMMON_IO
i2s_set_clk(audio_i2s.mic_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
#endif
#endif // ESP32
}
#ifdef ESP32
void mp3_task(void *arg) {
while (1) {
while (audio_i2s.mp3->isRunning()) {
if (!audio_i2s.mp3->loop()) {
audio_i2s.mp3->stop();
mp3_delete();
audio_i2s.out->stop();
if (audio_i2s.mp3_task_h) {
vTaskDelete(audio_i2s.mp3_task_h);
audio_i2s.mp3_task_h = 0;
}
//mp3_task_h=nullptr;
}
delay(1);
}
}
}
#endif // ESP32
#ifdef USE_I2S_WEBRADIO
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) {
const char *ptr = reinterpret_cast<const char *>(cbData);
(void) isUnicode; // Punt this ball for now
(void) ptr;
if (strstr_P(type, PSTR("Title"))) {
strncpy(audio_i2s.wr_title, str, sizeof(audio_i2s.wr_title));
audio_i2s.wr_title[sizeof(audio_i2s.wr_title)-1] = 0;
//AddLog(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title);
} else {
// Who knows what to do? Not me!
}
}
void StatusCallback(void *cbData, int code, const char *string) {
const char *ptr = reinterpret_cast<const char *>(cbData);
(void) code;
(void) ptr;
//strncpy_P(status, string, sizeof(status)-1);
//status[sizeof(status)-1] = 0;
}
void Webradio(const char *url) {
if (audio_i2s.decoder || audio_i2s.mp3) return;
if (!audio_i2s.out) return;
AUDIO_PWR_ON
audio_i2s.ifile = new AudioFileSourceICYStream(url);
audio_i2s.ifile->RegisterMetadataCB(MDCallback, NULL);
audio_i2s.buff = new AudioFileSourceBuffer(audio_i2s.ifile, audio_i2s.preallocateBuffer, preallocateBufferSize);
audio_i2s.buff->RegisterStatusCB(StatusCallback, NULL);
audio_i2s.decoder = new AudioGeneratorMP3(audio_i2s.preallocateCodec, preallocateCodecSize);
audio_i2s.decoder->RegisterStatusCB(StatusCallback, NULL);
audio_i2s.decoder->begin(audio_i2s.buff, audio_i2s.out);
if (!audio_i2s.decoder->isRunning()) {
// Serial.printf_P(PSTR("Can't connect to URL"));
StopPlaying();
// strcpy_P(status, PSTR("Unable to connect to URL"));
audio_i2s.retryms = millis() + 2000;
}
xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1);
}
void mp3_task2(void *arg){
while (1) {
if (audio_i2s.decoder && audio_i2s.decoder->isRunning()) {
if (!audio_i2s.decoder->loop()) {
StopPlaying();
//retryms = millis() + 2000;
}
delay(1);
}
}
}
void StopPlaying() {
if (audio_i2s.mp3_task_h) {
vTaskDelete(audio_i2s.mp3_task_h);
audio_i2s.mp3_task_h = nullptr;
}
if (audio_i2s.decoder) {
audio_i2s.decoder->stop();
delete audio_i2s.decoder;
audio_i2s.decoder = NULL;
}
if (audio_i2s.buff) {
audio_i2s.buff->close();
delete audio_i2s.buff;
audio_i2s.buff = NULL;
}
if (audio_i2s.ifile) {
audio_i2s.ifile->close();
delete audio_i2s.ifile;
audio_i2s.ifile = NULL;
}
AUDIO_PWR_OFF
}
void Cmd_WebRadio(void) {
if (!audio_i2s.out) return;
if (audio_i2s.decoder) {
StopPlaying();
}
if (XdrvMailbox.data_len > 0) {
Webradio(XdrvMailbox.data);
ResponseCmndChar(XdrvMailbox.data);
} else {
ResponseCmndChar_P(PSTR("Stopped"));
}
}
#ifdef USE_WEBSERVER
const char HTTP_WEBRADIO[] PROGMEM =
"{s}" "I2S_WR-Title" "{m}%s{e}";
void I2S_WR_Show(bool json) {
if (audio_i2s.decoder) {
if (json) {
ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), audio_i2s.wr_title);
} else {
WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title);
}
}
}
#endif // USE_WEBSERVER
#endif // USE_I2S_WEBRADIO
#ifdef ESP32
void Play_mp3(const char *path) {
#ifdef USE_UFILESYS
if (audio_i2s.decoder || audio_i2s.mp3) return;
if (!audio_i2s.out) return;
bool I2S_Task;
if (*path=='+') {
I2S_Task = true;
path++;
} else {
I2S_Task = false;
}
FS *mp3fsp = ufsp;
if (!strncmp(path, "/ffs", 4)) {
path += 4;
mp3fsp = ffsp;
}
if (!mp3fsp->exists(path)) {
return;
}
AUDIO_PWR_ON
audio_i2s.file = new AudioFileSourceFS(*mp3fsp, path);
audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file);
if (audio_i2s.mp3ram) {
audio_i2s.mp3 = new AudioGeneratorMP3(audio_i2s.mp3ram, preallocateCodecSize);
} else {
audio_i2s.mp3 = new AudioGeneratorMP3();
}
audio_i2s.mp3->begin(audio_i2s.id3, audio_i2s.out);
if (I2S_Task) {
xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1);
} else {
#define MP3_TIMEOUT 30000
uint32_t tout = millis();
while (audio_i2s.mp3->isRunning()) {
if (!audio_i2s.mp3->loop()) {
audio_i2s.mp3->stop();
break;
}
OsWatchLoop();
if (millis()-tout > MP3_TIMEOUT) {
break;
}
}
audio_i2s.out->stop();
mp3_delete();
}
#endif // USE_UFILESYS
}
void mp3_delete(void) {
delete audio_i2s.file;
delete audio_i2s.id3;
delete audio_i2s.mp3;
audio_i2s.mp3=nullptr;
AUDIO_PWR_OFF
}
#endif // ESP32
void Say(char *text) {
if (!audio_i2s.out) return;
AUDIO_PWR_ON
audio_i2s.out->begin();
ESP8266SAM *sam = new ESP8266SAM;
sam->Say(audio_i2s.out, text);
delete sam;
audio_i2s.out->stop();
AUDIO_PWR_OFF
}
const char kI2SAudio_Commands[] PROGMEM = "I2S|"
"Say|Gain"
#ifdef USE_I2S_SAY_TIME
"|Time"
#endif // USE_I2S_SAY_TIME
#ifdef USE_I2S_RTTTL
"|Rtttl"
#endif
#ifdef ESP32
"|Play"
#ifdef USE_I2S_WEBRADIO
"|WR"
#endif // USE_I2S_WEBRADIO
#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
"|REC"
"|MGain"
#if defined(USE_SHINE) && defined(MP3_MIC_STREAM)
"|STREAM"
#endif // MP3_MIC_STREAM
#ifdef I2S_BRIDGE
"|BRIDGE"
#endif // I2S_BRIDGE
#endif // USE_SHINE
#endif // ESP32
;
void (* const I2SAudio_Command[])(void) PROGMEM = {
&Cmd_Say, &Cmd_Gain
#ifdef USE_I2S_SAY_TIME
,&Cmd_Time
#endif // USE_I2S_SAY_TIME
#ifdef USE_I2S_RTTTL
,&Cmd_I2SRtttl
#endif
#ifdef ESP32
,&Cmd_Play
#ifdef USE_I2S_WEBRADIO
,&Cmd_WebRadio
#endif // USE_I2S_WEBRADIO
#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
,&Cmd_MicRec
,&Cmd_MicGain
#if defined(USE_SHINE) && defined(MP3_MIC_STREAM)
,&Cmd_MP3Stream
#endif // MP3_MIC_STREAM
#ifdef I2S_BRIDGE
,&Cmd_I2SBridge
#endif // I2S_BRIDGE
#endif // USE_SHINE
#endif // ESP32
};
void Cmd_Play(void) {
if (XdrvMailbox.data_len > 0) {
Play_mp3(XdrvMailbox.data);
}
ResponseCmndChar(XdrvMailbox.data);
}
void Cmd_Gain(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
if (audio_i2s.out) {
audio_i2s.is2_volume=XdrvMailbox.payload;
audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0);
}
}
ResponseCmndNumber(audio_i2s.is2_volume);
}
void Cmd_Say(void) {
if (XdrvMailbox.data_len > 0) {
Say(XdrvMailbox.data);
}
ResponseCmndChar(XdrvMailbox.data);
}
#ifdef USE_I2S_RTTTL
void Cmd_I2SRtttl(void) {
if (XdrvMailbox.data_len > 0) {
Rtttl(XdrvMailbox.data);
}
ResponseCmndChar(XdrvMailbox.data);
}
#endif
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
void i2s_mp3_loop(void);
void i2s_mp3_init(void);
void MP3ShowStream(void);
bool Xdrv42(uint32_t function) {
bool result = false;
switch (function) {
case FUNC_COMMAND:
result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command);
break;
case FUNC_INIT:
I2S_Init();
break;
case FUNC_WEB_ADD_MAIN_BUTTON:
//MP3ShowStream();
break;
case FUNC_LOOP:
#if defined(USE_SHINE) && defined(MP3_MIC_STREAM)
i2s_mp3_loop();
#endif
#if defined(I2S_BRIDGE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
i2s_bridge_loop();
#endif
break;
case FUNC_WEB_ADD_HANDLER:
#if defined(USE_SHINE) && defined(MP3_MIC_STREAM)
audio_i2s.stream_enable = 1;
i2s_mp3_init(1);
#endif
#if defined(I2S_BRIDGE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
I2SBridgeInit();
#endif
break;
#ifdef USE_WEBSERVER
#ifdef USE_I2S_WEBRADIO
case FUNC_WEB_SENSOR:
I2S_WR_Show(false);
break;
#endif // USE_I2S_WEBRADIO
#endif // USE_WEBSERVER
#ifdef USE_I2S_WEBRADIO
case FUNC_JSON_APPEND:
I2S_WR_Show(true);
break;
#endif // USE_I2S_WEBRADIO
case FUNC_ACTIVE:
result = true;
break;
}
return result;
}
#endif // USE_I2S_AUDIO
#endif // ESP8266 || (ESP_IDF_VERSION_MAJOR < 5)

View File

@ -1,335 +0,0 @@
/*
xdrv_42_i2s_audio.ino - Audio dac 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/>.
*/
#if defined(ESP32) && (ESP_IDF_VERSION_MAJOR < 5)
#if ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
uint32_t SpeakerMic(uint8_t spkr) {
esp_err_t err = ESP_OK;
#ifndef USE_I2S_COMMON_IO
if (audio_i2s.mode == spkr) {
return 0;
}
if (spkr == MODE_SPK) {
if (audio_i2s.i2s_port == audio_i2s.mic_port) {
if (audio_i2s.mode != MODE_SPK) {
i2s_driver_uninstall(audio_i2s.mic_port);
}
I2S_Init_0();
audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume - 2) / 100.0) * 4.0);
audio_i2s.out->stop();
}
audio_i2s.mode = spkr;
return 0;
}
// set micro
if (audio_i2s.i2s_port == audio_i2s.mic_port) {
// close audio out
if (audio_i2s.out) {
audio_i2s.out->stop();
delete audio_i2s.out;
audio_i2s.out = nullptr;
}
if (audio_i2s.mode == MODE_SPK) {
i2s_driver_uninstall(audio_i2s.i2s_port);
}
}
// config mic
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER),
.sample_rate = audio_i2s.mic_rate,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
//.dma_buf_count = 8,
.dma_buf_count = 2,
//.dma_buf_len = 128,
.dma_buf_len = 1024,
.use_apll = 0, // Use audio PLL
.tx_desc_auto_clear = true,
.fixed_mclk = 12000000,
.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // I2S_MCLK_MULTIPLE_128
.bits_per_chan = I2S_BITS_PER_CHAN_16BIT
};
if (audio_i2s.mic_channels == 1) {
i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
} else {
i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
}
#ifdef USE_I2S_MIC
// Mic L/R to GND
#ifdef USE_INMP441
i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX );
i2s_config.communication_format = I2S_COMM_FORMAT_I2S;
i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
#elif MIC_PDM
i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM);
#else
i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX);
i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
#endif
#endif
#ifdef ESP32S3_BOX
i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX);
i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
#endif
#ifdef USE_M5STACK_CORE2
i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM);
#endif
err += i2s_driver_install(audio_i2s.mic_port, &i2s_config, 0, NULL);
i2s_pin_config_t tx_pin_config;
tx_pin_config.mck_io_num = audio_i2s.mic_mclk;
tx_pin_config.bck_io_num = audio_i2s.mic_bclk;
tx_pin_config.ws_io_num = audio_i2s.mic_ws;
tx_pin_config.data_out_num = audio_i2s.mic_dout;
tx_pin_config.data_in_num = audio_i2s.mic_din;
err += i2s_set_pin(audio_i2s.mic_port, &tx_pin_config);
i2s_channel_t mode = I2S_CHANNEL_MONO;
if (audio_i2s.mic_channels > 1) {
mode = I2S_CHANNEL_STEREO;
}
err += i2s_set_clk(audio_i2s.mic_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, mode);
#endif // USE_I2S_COMMON_IO
audio_i2s.mode = spkr;
return err;
}
#ifdef USE_SHINE
#include <layer3.h>
#include <types.h>
// micro to mp3 file or stream
void mic_task(void *arg){
int8_t error = 0;
uint8_t *ucp;
int written;
shine_config_t config;
shine_t s = nullptr;
uint16_t samples_per_pass;
File mp3_out = (File)nullptr;
int16_t *buffer = nullptr;
uint16_t bytesize;
uint16_t bwritten;
uint32_t ctime;
if (!audio_i2s.use_stream) {
mp3_out = ufsp->open(audio_i2s.mic_path, "w");
if (!mp3_out) {
error = 1;
goto exit;
}
} else {
if (!audio_i2s.stream_active) {
error = 2;
audio_i2s.use_stream = 0;
goto exit;
}
audio_i2s.client.flush();
audio_i2s.client.setTimeout(3);
audio_i2s.client.print("HTTP/1.1 200 OK\r\n"
"Content-Type: audio/mpeg;\r\n\r\n");
// Webserver->send(200, "application/octet-stream", "");
//"Content-Type: audio/mp3;\r\n\r\n");
}
shine_set_config_mpeg_defaults(&config.mpeg);
if (audio_i2s.mic_channels == 1) {
config.mpeg.mode = MONO;
} else {
config.mpeg.mode = STEREO;
}
config.mpeg.bitr = 128;
config.wave.samplerate = audio_i2s.mic_rate;
config.wave.channels = (channels)audio_i2s.mic_channels;
if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) {
error = 3;
goto exit;
}
s = shine_initialise(&config);
if (!s) {
error = 4;
goto exit;
}
samples_per_pass = shine_samples_per_pass(s);
bytesize = samples_per_pass * 2 * audio_i2s.mic_channels;
buffer = (int16_t*)malloc(bytesize);
if (!buffer) {
error = 5;
goto exit;
}
ctime = TasmotaGlobal.uptime;
while (!audio_i2s.mic_stop) {
uint32_t bytes_read;
i2s_read(audio_i2s.mic_port, (char *)buffer, bytesize, &bytes_read, (100 / portTICK_RATE_MS));
if (audio_i2s.mic_gain > 1) {
// set gain
for (uint32_t cnt = 0; cnt < bytes_read / 2; cnt++) {
buffer[cnt] *= audio_i2s.mic_gain;
}
}
ucp = shine_encode_buffer_interleaved(s, buffer, &written);
if (!audio_i2s.use_stream) {
bwritten = mp3_out.write(ucp, written);
if (bwritten != written) {
break;
}
} else {
audio_i2s.client.write((const char*)ucp, written);
if (!audio_i2s.client.connected()) {
break;
}
}
audio_i2s.recdur = TasmotaGlobal.uptime - ctime;
}
ucp = shine_flush(s, &written);
if (!audio_i2s.use_stream) {
mp3_out.write(ucp, written);
} else {
audio_i2s.client.write((const char*)ucp, written);
}
exit:
if (s) {
shine_close(s);
}
if (mp3_out) {
mp3_out.close();
}
if (buffer) {
free(buffer);
}
if (audio_i2s.use_stream) {
audio_i2s.client.stop();
}
SpeakerMic(MODE_SPK);
audio_i2s.mic_stop = 0;
audio_i2s.mic_error = error;
AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error);
audio_i2s.mic_task_h = 0;
audio_i2s.recdur = 0;
audio_i2s.stream_active = 0;
vTaskDelete(NULL);
}
int32_t i2s_record_shine(char *path) {
esp_err_t err = ESP_OK;
if (audio_i2s.mic_port == 0) {
if (audio_i2s.decoder || audio_i2s.mp3) return 0;
}
err = SpeakerMic(MODE_MIC);
if (err) {
if (audio_i2s.mic_port == 0) {
SpeakerMic(MODE_SPK);
}
AddLog(LOG_LEVEL_INFO, PSTR("mic init error: %d"), err);
return err;
}
strlcpy(audio_i2s.mic_path, path, sizeof(audio_i2s.mic_path));
audio_i2s.mic_stop = 0;
uint32_t stack = 4096;
audio_i2s.use_stream = !strcmp(audio_i2s.mic_path, "stream.mp3");
if (audio_i2s.use_stream) {
stack = 8000;
}
err = xTaskCreatePinnedToCore(mic_task, "MIC", stack, NULL, 3, &audio_i2s.mic_task_h, 1);
return err;
}
void Cmd_MicRec(void) {
if (XdrvMailbox.data_len > 0) {
if (!strncmp(XdrvMailbox.data, "-?", 2)) {
Response_P("{\"I2SREC-duration\":%d}", audio_i2s.recdur);
} else {
i2s_record_shine(XdrvMailbox.data);
ResponseCmndChar(XdrvMailbox.data);
}
} else {
if (audio_i2s.mic_task_h) {
// stop task
audio_i2s.mic_stop = 1;
while (audio_i2s.mic_stop) {
delay(1);
}
ResponseCmndChar_P(PSTR("Stopped"));
}
}
}
#endif // USE_SHINE
// mic gain in factor not percent
void Cmd_MicGain(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 256)) {
audio_i2s.mic_gain = XdrvMailbox.payload;
}
ResponseCmndNumber(audio_i2s.mic_gain);
}
#endif // USE_I2S_AUDIO
#endif // ESP32 && (ESP_IDF_VERSION_MAJOR < 5)

View File

@ -1,64 +0,0 @@
#if defined(ESP32) && (ESP_IDF_VERSION_MAJOR < 5)
#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
#ifdef MP3_MIC_STREAM
#ifndef MP3_STREAM_PORT
#define MP3_STREAM_PORT 81
#endif
void Stream_mp3(void) {
if (!audio_i2s.stream_enable) {
return;
}
if (audio_i2s.stream_active) {
return;
}
AddLog(LOG_LEVEL_INFO, PSTR("I2S: Handle mp3server"));
audio_i2s.stream_active = 1;
audio_i2s.client = audio_i2s.MP3Server->client();
AddLog(LOG_LEVEL_INFO, PSTR("I2S: Create client"));
i2s_record_shine((char*)"stream.mp3");
}
void i2s_mp3_loop(void) {
if (audio_i2s.MP3Server) {
audio_i2s.MP3Server->handleClient();
}
}
void i2s_mp3_init(uint32_t on) {
if (on) {
if (!audio_i2s.MP3Server) {
audio_i2s.MP3Server = new ESP8266WebServer(MP3_STREAM_PORT);
audio_i2s.MP3Server->on(PSTR("/stream.mp3"), Stream_mp3);
audio_i2s.MP3Server->on(PSTR("/stream.m3a"), Stream_mp3);
audio_i2s.MP3Server->begin();
AddLog(LOG_LEVEL_INFO, PSTR("MP3: server created on port: %d "), MP3_STREAM_PORT);
}
} else {
if (audio_i2s.MP3Server) {
audio_i2s.MP3Server->stop();
delete audio_i2s.MP3Server;
audio_i2s.MP3Server = nullptr;
AddLog(LOG_LEVEL_INFO, PSTR("MP3: server deleted"));
}
}
}
void Cmd_MP3Stream(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
audio_i2s.stream_enable = XdrvMailbox.payload;
}
i2s_mp3_init(audio_i2s.stream_enable);
ResponseCmndNumber(audio_i2s.stream_enable);
}
#endif // MP3_MIC_STREAM
#endif // USE_SHINE
#endif // defined(ESP32) && (ESP_IDF_VERSION_MAJOR < 5)

View File

@ -249,6 +249,14 @@ extern "C" {
be_return_nil(vm);
}
// Berry: `webserver.content_close() -> nil`
//
int32_t w_webserver_content_close(struct bvm *vm);
int32_t w_webserver_content_close(struct bvm *vm) {
WSContentEnd();
be_return_nil(vm);
}
// Berry: `webserver.content_button([button:int]) -> nil`
// Default button is BUTTON_MAIN
//

View File

@ -899,7 +899,7 @@ bool Xdrv52(uint32_t function)
#ifdef USE_ETHERNET
network_up = network_up || EthernetHasIP();
#endif
if (network_up) { // if network is already up, send a synthetic event to trigger web handlers
if (network_up && (Webserver != NULL)) { // if network is already up, send a synthetic event to trigger web handlers
callBerryEventDispatcher(PSTR("web_add_handler"), nullptr, 0, nullptr);
berry.web_add_handler_done = true;
}

View File

@ -1,7 +1,7 @@
/*
xsns_100_ina3221.ino - INA3221 3-channels Current Sensor support for Tasmota
Copyright (C) 2021 Barbudor and Theo Arends + fb-pilot 2024-3-7
Copyright (C) 2021 Barbudor and Theo Arends + fb-pilot 2024-4-4, 2024-4-20
Based on Barbudor's CircuitPython_INA3221
This program is free software: you can redistribute it and/or modify
@ -38,32 +38,57 @@
* #define INA3221_ADDRESS1 0x41
* That would leave 0x40 and 0x43 for other devices
* By defining INA3221_CALC_CHARGE_AH INA3221_CALC_ENERGY_WH the driver adds a estimation of
* energies in Ah and Wh to the output, To reset the energie calculation disable tge
* according INA3221 chanel by setting the shunt to 0.0 and enable it again
* For example :
* sensor100 1,0.0,0.1,0.2 and sensor100 1,0.1,0.1,0.2 will reset channel 1
* Nevertheless, hte driver tries to identifiy if the chip as an address is a IN3221
* energies in Ah and Wh to the output, To reset the energie calculation disable tge
* according INA3221 chanel by setting the shunt to 0.0 and enable it again
* For example :
* sensor100 1,0,0,0 (or sensor100 1,,,) and sensor100 1,0.1,0.1,0.2 will reset channel 1
* By defining INA3221_SUPPLY_SIDE the driver adds the measured Shunt Voltage to the Bus Voltage
* for the cannel with a negativ shunt (shunt <0) thus showing the values of the supply side (IN+)
* otherwise (shunt >0) the load-side (IN-) is shown.
* e.g. sensor100 1,-0.1,0.1,-0.2
* additionaly the bits set (bit 0,1,2) enable the scanning of the voltage in the according channel
* e.g. INA3221_SUPPLY_SIDE = 0x0005 enables enables voltage measurment of channel 1 and 3 for the device 1
* By defining INA3221_CYCLE_COUNT xx ... the device is scanned all xx * 250ms range 1 .. 127
* By defining USE_DEEPSLEEP the device is configured to Power down (reduces the supply current 1mA --> 15µA )
* Nevertheless, the driver tries to identifiy if the chip as an address is a IN3221
\*********************************************************************************************/
// setup of INA3221 config
#define XSNS_100 100
#define XI2C_72 72 // See I2CDEVICES.md
// setup of INA3221 config
#ifndef INA3221_CONFIG_INIT
#define INA3221_CONFIG_INIT INA3221_AVERAGING_16_SAMPLES |\
INA3221_VBUS_CONV_TIME_1MS |\
INA3221_SHUNT_CONV_TIME_1MS
// that results in a complete conversions sequence in 6,6 ms and a slope time of 105,6 ms
#endif // end of setup of INA3221 config
#if !defined(INA3221_MAX_COUNT)
#define INA3221_MAX_COUNT 4
#elif (INA3221_MAX_COUNT > 4)
#undef INA3221_MAX_COUNT
#define INA3221_MAX_COUNT 4
#warning **** INA3221 bad INA3221_MAX_COUNT ****
#warning **** has to be 1 to 4 ... set to 4 ****
#elif (INA3221_MAX_COUNT < 1)
#undef INA3221_MAX_COUNT
#define INA3221_MAX_COUNT 1
#warning **** INA3221 bad INA3221_MAX_COUNT ****
#warning **** has to be 1 to 4 ... set to 1 ****
#endif
// end of setup of INA3221 config
#define XSNS_100 100
#define XI2C_72 72 // See I2CDEVICES.md
#ifndef INA3221_MAX_COUNT
#define INA3221_MAX_COUNT 4
#endif
#if (INA3221_MAX_COUNT > 4)
#error "**** INA3221_MAX_COUNT can't be greater than 4 ****"
#if !defined(INA3221_CYCLE_COUNT)
#define INA3221_CYCLE_COUNT 1
#elif (INA3221_CYCLE_COUNT < 1)
#undef INA3221_CYCLE_COUNT
#define INA3221_CYCLE_COUNT 1
#warning **** INA3221 bad INA3221_CYCLE_COUNT ****
#warning **** must be 1 to 127 ... set to 1 ****
#elif (INA3221_CYCLE_COUNT > 127)
#undef INA3221_CYCLE_COUNT
#define INA3221_CYCLE_COUNT 127
#warning **** INA3221 bad INA3221_CYCLE_COUNT ****
#warning **** must be 1 to 127 ... set to 127 ****
#endif
#ifndef INA3221_ADDRESS1
@ -77,10 +102,10 @@
#error "**** INA3221 bad combination for ADDRESS1 and MAX_COUNT ****"
#endif
#define INA3221_NB_CHAN (3)
#if (INA3221_NB_CHAN>4)
#error "**** INA3221 too manny channels ****"
#endif
// Config register - ch : 0..2
#define INA3221_REG_CONFIG (0x00)
@ -128,6 +153,7 @@
#define INA3221_MODE_BUS_VOLTAGE_CONTINUOUS (0x0006)
#define INA3221_MODE_SHUNT_AND_BUS_CONTINOUS (0x0007) // default
// Other registers - ch = 0..2
#define INA3221_REG_SHUNT_VOLTAGE_CH(ch) (0x01+((ch)<<1))
#define INA3221_REG_BUS_VOLTAGE_CH(ch) (0x02+((ch)<<1))
@ -146,7 +172,8 @@
#define INA3221_WARNING_FLAG_CH(ch) (0x0020>>((ch)-1))
#define INA3221_POWER_ALERT_FLAG (0x0004)
#define INA3221_TIMING_ALERT_FLAG (0x0002)
#define INA3221_CONV_READY_FLAG (0x0001)
#define INA3221_CONV_READY_FLAG (0x0001) // The conversion bit is set after all conversions are
// complete. Conversion ready clearsReading the Mask/Enable register.
// Other registers
#define INA3221_REG_POWER_VALID_UPPER_LIMIT (0x10)
@ -159,11 +186,42 @@
#define INA3221_DIE_ID (0x3220)
// General constants
#define INA3221C_BUS_ADC_LSB (0.008) // VBus ADC LSB is 8mV
#define INA3221C_SHUNT_ADC_LSB (0.00004) // VShunt ADC LSB is 40µV
#define INA3221_DEFAULT_SHUNT_RESISTOR (0.1)
#define INA3221C_BUS_ADC_LSB (0.008f) // VBus ADC LSB is 8mV
#define INA3221C_SHUNT_ADC_LSB (0.00004f) // VShunt ADC LSB is 40µV
#define INA3221_DEFAULT_SHUNT_RESISTOR (0.1f)
#define INA3221_ENERGY_FACTOR (1.0/(3600.0*1000.0)) // reading values all xx ms
#define INA3221_ENERGY_FACTOR (1.0f/(3600.0f*1000.0f)) // reading values all xx ms
#ifdef INA3221_SUPPLY_SIDE
#define INA3221_ENABLE_CHAN(device) (((INA3221_SUPPLY_SIDE >> ((device)*4)) & (0xF >> (4-INA3221_NB_CHAN))) << 4)
#endif
/* show final Defines :
#ifdef INA3221_CALC_CHARGE_AH
#define BF_DEF_CHARGE_AH defined
#else
#define BF_DEF_CHARGE_AH not defined
#endif
#ifdef INA3221_CALC_ENERGY_WH
#define BF_DEF_ENERGY_WH defined
#else
#define BF_DEF_ENERGY_WH not defined
#endif
#ifdef INA3221_SUPPLY_SIDE
#define BF_DEF_SUPPLY_SIDE defined ... INA3221_SUPPLY_SIDE
#else
#define BF_DEF_SUPPLY_SIDE not defined
#endif
#pragma message ("\n=========================>"\
"\n Sensor XSNS_ = " BF_TEXT(XSNS_100)" ... I2C-driver = " BF_TEXT(XI2C_72) \
"\n first Address = " BF_TEXT(INA3221_ADDRESS1)\
"\n init-config = " BF_TEXT(INA3221_CONFIG_INIT)\
"\n INA3221_MAX_COUNT = " BF_TEXT(INA3221_MAX_COUNT)\
"\n INA3221_CYCLE_COUNT = " BF_TEXT(INA3221_CYCLE_COUNT)\
"\n CHARGE_AH = " BF_TEXT(BF_DEF_CHARGE_AH)" ... ENERGY_WH = " BF_TEXT(BF_DEF_ENERGY_WH)\
"\n SUPPLY_SIDE = " BF_TEXT(BF_DEF_SUPPLY_SIDE) \
"\n=========================>\n\n")
// end show final Defines */
#ifdef DEBUG_TASMOTA_SENSOR
// temporary strings for floating point in debug messages
@ -199,11 +257,52 @@ struct INA3221_Data {
struct INA3221_Data *Ina3221Data = nullptr;
uint8_t Ina3221count = 0;
static uint8_t _ina3221_current_device = 0;
static int8_t _ina3221_current_device = 0;
#define D_INA3221 "INA3221"
const char INA3221_TYPE[] = D_INA3221;
#ifdef INA3221_SUPPLY_SIDE
bool Ina3221WriteConfig(uint8_t device)
{
uint16_t config = 0x8000;
for (uint32_t ch = 0; ch < INA3221_NB_CHAN; ch++) {
if ((Ina3221Data[device].enabled_chan & (1 << ch)) || (Ina3221Data[device].enabled_chan & (1 << (ch+4)))){
config |= INA3221_ENABLE_CH(ch);
}
}
config |= INA3221_CONFIG_INIT;
// bf.. INA3221_MODE_SHUNT_AND_BUS_CONTINOUS ableiten aus Ina3221Data[device].enabled_chan device !!
if (Ina3221Data[device].enabled_chan < 0x0F){
config |= INA3221_MODE_SHUNT_VOLTAGE_CONTINUOUS;
}
else if (Ina3221Data[device].enabled_chan == (Ina3221Data[device].enabled_chan & 0xF0) ){
config |= INA3221_MODE_BUS_VOLTAGE_CONTINUOUS;
}
else{
config |= INA3221_MODE_SHUNT_AND_BUS_CONTINOUS;
}
/*
#define INA3221_ENABLE_MASK (0x7000)
#define INA3221_ENABLE_CH(ch) (0x4000>>(ch)) // default: set
#define INA3221_MODE_SHUNT_VOLTAGE_CONTINUOUS (0x0005)
#define INA3221_MODE_BUS_VOLTAGE_CONTINUOUS (0x0006)
#define INA3221_MODE_SHUNT_AND_BUS_CONTINOUS (0x0007) // default
*/
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(PSTR(D_INA3221 ":WriteConfig: device=%d, addr:0x%02X, onfiguration register=0x%04X"),device+1, Ina3221Data[device].i2caddr, config);
#endif
// Set Config register
if (!I2cWrite16(Ina3221Data[device].i2caddr, INA3221_REG_CONFIG, config))
return false;
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ":WriteConfig: device=%d, addr:0x%02X, onfiguration register=0x%04X"),device+1, Ina3221Data[device].i2caddr, config);
return true;
}
#endif
bool Ina3221SetConfig(uint8_t addr)
{
// check if device is a INA3221
@ -216,8 +315,7 @@ bool Ina3221SetConfig(uint8_t addr)
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_INA3221 ":SetConfig: manId=0x%04X, dieId=0x%04X"), manufacturer_id, die_id);
return false;
}
// write default configuration
#ifndef INA3221_SUPPLY_SIDE
uint16_t config = INA3221_ENABLE_MASK |
INA3221_CONFIG_INIT |
INA3221_MODE_SHUNT_AND_BUS_CONTINOUS;
@ -225,6 +323,7 @@ bool Ina3221SetConfig(uint8_t addr)
// Set Config register
if (!I2cWrite16(addr, INA3221_REG_CONFIG, config))
return false;
#endif
return true;
}
@ -247,56 +346,105 @@ bool Ina3221PowerDown(uint8_t device)
void Ina3221SetShunt(uint8_t device, uint8_t channel, float shunt)
{
Ina3221Data[device].chan[channel].shunt = shunt;
if (shunt > 0.0){
#ifdef INA3221_SUPPLY_SIDE
if (fabs(shunt) > 0.0f){
#else
if (shunt > 0.0f){
#endif
#if (defined(INA3221_CALC_CHARGE_AH) || defined(INA3221_CALC_ENERGY_WH))
if (!(Ina3221Data[device].enabled_chan & (1<<channel))){
#ifdef INA3221_CALC_CHARGE_AH
Ina3221Data[device].chan[channel].charge_ah = 0.0f;
#endif
#ifdef INA3221_CALC_ENERGY_WH
if ((Ina3221Data[device].enabled_chan & (1<<(channel+4)))){
Ina3221Data[device].chan[channel].energy_wh = 0.0f;
}
#endif
}
#endif
Ina3221Data[device].enabled_chan |= (1<<channel);
}else{
}
else{
Ina3221Data[device].enabled_chan &= ~(1<<channel);
Ina3221Data[device].chan[channel].current = NAN;
#ifdef INA3221_CALC_CHARGE_AH
Ina3221Data[device].chan[channel].charge_ah =0.0;
Ina3221Data[device].chan[channel].charge_ah = NAN;
#endif
#ifdef INA3221_CALC_ENERGY_WH
Ina3221Data[device].chan[channel].energy_wh =0.0;
Ina3221Data[device].chan[channel].energy_wh = NAN;
#endif
}
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ":enabled: device/channel=%d/%d,shunt=%-3_f enabled=0x%02X"), device+1,channel,&shunt,Ina3221Data[device].enabled_chan);
}
bool Ina3221Read(uint8_t device, uint8_t channel)
{
// if (channel>INA3221_NB_CHAN)
// return false;
uint8_t addr = Ina3221Data[device].i2caddr;
int16_t bus_voltage, shunt_voltage;
struct INA3221_Channel_Data *pChannel = &Ina3221Data[device].chan[channel];
bus_voltage = I2cReadS16(addr, INA3221_REG_BUS_VOLTAGE_CH(channel));
DEBUG_SENSOR_LOG(D_INA3221 ":GetBusVoltage: RegVBus[%d:%d](0x%02X) = 0x%04X = %d", device, channel, INA3221_REG_BUS_VOLTAGE_CH(channel), bus_voltage, bus_voltage);
// Convert to VBus voltage in V
pChannel->voltage = INA3221C_BUS_ADC_LSB * (float)(bus_voltage >> 3);
if (pChannel->shunt > 0.0) {
shunt_voltage = I2cReadS16(addr, INA3221_REG_SHUNT_VOLTAGE_CH(channel));
#ifdef INA3221_SUPPLY_SIDE
if (Ina3221Data[device].enabled_chan & (0x01 << (channel+4))){
#endif
bus_voltage = I2cReadS16(addr, INA3221_REG_BUS_VOLTAGE_CH(channel));
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 ":GetBusVoltage: RegVBus[%d:%d](0x%02X) = 0x%04X = %d", device, channel, INA3221_REG_BUS_VOLTAGE_CH(channel), bus_voltage, bus_voltage);
#endif
// Convert to VBus voltage in V
pChannel->voltage = INA3221C_BUS_ADC_LSB * (float)(bus_voltage >> 3);
#ifdef INA3221_SUPPLY_SIDE
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ":GetBusVoltage: RegVBus[0x%02X:%d:%d](0x%02X) = 0x%04X = %d voltage=%5_f"),addr ,device, channel, INA3221_REG_BUS_VOLTAGE_CH(channel), bus_voltage, bus_voltage, &pChannel->voltage);
}
else{
pChannel->voltage = NAN;
}
if ((fabs(pChannel->shunt)) > 0.0f) {
shunt_voltage = I2cReadS16(addr, INA3221_REG_SHUNT_VOLTAGE_CH(channel));
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 ":GetShuntVoltage: RegSh[%d:%d](0x%02X) = 0x%04X = %d", device, channel, INA3221_REG_SHUNT_VOLTAGE_CH(channel), shunt_voltage, shunt_voltage);
#endif
// convert to shunt voltage in V
if (pChannel->shunt < 0){
pChannel->voltage += INA3221C_SHUNT_ADC_LSB * (float)(shunt_voltage >> 3);
}
pChannel->current = INA3221C_SHUNT_ADC_LSB * (float)(shunt_voltage >> 3) / (fabs(pChannel->shunt));
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ":GetShuntVoltage: RegSh[%d:%d](0x%02X) = 0x%04X = %d current=%5_f"),device, channel, INA3221_REG_SHUNT_VOLTAGE_CH(channel), shunt_voltage, shunt_voltage, &pChannel->current);
#else
if (pChannel->shunt > 0.0f) {
shunt_voltage = I2cReadS16(addr, INA3221_REG_SHUNT_VOLTAGE_CH(channel));
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 ":GetShuntVoltage: RegSh[%d:%d](0x%02X) = 0x%04X = %d", device, channel, INA3221_REG_SHUNT_VOLTAGE_CH(channel), shunt_voltage, shunt_voltage);
#endif
// convert to shunt voltage in V
pChannel->current = INA3221C_SHUNT_ADC_LSB * (float)(shunt_voltage >> 3) / pChannel->shunt;
#endif
#ifdef INA3221_CALC_CHARGE_AH
pChannel->charge_ah += (pChannel->current * (float)INA3221_delta_ms * INA3221_ENERGY_FACTOR);
#endif
#ifdef INA3221_CALC_ENERGY_WH
pChannel->energy_wh += (pChannel->current * pChannel->voltage * (float)INA3221_delta_ms * INA3221_ENERGY_FACTOR);
#endif
} else {
pChannel->current = INFINITY;
}
else {
pChannel->current = INFINITY;
#ifdef INA3221_CALC_CHARGE_AH
pChannel->charge_ah = INFINITY;
#endif
#ifdef INA3221_CALC_ENERGY_WH
pChannel->energy_wh = INFINITY;
#endif
#endif
}
#ifdef DEBUG_TASMOTA_SENSOR
dtostrfd(pChannel->voltage,5,_ina3221_dbg1);
dtostrfd(pChannel->current,5,_ina3221_dbg2);
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 ":Read[%d:%d]: V=%sV, I=%sA", device, channel, _ina3221_dbg1, _ina3221_dbg2);
#endif
#endif
return true;
}
@ -308,28 +456,32 @@ bool Ina3221Read(uint8_t device, uint8_t channel)
bool Ina3221CmndSensor(void)
{
int argc = ArgC();
if(argc != 1 && argc != 4) {
AddLog(LOG_LEVEL_DEBUG, PSTR(D_INA3221 ": Not enough arguments (1 or 4)"));
if(argc != 1 && argc != (INA3221_NB_CHAN+1)) {
AddLog(LOG_LEVEL_INFO, PSTR(D_INA3221 ": Not enough arguments (1 or %d)"),(INA3221_NB_CHAN+1));
return false;
}
char argument[XdrvMailbox.data_len+FLOATSZ];
uint32_t device = atoi(ArgV(argument,1)) -1;
if (device >= INA3221_MAX_COUNT || !Ina3221Data[device].i2caddr) {
DEBUG_SENSOR_LOG(D_INA3221 ":Sensor: invalid device %d", device+1);
AddLog(LOG_LEVEL_INFO, PSTR(D_INA3221 ":invalid device %d"),device+1);
return false;
}
if (argc > 1) {
for (int channel = 0 ; channel < INA3221_NB_CHAN ; channel++) {
float shunt = CharToFloat(ArgV(argument,2+channel));
Ina3221SetShunt(device, channel, shunt);
}
if (!Ina3221WriteConfig(device)){
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(D_INA3221 "error write configuration %d", device+1);
#endif
return false;
}
}
Response_P(INA3221_SENSORCMND_START, XSNS_100, device +1, Ina3221Data[device].i2caddr);
for (int channel = 0 ; channel < INA3221_NB_CHAN ; channel++ ) {
dtostrfd(Ina3221Data[device].chan[channel].shunt,5,argument);
ResponseAppend_P(PSTR("%s%c"), argument, ((channel < (INA3221_NB_CHAN-1))?',':'\0'));
ResponseAppend_P(PSTR("%5_f%c"),&Ina3221Data[device].chan[channel].shunt , ((channel < (INA3221_NB_CHAN-1))?',':'\0'));
}
ResponseAppend_P(INA3221_SENSORCMND_END);
@ -342,23 +494,40 @@ void Ina3221Detect(void)
{
_ina3221_current_device = 0;
Ina3221count = 0;
// ????
for (uint32_t i = 0; i < INA3221_MAX_COUNT; i++) {
uint16_t addr = INA3221_ADDRESS(i);
if (!I2cSetDevice(addr)) { continue; }
if (!Ina3221Data) {
Ina3221Data = (struct INA3221_Data*)calloc(INA3221_MAX_COUNT,sizeof(struct INA3221_Data));
// bf ... calloc(INA3221_MAX_COUNT ... ??
Ina3221Data = (struct INA3221_Data*)calloc(INA3221_MAX_COUNT,sizeof(struct INA3221_Data));
if (!Ina3221Data) {
AddLog(LOG_LEVEL_ERROR,PSTR(D_INA3221 ": Mem allocation error"));
return;
}
}
if (Ina3221SetConfig(addr)) {
// bf.. Ina3221SetConfig(addr)) erweitern om device !!
if (Ina3221SetConfig(addr)) {
I2cSetActiveFound(addr, INA3221_TYPE);
Ina3221Data[Ina3221count].i2caddr = addr;
#ifdef INA3221_SUPPLY_SIDE
Ina3221Data[Ina3221count].enabled_chan = INA3221_ENABLE_CHAN(i);
if (!Ina3221WriteConfig(Ina3221count)){
Ina3221count++;
continue;
}
#else
Ina3221Data[Ina3221count].enabled_chan = 0;
Ina3221Data[Ina3221count].chan[0].shunt = \
Ina3221Data[Ina3221count].chan[1].shunt = \
Ina3221Data[Ina3221count].chan[2].shunt = 0.0;
#endif
for (uint32_t j = 0; j < INA3221_NB_CHAN; j++) {
Ina3221Data[Ina3221count].chan[j].shunt = 0.0f;
#ifdef INA3221_CALC_CHARGE_AH
Ina3221Data[Ina3221count].chan[j].charge_ah = 0.0f;
#endif
#ifdef INA3221_CALC_ENERGY_WH
Ina3221Data[Ina3221count].chan[j].energy_wh = 0.0f;
#endif
}
Ina3221count++;
}
}
@ -373,27 +542,35 @@ void Ina3221Detect(void)
void Ina3221Every250ms(void)
{
DEBUG_SENSOR_LOG(PSTR(D_INA3221 ": cur:%d, en:%d"), _ina3221_current_device, Ina3221Data[_ina3221_current_device].enabled_chan);
uint8_t enabled_chan = Ina3221Data[_ina3221_current_device].enabled_chan;
for (int chan = 0 ; enabled_chan ; chan++, enabled_chan>>=1) {
if (0x01 & enabled_chan)
Ina3221Read(_ina3221_current_device, chan);
}
if (++_ina3221_current_device < 0 ){
return;
}
// Conversion-ready flag. CVRF ?? ==> INA3221_CONV_READY_FLAG
#ifdef DEBUG_TASMOTA_SENSOR
DEBUG_SENSOR_LOG(PSTR(D_INA3221 ": cur:%d, en:%d"), _ina3221_current_device, Ina3221Data[(uint8_t) _ina3221_current_device].enabled_chan);
#endif
uint8_t enabled_chan = Ina3221Data[(uint8_t)(_ina3221_current_device)].enabled_chan;
if (++_ina3221_current_device >= INA3221_MAX_COUNT){
_ina3221_current_device = 0;
if (_ina3221_current_device >= INA3221_MAX_COUNT){
_ina3221_current_device = (-INA3221_CYCLE_COUNT);
#if defined(INA3221_CALC_CHARGE_AH) || defined(INA3221_CALC_ENERGY_WH)
INA3221_delta_ms = millis()-INA3221_last_millis;
INA3221_last_millis = millis();
#endif
}
}
else{
for (int chan = 0 ; enabled_chan ; chan++, enabled_chan>>=1, enabled_chan &= 0xF7) {
if (0x11 & enabled_chan)
Ina3221Read((uint8_t)(_ina3221_current_device), chan);
}
}
}
#ifdef USE_WEBSERVER
// {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#define INA3221_AL "<td style='text-align:right'>"
const char HTTP_SNS_INA3221_HEADER[] PROGMEM =
// "{s}" D_INA3221 "&nbsp;&nbsp;&nbsp;&nbsp;</th><td>&nbsp;</td><td style='text-align:right'>" D_VOLTAGE " </td><td>&nbsp;</td><td style='text-align:right'>" D_CURRENT " </td><td>&nbsp;</td><td style='text-align:right'>" D_POWERUSAGE
"{s}" D_INA3221 "&nbsp;&nbsp;&nbsp;&nbsp;</th>" INA3221_AL D_VOLTAGE " </td><td>&nbsp;</td>" INA3221_AL D_CURRENT " </td><td>&nbsp;</td>" INA3221_AL D_POWERUSAGE
#ifdef INA3221_CALC_CHARGE_AH
"</td><td>&nbsp;</td>" INA3221_AL D_CHARGE
@ -404,31 +581,31 @@ const char HTTP_SNS_INA3221_HEADER[] PROGMEM =
"{e}";
const char HTTP_SNS_INA3221_DATA[] PROGMEM =
// "{s}%s </th></th><td>&nbsp;</td><td style='text-align:right'>%s " D_UNIT_VOLT " </td><td>&nbsp;</td><td style='text-align:right'>%s " D_UNIT_AMPERE " </td><td>&nbsp;</td><td style='text-align:right'>%s " D_UNIT_WATT
"{s}%s </th></th>" INA3221_AL " %s " D_UNIT_VOLT " </td><td>&nbsp;</td>" INA3221_AL " %s " D_UNIT_AMPERE " </td><td>&nbsp;</td>" INA3221_AL " %s " D_UNIT_WATT
"{s}%s </th>" INA3221_AL " %*_f " D_UNIT_VOLT " </td><td>&nbsp;</td>" INA3221_AL " %*_f " D_UNIT_AMPERE " </td><td>&nbsp;</td>" INA3221_AL " %*_f " D_UNIT_WATT
#ifdef INA3221_CALC_CHARGE_AH
"</td><td>&nbsp;</td>" INA3221_AL " %s " D_UNIT_CHARGE
"</td><td>&nbsp;</td>" INA3221_AL " %*_f " D_UNIT_CHARGE
#endif
#ifdef INA3221_CALC_ENERGY_WH
"</td><td>&nbsp;</td>" INA3221_AL " %s " D_UNIT_WATTHOUR
"</td><td>&nbsp;</td>" INA3221_AL " %*_f " D_UNIT_WATTHOUR
#endif
"{e}";
#endif // USE_WEBSERVER
#endif // USE_WEBSERVER
void Ina3221Show(bool json)
{
char name[FLOATSZ];
char temp[FLOATSZ];
char voltage[3*FLOATSZ+3];
char current[3*FLOATSZ+3];
char power[3*FLOATSZ+3];
// char temp[FLOATSZ];
char voltage[INA3221_NB_CHAN*FLOATSZ+3];
char current[INA3221_NB_CHAN*FLOATSZ+3];
char power[INA3221_NB_CHAN*FLOATSZ+3];
#ifdef INA3221_CALC_CHARGE_AH
char charge_ah[3*FLOATSZ+3];
char charge_ah[INA3221_NB_CHAN*FLOATSZ+3];
#endif
#ifdef INA3221_CALC_ENERGY_WH
char energy_wh[3*FLOATSZ+3];
char energy_wh[INA3221_NB_CHAN*FLOATSZ+3];
#endif
float pw = 0.0f;
if (json) {
// data
for (int device=0 ; device < Ina3221count ; device++) {
@ -440,56 +617,23 @@ void Ina3221Show(bool json)
}
else{
snprintf_P(name, sizeof(name), PSTR("%s"), INA3221_TYPE);
voltage[0] = current[0] = power[0] =
#ifdef INA3221_CALC_CHARGE_AH
charge_ah[0] =
#endif
#ifdef INA3221_CALC_ENERGY_WH
energy_wh[0] =
#endif
'\0';
}
for (int chan=0 ; enabled_chan ; chan++, enabled_chan>>=1) {
if (0x01 & enabled_chan) {
dtostrfd(Ina3221Data[device].chan[chan].voltage, Settings->flag2.voltage_resolution, temp);
strncat(voltage, temp, sizeof(voltage));
dtostrfd(Ina3221Data[device].chan[chan].current, Settings->flag2.current_resolution, temp);
strncat(current, temp, sizeof(current));
dtostrfd(Ina3221Data[device].chan[chan].voltage * Ina3221Data[device].chan[chan].current, Settings->flag2.wattage_resolution, temp);
strncat(power, temp, sizeof(power));
int32_t count_v = 0, count_i = 0, count_p = 0, count_ah = 0, count_wh = 0;
for (int32_t chan=0 ; enabled_chan; chan++, enabled_chan>>=1, enabled_chan &= 0xF7) {
// if (0x11 & enabled_chan) {
count_v += ext_snprintf_P(&voltage[count_v], sizeof(voltage) - count_v, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.voltage_resolution, &Ina3221Data[device].chan[chan].voltage);
count_i += ext_snprintf_P(&current[count_i], sizeof(current) - count_i, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.current_resolution, &Ina3221Data[device].chan[chan].current);
pw = Ina3221Data[device].chan[chan].voltage * Ina3221Data[device].chan[chan].current;
count_p += ext_snprintf_P(&power[count_p], sizeof(power) - count_p, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.wattage_resolution, &pw);
#ifdef INA3221_CALC_CHARGE_AH
dtostrfd(Ina3221Data[device].chan[chan].charge_ah, Settings->flag2.energy_resolution, temp);
strncat(charge_ah, temp, sizeof(charge_ah));
count_ah += ext_snprintf_P(&charge_ah[count_ah], sizeof(charge_ah) - count_ah, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.energy_resolution, &Ina3221Data[device].chan[chan].charge_ah);
#endif
#ifdef INA3221_CALC_ENERGY_WH
dtostrfd(Ina3221Data[device].chan[chan].energy_wh, Settings->flag2.energy_resolution, temp);
strncat(energy_wh, temp, sizeof(energy_wh));
count_wh += ext_snprintf_P(&energy_wh[count_wh], sizeof(energy_wh) - count_wh, PSTR("%s%*_f"), (chan>0 ? ",":""), Settings->flag2.energy_resolution, &Ina3221Data[device].chan[chan].energy_wh);
#endif
} //if enabled
else {
strncat(voltage, "null", sizeof(voltage));
strncat(current, "null", sizeof(current));
strncat(power, "null", sizeof(power));
#ifdef INA3221_CALC_CHARGE_AH
strncat(charge_ah, "null", sizeof(charge_ah));
#endif
#ifdef INA3221_CALC_ENERGY_WH
strncat(energy_wh, "null", sizeof(energy_wh));
#endif
}
if (0xFE & enabled_chan) {
strncat(voltage, ",", sizeof(voltage));
strncat(current, ",", sizeof(current));
strncat(power, ",", sizeof(power));
#ifdef INA3221_CALC_CHARGE_AH
strncat(charge_ah, ",", sizeof(charge_ah));
#endif
#ifdef INA3221_CALC_ENERGY_WH
strncat(energy_wh, ",", sizeof(energy_wh));
#endif
}
// } //if enabled
} // for channel
ResponseAppend_P(PSTR(",\"%s\":{\"Id\":\"0x%02x\",\"" D_JSON_VOLTAGE "\":[%s],\"" D_JSON_CURRENT "\":[%s],\"" D_JSON_POWERUSAGE "\":[%s]"
ResponseAppend_P(PSTR(",\"%s\":{\"Id\":\"0x%02x\",\"" D_JSON_VOLTAGE "\":[%s],\"" D_JSON_CURRENT "\":[%s],\"" D_JSON_POWERUSAGE "\":[%s]"
#ifdef INA3221_CALC_CHARGE_AH
",\"" D_JSON_CHARGE "\":[%s]"
#endif
@ -519,30 +663,23 @@ void Ina3221Show(bool json)
// data
for (int device=0 ; device < Ina3221count ; device++) {
uint8_t enabled_chan = Ina3221Data[device].enabled_chan;
for (int chan=0 ; enabled_chan ; chan++, enabled_chan>>=1) {
if (0x01 & enabled_chan) {
for (int chan=0 ; enabled_chan ; chan++, enabled_chan>>=1, enabled_chan &= 0xF7) {
if (0x11 & enabled_chan) {
if (Ina3221count > 1){
snprintf_P(name, sizeof(name), PSTR("%s%c%d:%d"), INA3221_TYPE, IndexSeparator(), device +1, chan);
}else{
}
else{
snprintf_P(name, sizeof(name), PSTR("%s:%d"), INA3221_TYPE, chan);
dtostrfd(Ina3221Data[device].chan[chan].voltage, Settings->flag2.voltage_resolution, voltage);
dtostrfd(Ina3221Data[device].chan[chan].current, Settings->flag2.current_resolution, current);
dtostrfd(Ina3221Data[device].chan[chan].voltage * Ina3221Data[device].chan[chan].current, Settings->flag2.wattage_resolution, power);
#ifdef INA3221_CALC_CHARGE_AH
dtostrfd(Ina3221Data[device].chan[chan].charge_ah, Settings->flag2.energy_resolution, charge_ah);
#endif
#ifdef INA3221_CALC_ENERGY_WH
dtostrfd(Ina3221Data[device].chan[chan].energy_wh, Settings->flag2.energy_resolution, energy_wh);
#endif
WSContentSend_PD(HTTP_SNS_INA3221_DATA, name, voltage, current, power
#ifdef INA3221_CALC_CHARGE_AH
, charge_ah
#endif
#ifdef INA3221_CALC_ENERGY_WH
, energy_wh
#endif
);
}
pw = Ina3221Data[device].chan[chan].voltage * Ina3221Data[device].chan[chan].current;
WSContentSend_PD(HTTP_SNS_INA3221_DATA, name, Settings->flag2.voltage_resolution, &Ina3221Data[device].chan[chan].voltage, Settings->flag2.current_resolution, &Ina3221Data[device].chan[chan].current, Settings->flag2.wattage_resolution, &pw
#ifdef INA3221_CALC_CHARGE_AH
, Settings->flag2.energy_resolution, &Ina3221Data[device].chan[chan].charge_ah
#endif
#ifdef INA3221_CALC_ENERGY_WH
, Settings->flag2.energy_resolution, &Ina3221Data[device].chan[chan].energy_wh
#endif
);
} // if active
} // for channel
} // for device

View File

@ -19,7 +19,7 @@
*/
#ifdef USE_I2C
#if defined(USE_AHT1x) || defined(USE_AHT2x)
#if defined(USE_AHT1x) || defined(USE_AHT2x) || defined(USE_AHT3x)
/*********************************************************************************************\
* AHT10/15/20 - Temperature and Humidity
@ -40,7 +40,10 @@
* and allows other I2C Devices on the bus but have only one I2C Address (0x38)
* 14.09.2021 support AHT20 without enabling AHT1x
*
* 26.10.2023 support for AHT30 added.
*
* AHT20 I2C Address: 0x38
* AHT30 I2C Address: 0x38
\*********************************************************************************************/
#define XSNS_63 63
@ -56,12 +59,15 @@
#define AHT_TEMPERATURE_OFFSET 50
#define KILOBYTE_CONST 1048576.0f
#define AHT1X_CMD_DELAY 40
#define AHT1X_RST_DELAY 30
#define AHT1X_CMD_DELAY 20
#define AHT1X_RST_DELAY 10
#define AHT1X_MEAS_DELAY 80 // over 75ms in datasheet
#ifdef USE_AHT2x
#define AHTX_CMD 0xB1 // Cmd for AHT2x
#ifdef USE_AHT3x
#define AHTX_CMD 0xBE // Cmd for AHT3x
const char ahtTypes[] PROGMEM = "AHT3X|AHT3X";
#elif defined(USE_AHT2x)
#define AHTX_CMD 0xBE // Cmd for AHT2x
const char ahtTypes[] PROGMEM = "AHT2X|AHT2X";
#else
#define AHTX_CMD 0xE1 // Cmd for AHT1x
@ -92,6 +98,7 @@ bool AHT1XWrite(uint8_t aht1x_idx) {
Wire.write(AHTMeasureCmd, 3);
if (Wire.endTransmission() != 0)
return false;
delay(AHT1X_MEAS_DELAY);
return true;
}
@ -198,7 +205,7 @@ bool Xsns63(uint32_t function)
}
else if (aht1x.count) {
switch (function) {
case FUNC_EVERY_100_MSECOND:
case FUNC_EVERY_SECOND:
AHT1XPoll();
break;
case FUNC_JSON_APPEND:
@ -215,4 +222,4 @@ bool Xsns63(uint32_t function)
}
#endif // USE_AHT1X
#endif // USE_I2C
#endif // USE_I2C