I have project for a microbrewery, where the aim is to be able to monitor pH during a certain step of the process, and trigger notifications when certain pH thresholds are met. I’ve managed to source a cheap Analog 5V pH-sensor, which is hooked up to an Arduino and converts the voltage to a pH value. I’m also connecting the Arduino to an LCD-screen, so you can see the current pH-value locally.
My intention is now send that Arduino pH value to ESPhome. The ultimate goal is to have a sensor in Home Assistant, which displays the same value shown in the LCD. I’ve tried to work my way through understanding the Custom UART component in ESPhome, but I’m not sure where I go wrong. I’ve connected the Arduino and Wemos using the RX and TX pins (RX -> TX and TX->RX). This is the sketch for the Arduino:
So my question is; Do I need to add something in the Arduino code and what configurations do I need to do on the ESP side to read that pHValue as a sensor value for Home Assistant?
What you might consider is eliminating the Arduino completely and simply use an ESP. The I/O capability on the ESP should be sufficient for doing what you want, especially an ESP32. The trick will be reading the PH sensor. But I think you could do that with a simple analog sensor with the right filters. By eliminating the Arduino you improve the reliability of your setup (less moving parts) and eliminate the need for inter-device communication. ESPHome supports both LCD and OLED displays.
See https://esphome.io/cookbook/display_time_temp_oled.html for an example of using a local display with an external sensor. I’m currently working on a similar project for my bathroom where I’m creating a humidity controlled exhaust fan controller plus a clock.
That’s a cool solution. But I’m still wondering if it’s unnecessarily complex. You can hook the LCD and PH sensor up to your Wemos D1 mini and skip the Arduino altogether. Is there a reason you have to use the Arduino? (I know there are always additional constraints and conditions I’m not aware of.)
The ESPHome YAML would be pretty straight forward with a little bit of Lambda for displaying on the LCD and the pH calculation. But then you have one less hardware device, no custom components, and a lot more direct control.
From what I can tell of your Arduino code, it takes 10 readings, sorts them, and throws out the two highest and two lowest readings. Then it averages the remaining 6 readings and calculates the pH value. I’m not sure if throwing out the high and low values is critical. You could do that with ESPHome, but I did not do that in my example below. I did throw in an averaging filter though.
I have NOT tested this YAML (although it compiles fine), but it should give you an idea of how to proceed if you want to go this route.
sensor:
# https://esphome.io/components/sensor/adc.html
- platform: adc
pin: A0
id: ph
name: "pH Sensor"
update_interval: 1s
unit_of_measurement: pH
# https://esphome.io/components/sensor/index.html#sensor-filters
filters:
#adjust this forumula for your needs, the /6 in the original was part of the averaging so I skipped it
- lambda: return -5.70 * ( x * 5.0 / 1024) + 21.42;
- sliding_window_moving_average:
window_size: 10
send_every: 10
display:
# https://esphome.io/components/display/lcd_display.html
- platform: lcd_gpio
dimensions: 18x4
data_pins:
- D0
- D1
- D2
- D3
enable_pin: D4
rs_pin: D5
lambda: |-
it.printf(0, 0, "pH: %.1f", id(ph).state);
if (id(ph).state < 4) {
it.print(1, 0, "Very acidic");
}
else if (id(ph).state >= 4 && id(ph).state < 5) {
it.print(1, 0, "Acidic");
}
else if (id(ph).state >= 5 && id(ph).state < 7) {
it.print(1, 0, "Acidic-ish");
}
else if (id(ph).state >= 7 && id(ph).state < 8) {
it.print(1, 0, "Neutral");
}
else if (id(ph).state >= 8 && id(ph).state < 10) {
it.print(1, 0, "Alkaline-ish");
}
else if (id(ph).state >= 10 && id(ph).state < 11) {
it.print(1, 0, "Alkaline");
}
else if (id(ph).state >= 11) {
it.print(1, 0, "Very alkaline");
}
The main reason for using the Arduino originally, was that it can receive 5V on the A0. And during the time when I was investigating what to use for the project, I didn’t even know what a ESP-board was and what it could do. Now though, it actually seems like the Arduino would be unnecessary, if I were to use a voltage divider to bring down the voltage to 3.3V, which the Wemos handle on the A0 pin. I would definitely appreciate the leanest possible solution.
Thanks for taking the time to put together the config. Do you know if there is a possibility to include a variable in the lambda, which could be defined directly in the HA UI using the number_input (https://www.home-assistant.io/integrations/input_number/)? That way I could modify the calibration value (currently set at 21.42) without compiling the yaml each time.
One other comment, I think you might actually be better served by the median filter instead of the simple average. This would help compensate for outlier readings similar to how the original code threw out the top and bottom two readings.
So, at the end of day I went with no Arduino at all. It didn’t really make any sense to use it since the ESP could handle everything by itself. I used a potentiometer to scale down the voltage from the pH sensor from 5V to 3.3V. Here are some pics of the final product (https://imgur.com/gallery/CBLpdwX)
Thanks @coderanger for your yaml suggestions. The final yaml is as follows:
Hi @frasskungin. Nice project !
What kind of potentiometer did you use ?
How did you proceed to make your ajustement with it ?
Did you use a calibration automation ?
brgds
At the end of the day the project was not successful fit for the application I had planned it for. The thing was that long submersion of the probe caused the sensor values to go off the charts. For shorter periods of measuring the sensor works pretty well.
You might find that if you use an external ADC it will be more accurate. Something about the pH scale means every 1 point in pH is an exponential increase (or decrease), so the higher or lower you get from natural pH7 the hydrogen ion activity is doubling so a pH of 5 has 4x the ion activity of a natural pH of 7, not double, and pH 4 is 8 times more than pH 7. This means you need to divide the voltage more times than you can with pin A0 because of the limitations of the internal ADC. The pH probe being submerged is not the problem the ADC is.
Nice approach, much cleaner than the random tutorials. Once again, I’m impressed by EspHome’s design. I ended up with this:
- platform: adc
pin: A0
id: horti3_ph
name: "Horti3 PH Meter"
update_interval: 1s
unit_of_measurement: pH
# https://esphome.io/components/sensor/index.html#sensor-filters
filters:
- median:
window_size: 7
send_every: 4
send_first_at: 3
- lambda: |-
ESP_LOGD("ph", "volatge=%fv", x);
return x;
# Measured voltage -> Actual pH (buffer solution)
# Lower the potentiometer (counter-clockwise) at pH 7.
# Pick a baseline voltage (the log entry above) on the low end of the range to leave room for the higher voltage of low pH.
# Then, switch to pH 4 and note the voltage. Do not worry about expected voltages (e.g. 2.5v) found in random tutorials.
- calibrate_linear:
- 0.79 -> 7.0
- 0.93 -> 4.0
- lambda: |-
ESP_LOGD("ph", "read=%fpH", x);
return x;
This is a ESP8266 NodeMCU. So far, even with a CCC probe, it’s in-line with my Apera meter.