4-20ma to i2c board for water tank level sensing. How to make an esphome component?

I have had some of these tank level sensors for some time which have been connected to an Arduino ADC pin via a simple current to voltage circuit and then into my previous automation software Homeseer. This worked ok but there was some noise, and the tank level varied by 2 or 3% everytime the level was read.

I now would like to get the tank levels into Home Assistant.

I bought one of these boards

and it came with this arduino code. I don’t know how to get this into an esphome component. Can anyone help please?

Cheers!

#include <Arduino.h>
#include <stdint.h>
#include <Wire.h>
#include "ADC.h"

//! I2C address of the module.
//! Configured by tying the A1, A0 pins to HI( High ) or LO( Low ). Or to be left open for Float.

// I2C Address                     //  A1       A0       
   #define ADC_I2C_ADDRESS 0x14    //  Low       Low       
// #define ADC_I2C_ADDRESS 0x16    //  Low       High       
// #define ADC_I2C_ADDRESS 0x15    //  Low       Float    
// #define ADC_I2C_ADDRESS 0x26    //  High      Low
// #define ADC_I2C_ADDRESS 0x34    //  High      High
// #define ADC_I2C_ADDRESS 0x27    //  High      Float
// #define ADC_I2C_ADDRESS 0x17    //  Float     Low
// #define ADC_I2C_ADDRESS 0x25    //  Float     High
// #define ADC_I2C_ADDRESS 0x24    //  Float     Float

// Global variables
static int16_t  speed_mode = SLOW;                   //!< The ADC Speed Mode settings. Change it to "FAST" for higher data rate.
static float    adc_vref = 2.5;                      //!< The ADC reference voltage
static uint8_t  rejection_mode = ADC_R50;            //!< The ADC rejection mode
static uint8_t  i2c_address = ADC_I2C_ADDRESS;       //!< I2C address in 7 bit format for part
static uint16_t eoc_timeout = 300;                   //!< Timeout in ms

static float adc_offset = 0;                      


//! Lookup table to build the command for single-ended mode
const uint16_t BUILD_COMMAND[4] = { ADC_CH0, ADC_CH1, ADC_CH2, ADC_CH3 };  

void setup()
{
  Wire.begin();               // wake up I2C bus
  Serial.begin(9600);         // Initialize the serial port to the PC
}
  
void loop()
{
  uint8_t acknowledge = 0;
  
  acknowledge |= read_analog_input();
  
  if (acknowledge)
  Serial.println(F("***** I2C ERROR *****"));
  Serial.print(F("\n*************************\n"));
}


//! @return 0 if successful, 1 is failure
int8_t read_analog_input()
{
  uint8_t adc_command_high;  // The ADC command high byte
  uint8_t adc_command_low;   // The ADC command low byte
  int32_t adc_code = 0;      // The ADC code
  float adc_voltage = 0;     // The ADC voltage
  float current_mA = 0;      // 4-20mA current
  
  while (1)
  {
    uint8_t ack = 0;
    
    adc_command_high = BUILD_COMMAND[0];             // Build ADC command for channel 0
    adc_command_low = rejection_mode| speed_mode;
    
    ack |= adc_read( i2c_address, adc_command_high, adc_command_low, &adc_code, eoc_timeout );   // Throws out last reading

    for (int8_t x = 0; x <= 3; x++)                            
    {
      adc_command_high = BUILD_COMMAND[(x + 1) % 4];
      
      ack |= adc_read( i2c_address, adc_command_high, adc_command_low, &adc_code, eoc_timeout );
      adc_voltage = adc_code_to_voltage( adc_code, adc_vref) + adc_offset + adc_vref/2;
      current_mA = adc_voltage * 10;
      if (!ack)
      {
        Serial.print(F("  ****"));
        Serial.print(F("CH"));
        Serial.print(x);
        Serial.print(F(": "));
        Serial.print(F("Received Code: 0x"));
        Serial.print(adc_code, HEX);  
        Serial.print(";  Current: ");      
        Serial.print(current_mA, 4);
        Serial.print(F("mA\n"));
      }
      else
      {
          Serial.print(F("  ****"));
          Serial.print(F("CH"));
          Serial.print(x);
          Serial.print(F(": "));
          Serial.print(F("Error in read"));
          return 1;
      }
      
     }

     Serial.print(F("\n")); 
     delay(1000);
  }    
  return(0);
}

Do you intend to use an ESP node? Which one?

I2C is supported in ESPHome

The compatible units are on the ESPHome website. It would be worth finding out first whether the unit is supported or the ardunio software can be imported

I have the same pressure sensor but I use ADS115 for measuring voltage. Works well BUT I have suddenly seen a lot of noise in the signal after a year in use. My next project to find out why. Good luck

I’ll be using a wt32-eth01 in wired Ethernet mode.

I already use several of these with i2c expanders but I need to create a custom esphome component for this board.

Just started reading up on how to create one.

any luck with the 4-20ma signal to ESP home?

This may be unorhtodox but i use an ina-219 current sensor and a 4-20 ma transducer and the bottom of my water tank. on a 1000 gal tank with a 2 meter transducer i get a ~14 gallon resolution i got a transducer with an airline in the cable to compensate for atmospheric pressure changes. For my application being accurate to milliliters was just not needed 14 gallons work’s fine Its been in the tank for a year now before this i tried just about every other sensor out there TOF UT etc…None survived the environment. Its a 1200 gallon tank we have a low producing well so i use this to keep the tank full 80 gallons a run and a three hour min between runs. i have a 100 gallon margin so a worst case error of 28 gallons wont hurt anything.

Here is my code for the current sensor its commented out i calibrated is in my shower in a 5 gallon bucket water column is water column the width doesn’t matter The -200 gallons is the pump inlet port i also have an automation set to disable the pump if it goes below 50 gallons so it wont run dry and suck air. I’m sure there’s a better lambda for how I did this but how it is works and reads correctly. I program machine tools so i just did the math like i would have for that.

- platform: ina219

    i2c_id: bus_a

    address: 0x40

    shunt_resistance: 0.1 ohm

    current:

      name: "Tank Level"

      id: tank_level

      accuracy_decimals: 1

      filters:

      - multiply: 1000 #convert from Amps to mA

      - offset: -4.205 #-4 to get to zero 4-20ma this was the zero reading on the sensor This was the reading i got on the sensor just sitting in empty bucket

      - lambda: return (x/0.196) * 24.99; #one inch of water is 24.99 gal .196ma = one inch in tank got these numbers from a 5 gallon bucket in my shower and a tape measure i got consistent numbers from 2-8 inches one inch didn't totally cover sensor, head pressure is depth not diameter The 24.99 goes with the diameter of my tank gallons per inch of depth.

      - offset: -200.0 #useable gallons useable before pump will suck air the bottom 200 gallons cant be used (i didn't drill that hole)
unit_of_measurement: "Gallons"

    power:

      name: "INA219 Power"

    bus_voltage:

      name: "INA219 Bus Voltage"

      accuracy_decimals: 4

    shunt_voltage:

      name: "INA219 Shunt Voltage"

    max_voltage: 16.0V

    max_current: 400ma

    update_interval: 5s

Hello BeckyB,

thanks for sharing.
I am testing a water pressure transducer with INA219 and your template seems to work with me.
Could you please explain the maths behind opting max_voltage and max_current?

thanks

the transducer wont see more than 15vdc and nor will it go over 20 ma. the lower the range the more resolution the ina-219 has is how i read the data sheets