Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a usermod for AHT10, AHT15 and AHT20 temperature/humidity sensors #3977

Merged
merged 10 commits into from
May 21, 2024
3 changes: 3 additions & 0 deletions platformio_override.sample.ini
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D USERMOD_AUTO_SAVE
; -D AUTOSAVE_AFTER_SEC=90
;
; Use AHT10/AHT15/AHT20 usermod
; -D USERMOD_AHT10
;
; Use 4 Line Display usermod with SPI display
; -D USERMOD_FOUR_LINE_DISPLAY
; -D USE_ALT_DISPlAY # mandatory
Expand Down
37 changes: 37 additions & 0 deletions usermods/AHT10_v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Usermod AHT10
This Usermod is designed to read a `AHT10`, `AHT15` or `AHT20` sensor and output the following:
- Temperature
- Humidity

Configuration is performed via the Usermod menu. The following settings can be configured in the Usermod Menu:
- I2CAddress: The i2c address in decimal. Set it to either 56 (0x38, the default) or 57 (0x39).
- SensorType, one of:
- 0 - AHT10
- 1 - AHT15
- 2 - AHT20
- CheckInterval: Number of seconds between readings
- Decimals: Number of decimals to put in the output

Dependencies, These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
- Libraries
- `enjoyneering/AHT10@~1.1.0` (by [enjoyneering](https://registry.platformio.org/libraries/enjoyneering/AHT10))
- `Wire`

## Author
[@LordMike](https://github.com/LordMike)

# Compiling

To enable, compile with `USERMOD_AHT10` defined (e.g. in `platformio_override.ini`)
```ini
[env:aht10_example]
extends = env:esp32dev
build_flags =
${common.build_flags} ${esp32.build_flags}
-D USERMOD_AHT10
; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal
lib_deps =
${esp32.lib_deps}
enjoyneering/AHT10@~1.1.0
Wire
```
LordMike marked this conversation as resolved.
Show resolved Hide resolved
213 changes: 213 additions & 0 deletions usermods/AHT10_v2/usermod_aht10.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
#pragma once

#include "wled.h"
#include <AHT10.h>

#define AHT10_SUCCESS 1

class UsermodAHT10 : public Usermod
{
private:
static const char _name[];

unsigned long _lastLoopCheck = 0;
bool _initDone = false;

// Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime
bool _enabled = false;
uint8_t _i2cAddress = AHT10_ADDRESS_0X38;
ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR;
uint16_t _checkInterval = 60000; // milliseconds, user settings is in seconds
float _decimalFactor = 100; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..)

uint8_t _lastStatus = 0;
float _lastHumidity = 0;
float _lastTemperature = 0;

AHT10* _aht = nullptr;

float truncateDecimals(float val)
{
return roundf(val * _decimalFactor) / _decimalFactor;
}

void initializeAht()
{
if (_aht != nullptr)
{
delete _aht;
}

_aht = new AHT10(_i2cAddress, _ahtType);

_lastStatus = 0;
_lastHumidity = 0;
_lastTemperature = 0;
}

~UsermodAHT10()
{
delete _aht;
_aht = nullptr;
}

public:
void setup()
{
initializeAht();
}

void loop()
{
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
if (!_enabled || strip.isUpdating())
return;

// do your magic here
unsigned long currentTime = millis();

if (currentTime - _lastLoopCheck < _checkInterval)
return;
_lastLoopCheck = currentTime;

_lastStatus = _aht->readRawData();

if (_lastStatus == AHT10_ERROR)
{
// Perform softReset and retry
DEBUG_PRINTLN("AHTxx returned error, doing softReset");
LordMike marked this conversation as resolved.
Show resolved Hide resolved
if (!_aht->softReset())
{
DEBUG_PRINTLN("softReset failed");
return;
}

_lastStatus = _aht->readRawData();
}

if (_lastStatus == AHT10_SUCCESS)
{
float temperature = truncateDecimals(_aht->readTemperature(AHT10_USE_READ_DATA));
float humidity = truncateDecimals(_aht->readHumidity(AHT10_USE_READ_DATA));

// Push to MQTT

// Store
_lastHumidity = humidity;
_lastTemperature = temperature;
}
}

uint16_t getId()
{
return USERMOD_ID_AHT10;
}

void addToJsonInfo(JsonObject& root) override
{
// if "u" object does not exist yet wee need to create it
JsonObject user = root[F("u")];
if (user.isNull())
user = root.createNestedObject(F("u"));

#ifdef USERMOD_AHT10_DEBUG
JsonArray temp = user.createNestedArray(F("status"));
temp.add(_lastLoopCheck);
temp.add(F(" / "));
temp.add(_lastStatus);
#endif

JsonArray jsonTemp = user.createNestedArray(F("AHT Temperature"));
JsonArray jsonHumidity = user.createNestedArray(F("AHT Humidity"));

if (_lastLoopCheck == 0)
{
// Before first run
jsonTemp.add(F("Not read yet"));
jsonHumidity.add(F("Not read yet"));
return;
}

if (_lastStatus != AHT10_SUCCESS)
{
jsonTemp.add(F("An error occurred"));
jsonHumidity.add(F("An error occurred"));
return;
}

jsonTemp.add(_lastTemperature);
jsonTemp.add(F("°C"));

jsonHumidity.add(_lastHumidity);
jsonHumidity.add(F("%"));
}

void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top[F("Enabled")] = _enabled;
top[F("I2CAddress")] = static_cast<uint8_t>(_i2cAddress);
top[F("SensorType")] = _ahtType;
top[F("CheckInterval")] = _checkInterval / 1000;
top[F("Decimals")] = log10f(_decimalFactor);

DEBUG_PRINTLN(F("AHT10 config saved."));
}

bool readFromConfig(JsonObject& root) override
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)

JsonObject top = root[FPSTR(_name)];

bool configComplete = !top.isNull();
if (!configComplete)
return false;

configComplete &= getJsonValue(top[F("Enabled")], _enabled);
configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress);
configComplete &= getJsonValue(top[F("CheckInterval")], _checkInterval);
if (configComplete)
{
if (1 <= _checkInterval && _checkInterval <= 600)
_checkInterval *= 1000;
else
// Invalid input
_checkInterval = 60000;
}

configComplete &= getJsonValue(top[F("Decimals")], _decimalFactor);
if (configComplete)
{
if (0 <= _decimalFactor && _decimalFactor <= 5)
_decimalFactor = pow10f(_decimalFactor);
else
// Invalid input
_decimalFactor = 100;
}

uint8_t tmpAhtType;
configComplete &= getJsonValue(top[F("SensorType")], tmpAhtType);
if (configComplete)
{
if (0 <= tmpAhtType && tmpAhtType <= 2)
_ahtType = static_cast<ASAIR_I2C_SENSOR>(tmpAhtType);
else
// Invalid input
_ahtType = ASAIR_I2C_SENSOR::AHT10_SENSOR;
}

if (_initDone)
{
// Reloading config
initializeAht();
}

_initDone = true;
return configComplete;
}
};
LordMike marked this conversation as resolved.
Show resolved Hide resolved

const char UsermodAHT10::_name[] PROGMEM = "AHTxx";
1 change: 1 addition & 0 deletions wled00/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@
#define USERMOD_ID_HTTP_PULL_LIGHT_CONTROL 46 //usermod "usermod_v2_HttpPullLightControl.h"
#define USERMOD_ID_TETRISAI 47 //Usermod "usermod_v2_tetris.h"
#define USERMOD_ID_MAX17048 48 //Usermod "usermod_max17048.h"
#define USERMOD_ID_AHT10 49 //Usermod "usermod_aht10.h"

//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
Expand Down
8 changes: 8 additions & 0 deletions wled00/usermods_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@
#include "../usermods/TetrisAI_v2/usermod_v2_tetrisai.h"
#endif

#ifdef USERMOD_AHT10
#include "../usermods/AHT10_v2/usermod_aht10.h"
#endif

void registerUsermods()
{
/*
Expand Down Expand Up @@ -421,4 +425,8 @@ void registerUsermods()
#ifdef USERMOD_TETRISAI
usermods.add(new TetrisAIUsermod());
#endif

#ifdef USERMOD_AHT10
usermods.add(new UsermodAHT10());
#endif
}