Initial integration of ADS1X15 ADC

This commit is contained in:
oscgonfer 2025-06-23 15:35:12 +02:00
parent 547767ca68
commit 49b32debe4
8 changed files with 178 additions and 3 deletions

View File

@ -202,3 +202,5 @@ lib_deps =
sensirion/Sensirion Core@0.7.1
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0
# renovate: datasource=custom.pio depName=Adafruit ADS1X15 packageName=adafruit/library/Adafruit ADS1X15 Library
adafruit/Adafruit ADS1X15@2.5.0

View File

@ -195,6 +195,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define LTR390UV_ADDR 0x53
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418
#define PCT2075_ADDR 0x37
#define ADS1X15_ADDR 0x48 // same address as FT6336U
#define ADS1X15_ADDR_ALT1 0x49
#define ADS1X15_ADDR_ALT2 0x4A
#define ADS1X15_ADDR_ALT3 0x4B
// -----------------------------------------------------------------------------
// ACCELEROMETER

View File

@ -75,6 +75,7 @@ class ScanI2C
TCA8418KB,
PCT2075,
BMM150,
ADS1X15,
} DeviceType;
// typedef uint8_t DeviceAddress;

View File

@ -564,7 +564,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
}
break;
case 0x48: {
case 0x48: { // same as ADS1X15 main address
// ADS1X15 default config register is 8583h
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x01), 2);
if (registerValue == 0x8583) {
type = ADS1X15;
logFoundDevice("ADS1X15", (uint8_t)addr.address);
break;
}
i2cBus->beginTransmission(addr.address);
uint8_t getInfo[] = {0x5A, 0xC0, 0x00, 0xFF, 0xFC};
uint8_t expectedInfo[] = {0xa5, 0xE0, 0x00, 0x3F, 0x19};
@ -584,6 +593,17 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
break;
}
case ADS1X15_ADDR_ALT1:
case ADS1X15_ADDR_ALT2:
case ADS1X15_ADDR_ALT3:
// ADS1X15 default config register is 8583h
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x01), 2);
if (registerValue == 0x8583) {
type = ADS1X15;
logFoundDevice("ADS1X15", (uint8_t)addr.address);
break;
}
default:
LOG_INFO("Device found at address 0x%x was not able to be enumerated", (uint8_t)addr.address);
}

View File

@ -703,6 +703,7 @@ void setup()
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PCT2075, meshtastic_TelemetrySensorType_PCT2075);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ADS1X15, meshtastic_TelemetrySensorType_ADS1X15);
i2cScanner.reset();
#endif

View File

@ -22,6 +22,13 @@
#include "graphics/ScreenFonts.h"
#include <Throttle.h>
#if __has_include(<Adafruit_ADS1015.h>)
#include "Sensor/ADS1X15Sensor.h"
ADS1X15Sensor ads1x15Sensor;
#else
NullSensor ads1x15Sensor;
#endif
namespace graphics
{
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only);
@ -74,6 +81,8 @@ int32_t PowerTelemetryModule::runOnce()
result = ina3221Sensor.isInitialized() ? 0 : ina3221Sensor.runOnce();
if (max17048Sensor.hasSensor())
result = max17048Sensor.isInitialized() ? 0 : max17048Sensor.runOnce();
if (ads1x15Sensor.hasSensor())
result = ads1x15Sensor.isInitialized() ? 0 : ads1x15Sensor.runOnce();
}
// it's possible to have this module enabled, only for displaying values on the screen.
@ -205,6 +214,8 @@ bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m)
valid = ina3221Sensor.getMetrics(m);
if (max17048Sensor.hasSensor())
valid = max17048Sensor.getMetrics(m);
if (ads1x15Sensor.hasSensor())
valid = ads1x15Sensor.getMetrics(m);
#endif
return valid;
@ -246,9 +257,10 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
m.time = getTime();
if (getPowerTelemetry(&m)) {
LOG_INFO("Send: ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, "
"ch3_voltage=%f, ch3_current=%f",
"ch3_voltage=%f, ch3_current=%f, ch4_voltage=%f",
m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage,
m.variant.power_metrics.ch2_current, m.variant.power_metrics.ch3_voltage, m.variant.power_metrics.ch3_current);
m.variant.power_metrics.ch2_current, m.variant.power_metrics.ch3_voltage, m.variant.power_metrics.ch3_current,
m.variant.power_metrics.ch4_voltage);
sensor_read_error_count = 0;

View File

@ -0,0 +1,111 @@
#include "configuration.h"
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_ADS1015.h>)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "ADS1X15Sensor.h"
#include "TelemetrySensor.h"
#include <Adafruit_ADS1015.h>
ADS1X15Sensor::ADS1X15Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_ADS1X15, "ADS1X15") {}
int32_t ADS1X15Sensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
status = ads1x15.begin(nodeTelemetrySensorsMap[sensorType].first);
return initI2CSensor();
}
void ADS1X15Sensor::setup() {}
struct _ADS1X15Measurement ADS1X15Sensor::getMeasurement(ads1x15_ch_t ch)
{
struct _ADS1X15Measurement measurement;
// Reset gain
ads1x15.setGain(GAIN_TWOTHIRDS);
double voltage_range = 6.144;
// Get value with full range
uint16_t value = ads.readADC_SingleEnded(ch);
// Dynamic gain, to increase resolution of low voltage values
// If value is under 4.096v increase the gain depending on voltage
if (value < 21845) {
if (value > 10922) {
// 1x gain, 4.096V
ads1x15.setGain(GAIN_ONE);
voltage_range = 4.096;
} else if (value > 5461) {
// 2x gain, 2.048V
ads1x15.setGain(GAIN_TWO);
voltage_range = 2.048;
} else if (value > 2730) {
// 4x gain, 1.024V
ads1x15.setGain(GAIN_FOUR);
voltage_range = 1.024;
} else if (value > 1365) {
// 8x gain, 0.25V
ads1x15.setGain(GAIN_EIGHT);
voltage_range = 0.512;
} else {
// 16x gain, 0.125V
ads1x15.setGain(GAIN_SIXTEEN);
voltage_range = 0.256;
}
// Get the value again
value = ads1x15.readADC_SingleEnded(ch);
}
reading = (float)value / 32768 * voltage_range;
measurement.voltage = reading;
return measurement;
}
struct _ADS1X15Measurements ADS1X15Sensor::getMeasurements()
{
struct _ADS1X15Measurements measurements;
// ADS1X15 has 4 channels starting from 0
for (int i = 0; i < 4; i++) {
measurements.measurements[i] = getMeasurement((ads1x15_ch_t)i);
}
return measurements;
}
bool ADS1X15Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
struct _ADS1X15Measurements m = getMeasurements();
measurement->variant.power_metrics.has_ch1_voltage = true;
measurement->variant.power_metrics.has_ch2_voltage = true;
measurement->variant.power_metrics.has_ch3_voltage = true;
measurement->variant.power_metrics.has_ch4_voltage = true;
measurement->variant.power_metrics.ch1_voltage = m.measurements[0].voltage;
measurement->variant.power_metrics.ch2_voltage = m.measurements[1].voltage;
measurement->variant.power_metrics.ch3_voltage = m.measurements[2].voltage;
measurement->variant.power_metrics.ch4_voltage = m.measurements[3].voltage;
return true;
}
#endif

View File

@ -0,0 +1,24 @@
#include "configuration.h"
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_ADS1015.h>)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include "VoltageSensor.h"
#include <Adafruit_ADS1015.h>
class ADS1X15Sensor : public TelemetrySensor, VoltageSensor
{
private:
Adafruit_ADS1015 ads1x15;
protected:
virtual void setup() override;
public:
ADS1X15Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif