diff --git a/platformio.ini b/platformio.ini index c0eb6fedb..c01a41b76 100644 --- a/platformio.ini +++ b/platformio.ini @@ -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 diff --git a/src/configuration.h b/src/configuration.h index cddc7ba7a..92df118d7 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -195,6 +195,10 @@ along with this program. If not, see . #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 diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index dd290db98..232c43c34 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -75,6 +75,7 @@ class ScanI2C TCA8418KB, PCT2075, BMM150, + ADS1X15, } DeviceType; // typedef uint8_t DeviceAddress; diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 5734fc200..6c8851fc7 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -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); } diff --git a/src/main.cpp b/src/main.cpp index 9f3e4fe6a..be64f2a64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index a92013d01..435859c91 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -22,6 +22,13 @@ #include "graphics/ScreenFonts.h" #include +#if __has_include() +#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; diff --git a/src/modules/Telemetry/Sensor/ADS1X15Sensor.cpp b/src/modules/Telemetry/Sensor/ADS1X15Sensor.cpp new file mode 100644 index 000000000..deaf22619 --- /dev/null +++ b/src/modules/Telemetry/Sensor/ADS1X15Sensor.cpp @@ -0,0 +1,111 @@ +#include "configuration.h" + +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include() + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "ADS1X15Sensor.h" +#include "TelemetrySensor.h" +#include + +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 \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/ADS1X15Sensor.h b/src/modules/Telemetry/Sensor/ADS1X15Sensor.h new file mode 100644 index 000000000..ca3422736 --- /dev/null +++ b/src/modules/Telemetry/Sensor/ADS1X15Sensor.h @@ -0,0 +1,24 @@ +#include "configuration.h" + +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include() + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include "VoltageSensor.h" +#include + +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 \ No newline at end of file