Hello everyone. I am trying to connect a Digital ORP/pH sensor to an ESPHome. The sensor is like this. The sensor is digital, 16-bit, connected via I2C.
The seller provided me with separate executable files that work fine in the Arduino IDE (output data to the com port).
The main file of the executable program is as follows.
#include <Arduino.h>
#include <stdint.h>
#include <Wire.h>
#include "ADC.h"
//! I2C address of the pH/ORP module.
//! Configured by tying the CA0, CA1 pins to H( High ) or L( Low ). Or to be left open for Float.
// I2C Slave Address // CA1 CA0
// #define ADC_I2C_ADDRESS 0x14 // Low High
// #define ADC_I2C_ADDRESS 0x15 // Low Float
// #define ADC_I2C_ADDRESS 0x17 // Float High
#define ADC_I2C_ADDRESS 0x24 // Float Float(default)
// #define ADC_I2C_ADDRESS 0x26 // High High
// #define ADC_I2C_ADDRESS 0x27 // High 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; //!< Add an offset value to the ADC output.
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_adc();
if (acknowledge)
Serial.println(F("***** I2C ERROR *****"));
delay(1000);
}
//! @return 0 if successful, 1 is failure
int8_t read_adc()
{
uint8_t adc_command;
int32_t adc_code = 0;
float adc_voltage = 0;
uint8_t ack = 0;
adc_command = rejection_mode| speed_mode;
ack |= adc_read( i2c_address, adc_command, &adc_code, eoc_timeout );
adc_voltage = adc_code_to_voltage( adc_code, adc_vref ) * 1000 + adc_offset; //!< Convert the ADC code to mV voltage
//!< Convert the mV signal to pH value.
//!< ......
if ( !ack )
{
Serial.print( F("*************************\n" ) );
Serial.print( "BNC Input Voltage: " );
Serial.print( adc_voltage, 1 );
Serial.print( F( "mV\n" ) );
}
else
{
Serial.print( F( "Error in read" ) );
return 1;
}
return( 0 );
}
The other two are:
ADC.h
#ifndef ADC_H
#define ADC_H
//! This union splits one int32_t (32-bit signed integer) or uint32_t (32-bit unsigned integer)
//! four uint8_t's (8-bit unsigned integers) and vice versa.
union LT_UNION_INT32_4BYTES
{
int32_t LT_INT32; //!< 32-bit signed integer to be converted to four bytes
uint32_t LT_UINT32; //!< 32-bit unsigned integer to be converted to four bytes
uint8_t LT_BYTE[4]; //!< 4 bytes (unsigned 8-bit integers) to be converted to a 32-bit signed or unsigned integer
};
// Select rejection frequency - 50 and 60, 50, or 60Hz
#define ADC_R50 0b00000010
#define ADC_R60 0b00000100
#define ADC_R50_R60 0b00000000
// Speed settings is bit 7 in the 2nd byte
#define SLOW 0b00000000 // slow output rate with autozero
#define FAST 0b00000001 // fast output rate with no autozero
/*Commands
Construct a channel / resolution control word by bitwise ORing one choice from the channel configuration
and one choice from the Oversample ratio configuration. You can also enable 2Xmode, which will increase
sample rate by a factor of 2 but introduce an offset of up to 2mV. */
//! Reads from ADC.
//! @return 1 if no acknowledge, 0 if acknowledge
uint8_t adc_read(uint8_t i2c_address, //!< I2C address (7-bit format) for part
uint8_t adc_command, //!< High byte command written to ADC
int32_t *adc_code, //!< 4 byte conversion code read from ADC
uint16_t timeout //!< Timeout in ms
);
//! Calculates the voltage corresponding to an adc code, given the reference (in volts)
//! @return Returns voltage calculated from the ADC code.
float adc_code_to_voltage(int32_t adc_code, //!< Code read from adc
float vref //!< VRef (in volts)
);
#endif // ADC_H
ADC.cpp
#include <stdint.h>
#include <Arduino.h>
#include <Wire.h>
#include "ADC.h"
// Write two command bytes, then receive a block of data
int8_t i2c_one_byte_command_read_block(uint8_t address, uint8_t command, uint8_t length, uint8_t *values)
{
int8_t ret = 0;
uint8_t i = (length-1);
uint8_t readBack = 0;
Wire.beginTransmission(address);
Wire.write(byte(command));
if (Wire.endTransmission(false)) // endTransmission(false) is a repeated start
{
// endTransmission returns zero on success
Wire.endTransmission();
return(1);
}
readBack = Wire.requestFrom((uint8_t)address, (uint8_t)length, (uint8_t)true);
if (readBack == length)
{
while (Wire.available())
{
values[i] = Wire.read();
if (i == 0)
break;
i--;
}
return (0);
}
else
{
return (1);
}
}
//! Reads from the ADC that accepts a 8 bit configuration and returns a 24 bit result.
//! Returns the state of the acknowledge bit after the I2C address write. 0=acknowledge, 1=no acknowledge.
int8_t adc_i2c_8bit_command_24bit_data(uint8_t i2c_address,uint8_t adc_command,int32_t *adc_code,uint16_t eoc_timeout)
{
int8_t ack;
uint16_t timer_count = 0; // Timer count to wait for ACK
LT_UNION_INT32_4BYTES data; // ADC data
while(1)
{
ack = i2c_one_byte_command_read_block(i2c_address, adc_command, 3, data.LT_BYTE);
if(!ack) break; // !ack indicates success
if (timer_count++>eoc_timeout) // If timeout, return 1 (failure)
return(1);
else
delay(1);
}
data.LT_BYTE[3] = data.LT_BYTE[2]; // Shift bytes up by one. We read out 24 bits,
data.LT_BYTE[2] = data.LT_BYTE[1]; // which are loaded into bytes 2,1,0. Need to left-
data.LT_BYTE[1] = data.LT_BYTE[0]; // justify.
data.LT_BYTE[0] = 0x00;
data.LT_UINT32 >>= 2; // Shifts data 2 bits to the right; operating on unsigned member shifts in zeros.
data.LT_BYTE[3] = data.LT_BYTE[3] & 0x3F; // Clear upper 2 bits JUST IN CASE. Now the data format matches the SPI parts.
*adc_code = data.LT_INT32;
return(ack); // Success
}
// Calculates the voltage corresponding to an adc code, given the reference voltage (in volts)
// This function handles all differential input parts, including the "single-ended" mode on multichannel
// differential parts. Data from I2C parts must be right-shifted by two bit positions such that the MSB
// is in bit 28 (the same as the SPI parts.)
float adc_diff_code_to_voltage(int32_t adc_code, float vref)
{
float voltage;
adc_code -= 0x20000000; //! 1) Converts offset binary to binary
voltage=(float) adc_code;
voltage = voltage / 536870912.0; //! 2) This calculates the input as a fraction of the reference voltage (dimensionless)
voltage = voltage * vref; //! 3) Multiply fraction by Vref to get the actual voltage at the input (in volts)
return(voltage);
}
// Reads from the ADC.
uint8_t adc_read(uint8_t i2c_address, uint8_t adc_command, int32_t *adc_code, uint16_t timeout)
{
return (adc_i2c_8bit_command_24bit_data(i2c_address, adc_command, adc_code, timeout));
}
// Calculates the voltage corresponding to an ADC code, given the reference (in volts)
float adc_code_to_voltage(int32_t adc_code, float vref)
{
return (adc_diff_code_to_voltage(adc_code, vref));
}
But I can’t figure out how to integrate this code into ESP Home. I tried custom sensor component (Custom Sensor Component — ESPHome ), I get to step 3, and I don’t know what to do next. I’m still new to Home Assistant.
I would be grateful if someone could help me with this and guide me in the right direction.