Add ADS1115 usermod (#2752)
Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
This commit is contained in:
parent
e1365f185c
commit
7cac609c06
@ -115,6 +115,8 @@ build_flags =
|
|||||||
-D DECODE_LG=true
|
-D DECODE_LG=true
|
||||||
-DWLED_USE_MY_CONFIG
|
-DWLED_USE_MY_CONFIG
|
||||||
; -D USERMOD_SENSORSTOMQTT
|
; -D USERMOD_SENSORSTOMQTT
|
||||||
|
#For ADS1115 sensor uncomment following
|
||||||
|
; -D USERMOD_ADS1115
|
||||||
|
|
||||||
build_unflags =
|
build_unflags =
|
||||||
|
|
||||||
@ -177,6 +179,9 @@ lib_deps =
|
|||||||
; adafruit/Adafruit BMP280 Library @ 2.1.0
|
; adafruit/Adafruit BMP280 Library @ 2.1.0
|
||||||
; adafruit/Adafruit CCS811 Library @ 1.0.4
|
; adafruit/Adafruit CCS811 Library @ 1.0.4
|
||||||
; adafruit/Adafruit Si7021 Library @ 1.4.0
|
; adafruit/Adafruit Si7021 Library @ 1.4.0
|
||||||
|
#For ADS1115 sensor uncomment following
|
||||||
|
; adafruit/Adafruit BusIO @ 1.13.2
|
||||||
|
; adafruit/Adafruit ADS1X15 @ 2.4.0
|
||||||
|
|
||||||
extra_scripts = ${scripts_defaults.extra_scripts}
|
extra_scripts = ${scripts_defaults.extra_scripts}
|
||||||
|
|
||||||
|
15
usermods/ADS1115_v2/ChannelSettings.h
Normal file
15
usermods/ADS1115_v2/ChannelSettings.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "wled.h"
|
||||||
|
|
||||||
|
namespace ADS1115
|
||||||
|
{
|
||||||
|
struct ChannelSettings {
|
||||||
|
const String settingName;
|
||||||
|
bool isEnabled;
|
||||||
|
String name;
|
||||||
|
String units;
|
||||||
|
const uint16_t mux;
|
||||||
|
float multiplier;
|
||||||
|
float offset;
|
||||||
|
uint8_t decimals;
|
||||||
|
};
|
||||||
|
}
|
10
usermods/ADS1115_v2/readme.md
Normal file
10
usermods/ADS1115_v2/readme.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# ADS1115 16-Bit ADC with four inputs
|
||||||
|
|
||||||
|
This usermod will read from an ADS1115 ADC. The voltages are displayed in the Info section of the web UI.
|
||||||
|
|
||||||
|
Configuration is all completed via the Usermod menu. There are no settings to set in code!
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add the build flag `-D USERMOD_ADS1115` to your platformio environment.
|
||||||
|
Uncomment libraries with comment `#For ADS1115 sensor uncomment following`
|
255
usermods/ADS1115_v2/usermod_ads1115.h
Normal file
255
usermods/ADS1115_v2/usermod_ads1115.h
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "wled.h"
|
||||||
|
#include <Adafruit_ADS1X15.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "ChannelSettings.h"
|
||||||
|
|
||||||
|
using namespace ADS1115;
|
||||||
|
|
||||||
|
class ADS1115Usermod : public Usermod {
|
||||||
|
public:
|
||||||
|
void setup() {
|
||||||
|
ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V
|
||||||
|
|
||||||
|
if (!ads.begin()) {
|
||||||
|
Serial.println("Failed to initialize ADS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initChannel()) {
|
||||||
|
isInitialized = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startReading();
|
||||||
|
|
||||||
|
isEnabled = true;
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (isEnabled && millis() - lastTime > loopInterval) {
|
||||||
|
lastTime = millis();
|
||||||
|
|
||||||
|
// If we don't have new data, skip this iteration.
|
||||||
|
if (!ads.conversionComplete()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateResult();
|
||||||
|
moveToNextChannel();
|
||||||
|
startReading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addToJsonInfo(JsonObject& root)
|
||||||
|
{
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject user = root[F("u")];
|
||||||
|
if (user.isNull()) user = root.createNestedObject(F("u"));
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < channelsCount; i++) {
|
||||||
|
ChannelSettings* settingsPtr = &(channelSettings[i]);
|
||||||
|
|
||||||
|
if (!settingsPtr->isEnabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray lightArr = user.createNestedArray(settingsPtr->name); //name
|
||||||
|
float value = round((readings[i] + settingsPtr->offset) * settingsPtr->multiplier, settingsPtr->decimals);
|
||||||
|
lightArr.add(value); //value
|
||||||
|
lightArr.add(" " + settingsPtr->units); //unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addToConfig(JsonObject& root)
|
||||||
|
{
|
||||||
|
JsonObject top = root.createNestedObject(F("ADC ADS1115"));
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < channelsCount; i++) {
|
||||||
|
ChannelSettings* settingsPtr = &(channelSettings[i]);
|
||||||
|
JsonObject channel = top.createNestedObject(settingsPtr->settingName);
|
||||||
|
channel[F("Enabled")] = settingsPtr->isEnabled;
|
||||||
|
channel[F("Name")] = settingsPtr->name;
|
||||||
|
channel[F("Units")] = settingsPtr->units;
|
||||||
|
channel[F("Multiplier")] = settingsPtr->multiplier;
|
||||||
|
channel[F("Offset")] = settingsPtr->offset;
|
||||||
|
channel[F("Decimals")] = settingsPtr->decimals;
|
||||||
|
}
|
||||||
|
|
||||||
|
top[F("Loop Interval")] = loopInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readFromConfig(JsonObject& root)
|
||||||
|
{
|
||||||
|
JsonObject top = root[F("ADC ADS1115")];
|
||||||
|
|
||||||
|
bool configComplete = !top.isNull();
|
||||||
|
bool hasEnabledChannels = false;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < channelsCount && configComplete; i++) {
|
||||||
|
ChannelSettings* settingsPtr = &(channelSettings[i]);
|
||||||
|
JsonObject channel = top[settingsPtr->settingName];
|
||||||
|
|
||||||
|
configComplete &= !channel.isNull();
|
||||||
|
|
||||||
|
configComplete &= getJsonValue(channel[F("Enabled")], settingsPtr->isEnabled);
|
||||||
|
configComplete &= getJsonValue(channel[F("Name")], settingsPtr->name);
|
||||||
|
configComplete &= getJsonValue(channel[F("Units")], settingsPtr->units);
|
||||||
|
configComplete &= getJsonValue(channel[F("Multiplier")], settingsPtr->multiplier);
|
||||||
|
configComplete &= getJsonValue(channel[F("Offset")], settingsPtr->offset);
|
||||||
|
configComplete &= getJsonValue(channel[F("Decimals")], settingsPtr->decimals);
|
||||||
|
|
||||||
|
hasEnabledChannels |= settingsPtr->isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
configComplete &= getJsonValue(top[F("Loop Interval")], loopInterval);
|
||||||
|
|
||||||
|
isEnabled = isInitialized && configComplete && hasEnabledChannels;
|
||||||
|
|
||||||
|
return configComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t getId()
|
||||||
|
{
|
||||||
|
return USERMOD_ID_ADS1115;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const uint8_t channelsCount = 8;
|
||||||
|
|
||||||
|
ChannelSettings channelSettings[channelsCount] = {
|
||||||
|
{
|
||||||
|
"Differential reading from AIN0 (P) and AIN1 (N)",
|
||||||
|
false,
|
||||||
|
"Differential AIN0 AIN1",
|
||||||
|
"V",
|
||||||
|
ADS1X15_REG_CONFIG_MUX_DIFF_0_1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Differential reading from AIN0 (P) and AIN3 (N)",
|
||||||
|
false,
|
||||||
|
"Differential AIN0 AIN3",
|
||||||
|
"V",
|
||||||
|
ADS1X15_REG_CONFIG_MUX_DIFF_0_3,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Differential reading from AIN1 (P) and AIN3 (N)",
|
||||||
|
false,
|
||||||
|
"Differential AIN1 AIN3",
|
||||||
|
"V",
|
||||||
|
ADS1X15_REG_CONFIG_MUX_DIFF_1_3,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Differential reading from AIN2 (P) and AIN3 (N)",
|
||||||
|
false,
|
||||||
|
"Differential AIN2 AIN3",
|
||||||
|
"V",
|
||||||
|
ADS1X15_REG_CONFIG_MUX_DIFF_2_3,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Single-ended reading from AIN0",
|
||||||
|
false,
|
||||||
|
"Single-ended AIN0",
|
||||||
|
"V",
|
||||||
|
ADS1X15_REG_CONFIG_MUX_SINGLE_0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Single-ended reading from AIN1",
|
||||||
|
false,
|
||||||
|
"Single-ended AIN1",
|
||||||
|
"V",
|
||||||
|
ADS1X15_REG_CONFIG_MUX_SINGLE_1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Single-ended reading from AIN2",
|
||||||
|
false,
|
||||||
|
"Single-ended AIN2",
|
||||||
|
"V",
|
||||||
|
ADS1X15_REG_CONFIG_MUX_SINGLE_2,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Single-ended reading from AIN3",
|
||||||
|
false,
|
||||||
|
"Single-ended AIN3",
|
||||||
|
"V",
|
||||||
|
ADS1X15_REG_CONFIG_MUX_SINGLE_3,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
},
|
||||||
|
};
|
||||||
|
float readings[channelsCount] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
unsigned long loopInterval = 1000;
|
||||||
|
unsigned long lastTime = 0;
|
||||||
|
|
||||||
|
Adafruit_ADS1115 ads;
|
||||||
|
uint8_t activeChannel;
|
||||||
|
|
||||||
|
bool isEnabled = false;
|
||||||
|
bool isInitialized = false;
|
||||||
|
|
||||||
|
static float round(float value, uint8_t decimals) {
|
||||||
|
return roundf(value * powf(10, decimals)) / powf(10, decimals);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initChannel() {
|
||||||
|
for (uint8_t i = 0; i < channelsCount; i++) {
|
||||||
|
if (channelSettings[i].isEnabled) {
|
||||||
|
activeChannel = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeChannel = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveToNextChannel() {
|
||||||
|
uint8_t oldActiveChannel = activeChannel;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (++activeChannel >= channelsCount){
|
||||||
|
activeChannel = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!channelSettings[activeChannel].isEnabled && oldActiveChannel != activeChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startReading() {
|
||||||
|
ads.startADCReading(channelSettings[activeChannel].mux, /*continuous=*/false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateResult() {
|
||||||
|
int16_t results = ads.getLastConversionResults();
|
||||||
|
readings[activeChannel] = ads.computeVolts(results);
|
||||||
|
}
|
||||||
|
};
|
@ -95,6 +95,7 @@
|
|||||||
#define USERMOD_ID_AUDIOREACTIVE 32 //Usermod "audioreactive.h"
|
#define USERMOD_ID_AUDIOREACTIVE 32 //Usermod "audioreactive.h"
|
||||||
#define USERMOD_ID_ANALOG_CLOCK 33 //Usermod "Analog_Clock.h"
|
#define USERMOD_ID_ANALOG_CLOCK 33 //Usermod "Analog_Clock.h"
|
||||||
#define USERMOD_ID_PING_PONG_CLOCK 34 //Usermod "usermod_v2_ping_pong_clock.h"
|
#define USERMOD_ID_PING_PONG_CLOCK 34 //Usermod "usermod_v2_ping_pong_clock.h"
|
||||||
|
#define USERMOD_ID_ADS1115 35 //Usermod "usermod_ads1115.h"
|
||||||
|
|
||||||
//Access point behavior
|
//Access point behavior
|
||||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||||
|
@ -147,6 +147,10 @@
|
|||||||
#include "../usermods/usermod_v2_ping_pong_clock/usermod_v2_ping_pong_clock.h"
|
#include "../usermods/usermod_v2_ping_pong_clock/usermod_v2_ping_pong_clock.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USERMOD_ADS1115
|
||||||
|
#include "../usermods/ADS1115_v2/usermod_ads1115.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
void registerUsermods()
|
void registerUsermods()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -282,4 +286,8 @@ void registerUsermods()
|
|||||||
#ifdef USERMOD_PING_PONG_CLOCK
|
#ifdef USERMOD_PING_PONG_CLOCK
|
||||||
usermods.add(new PingPongClockUsermod());
|
usermods.add(new PingPongClockUsermod());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USERMOD_ADS1115
|
||||||
|
usermods.add(new ADS1115Usermod());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user