How to create a custom input with calibration using ADS1115 ADC and Gravity analog pH and EC sensors?

Thanks for sharing. I’ll review them and incorporate them into the next Mycodo release. I’ll probably also add a single Input for the EC as well, so the user can choose a single sensor Input if they only have one and the dual Input if they have both.

That’s OK. Both input examples are set up to allow measurements to be disabled as needed, though the ADC channel assignments are hard-coded (0=pH, 1=EC). I suppose my wish for a future extension would allow the individual enabling of each measurement and also assignment of the type of measurement per channel, e.g. a generic sensor input that supports analog pH, EC, ORP, DOx, TDS and so on.

1 Like

That’s a good point. One Input module should suffice then. I can add in the ability to select which ACD channel each input is connected to and all the sanity checks for user input. These are good exercises to see if what we want to accomplish breaks Mycodo, then I can update the code to allow it to work.

I just went through your ph_ec input and made some changes that I’d like you to review. I decided to only work on the combo input because it supports both sensors and you an independently select which measurements you want to store, so you can turn it into a single-sensor input this way. I also found some strange stuff in the module and went to reference the anyleaf Input to see what you were referencing and I found where the strangeness came from. It seems the person who made the anyleaf Input was a bit confused about the database storage mechanism. In any case, I made some changes that remove unnecessary code and make it a bit easier to read.

Essentially, I removed all the get_custom_option() functions in the initialization, as all these values are pulled from the database and set to the variables when setup_custom_options() is called. As an example, if you have a custom option with an id of “ph_cal_v1” and the default value is 5, then self.ph_cal_v1 will be set to 5 if a user has never saved a value. They are free to set a value in the dropdown settings of the Input, and if they save it as 6, then this value is stored in the database as 6 which will be automatically set the next time the Input is activated. Now, the set_custom_option() function saves values in the exact same dictionary as the user input, so if you have a custom option with an id of “ph_cal_v1” and you execute set_custom_option(“ph_cal_v1”, 7), this overwrites the current value of 6 in the database.

I noticed you modeled this module off the anyleaf module that was storing data in the database with the same names as the custom_options that were set, effectively overwriting the user options. Now, this is actually a pretty clever way of doing it, as the user can see what the calibration changed the values to. But, there was no way to reset the calibration back to the defaults without deleting and adding a new Input.

Here are the other changes I made:

  1. Add ability to select which ADC channel each sensor is connected to.
  2. Add clear calibration slots actions (which required the addition of a delete_custom_option() function)

I just pushed my changes and you can test the new module by upgrading to master.

I also just did a review of the Anyleaf pH and ORP Inputs and made some changes, similarly to your Input, to remove unnecessary code and also added the ability to select an external temperate compensation measurement.

I was able to upgrade to master, and your changes appear to work well. Will monitor it over the course of the next day or so and report back.

Just reading the code, found one minor correction at line 472:

    v = self.get_volt_data(int(self.adc_channel_ec))  # pH

The comment should say “# EC”.

Yes, I did copy most of the anyleaf module code over. Following your changes, I see that the calibration values don’t update until the Inputs page is refreshed in the browser. The new calibration does take effect at the next sensor read, as it should. The “live update” of the calibration data was a nice feature that I liked from the anyleaf module, but it’s not a dealbreaker if it’s not there anymore. I do like the calibration reset button.

Fixed. Thanks.

This is likely due to timing and the database values not changed until after the page has been requested and rendered.

I’m not sure what you mean, as there’s been no change in how data is stored during calibration.

In the old anyleaf module, the moment you click on any of the ‘Calibrate’ buttons, the ‘Cal data’ fields for that slot would immediately be updated with the voltage, pH/EC and temperature values. So it was nice UI feedback to know that the new calibration data had “taken effect”, without having to check the Data > Live page. Like you said, the author of the anyleaf module was pretty clever to do this – but it doesn’t matter, this feature (bug?) isn’t crucial to the utility of the input.

What I’m saying is I haven’t changed anything related to how data is saved or presented. None of my changes should have affected that behavior.

Has something changed about your setup, such as using an external temperature measurement for compensation?

Indeed, I don’t see any of your changes changing this behavior… unless the block of code containing all the get_custom_option() calls (that you’d removed) in initialize_input() was somehow getting called repeatedly in the custom input I shared?

No, I haven’t changed my setup – I am still connecting the same temperature source I previously used to your revised input module. I can load my old custom input again (I removed it to upgrade to master) if you’d like me to make a 1-to-1 comparison.

initialize_input() is only called once during activation, so wouldn’t have an affect later when a calibration is called. I just reviewed all the code executed and I can see no reason why you would experience different behaviors. The frontend should not be able to load until the calibration function has finished executing, at which point the new values would already be stored in the database.

I’ll create a test Input and experiment later. It’s desirable to have consistent behavior, and I would like to know what circumstances are responsible for the altered behavior.

1 Like

So, I tried importing my old custom input again (changed the name to avoid conflicts). I don’t see the old behavior any more – it behaves exactly the same as your revised version. Perhaps I’m misremembering, or the behavior changed between v8.9.2->master.

So I reviewed the code and conducted some tests. When a Custom Action button is pressed for an Input, a thread is spawned that executes the specified function in the Input. It does not wait for the function to complete before the web UI is reloaded as I originally thought. This means, depending on how long it takes for the function to execute, the web UI may or may not reload before the function completes. Because of this, you may see, for this particular Input, the new value display in the web UI immediately after pressing the button, or you may have to refresh the page to see the updated value.

Now, I do see value in allowing the user to specify whether a thread is spawned for the function or if the web UI should wait until the function completes before reloading. If a thread is not spawned, you can even return information to the UI for the user to read, such as a status message. So, I modified the system to allow this to happen. For Custom Action buttons, there is now the optional option “wait_for_return”, which when set True will not spawn a thread and will instead wait for the function to end and display in the UI status message the string that is returned. If your Custom Action is a long-running process, it makes sense to use the default behavior and set “wait_for_return” to False (or don’t even include the option, which defaults to False).

All the calibration Actions now have “wait_for_return” set True so the Custom Options will always be immediately updated with the calibrated values after the UI reloads and there won’t be any variability in this behavior as there was previously.


A post was split to a new topic: Error when creating new custom input module

@Roberto seems to be having issues with the ADS1115 pH/EC Input over on this topic:

I don’t have these types of pH/EC sensors, so I can’t be of much use diagnosing issues with the Input/Hardware interface. Could any of you provide some insight to help solve the problem?

I thought it’s more appropriate to answer here.

I’ve done the experiment.
With EC probe inside the solution i have no chanche to measure PH, sooner or later it stabilizes to 2.14V for any ph value.
What I could observe is that the ph meter i use as a reference was not affected by this interference and measures correctly.
Powering up EC sensor, it connects the power supply output to the water.
Connecting the GND of raspberry with a wire inside the water i obtain the same effect.
With two separate power supply i don’t have interferences.
I solved this with your method, using two 1uF capacitors for both wires of the probe.
1 uF fit perfects, i have same voltage on the output.
Thank you!
My question is why producer lost the capacitors or at least warned on the manual?

I prepared different solutions for EC and PH using my EC and PH tester as reference.
I put the points in a program called graph, that found the equations.
I edit it a bit for have same measurements of my reference tester.
Trying to edit your script is too difficult at the moment, so i will use regular ADS1115.
I thought i don’t need temperature compensation, because tha variation is minimal.
For re-calibration i don’t know, at the moment the probes are still calibrated.
I think it will be enough to calibrate for a single value and translate the function curve.
But i’have a strange effect:
I’ve just EC1,4 PH4 PH7 calibration solutions, i got the others ​​by adding ph- or fertilizer for EC.
With EC calibration solution i don’t have same voltage with water and fertilizer with same EC, difference about 0,2v.
But both EC measure the same value in my reference tester.
Tried many times.
So i can’t use the calibration solution to calibrate.
I need to check in the long time if it works properly.
If you have any other suggestions let me know.

That’s because they sell an “isolator module” to solve this… Now it just so happens that the capacitors work for isolating the EC from pH, I don’t know if it’s a good solution for every possibility. I do not have the isolator module so I couldn’t say how well it works compared to the capacitor hack.

Are both probes in the same physical location in the solution? The EC can be affected by boundary conditions. Don’t mount it too close to the sidewall of the container. The “range” can be different for the SEN0244 probe compared to your reference probe depending on the sensor design.

Works well for me.

I noticed it, I have to keep it a little higher from the bottom.
But the containers i use for calibration solutions are very small compared to the ones i use for water and fertilizer.
Maybe this?
I will start the script for peristaltic pump and refill.