[RELEASE] ST_Anything - Arduino/ESP8266/ESP32

Glad you like the icons! Those were a bit of a hidden treasure especially for you!

Not sure about you Wemos D1 pin issues, as I do not have any of those boards.

How many ultrasonic sensors are you attempting to connect to one microcontroller? Just two? It would seem like you could identify 2 pairs of pins that should work.

The Tx/Rx pins are usually connected to the onboard USB chip, making it somewhat difficult to use those pins for anything except USB communications (especially if you have a USB cable plugged into the board!!!) Also, the fact that ST_Anything sends ASCII text out the Tx pin for the Arduino Serial Debug Window makes it hard to use those pins for anything but USB communications.



Here is the Arduino IDE’s pin definitions for the Wemos D1 R2. This file is what allows your code to use the friendly pin names (like D1, D2, etc…) in your sketch. Be sure to NOT override any of these definitions in your sketch as that will mess up the mapping of friendly names to actual ESP8266 GPIO pins.

    #define PIN_WIRE_SDA (4)
    #define PIN_WIRE_SCL (5)

    static const uint8_t SDA = PIN_WIRE_SDA;
    static const uint8_t SCL = PIN_WIRE_SCL;

    #define LED_BUILTIN 2 //new ESP-12E GPIO2

    static const uint8_t D0   = 3;
    static const uint8_t D1   = 1;
    static const uint8_t D2   = 16;
    static const uint8_t D3   = 5;
    static const uint8_t D4   = 4;
    static const uint8_t D5   = 14;
    static const uint8_t D6   = 12;
    static const uint8_t D7   = 13;
    static const uint8_t D8   = 0;
    static const uint8_t D9   = 2;
    static const uint8_t D10  = 15;
    static const uint8_t D11  = 13;
    static const uint8_t D12  = 12;
    static const uint8_t D13  = 14;
    static const uint8_t D14  = 4;
    static const uint8_t D15  = 5; 

Thank you Dan!
Yes, I will only be using 2 sensors for now.
Should I add that to the sketch? The board is marked with the friendly names like D0, D1, D2 etc. I tried many combinations but the only pair that I could get to function properly are D6 and D7.
I won’t waste your time with this. I’ll spend some time trying to figure this out and in the process, will hopefully learn a few things about sketches etc. If I don’t come right, I’ll simply use two boards with one sensor each.
I must tell you that the water level sensor is incredibly accurate. It is actually mind-boggling that on a 3000l/790gal. tank, it registers the +/- 8 litres/2 gal. used when a toilet is flushed. I can’t get my head around.

I need to do this Ultrasonic tank sensor for my 2000gal tank!! Great Idea… Is this the sensor you have and will it work with 3.3V or only 5V as described? I want to use it with my ESP32.


No, do not add that to your sketch. That file is automatically included by the Arduino IDE when you tell the IDE which board you’re compiling the sketch for.

I just wanted to make sure your sketch wasn’t trying to redefine any of these, as that could result in the use of the wrong GPIO pins.

Check out the original thread for the water tank level work by @Saif76

1 Like

Yes, that is the one.
I’m not sure what the voltage of the sensor is. Some websites state it as 3.3V, others as 5V.
For my water level sensor application, I use it with a NodeMCU board at 3.3V and it works perfectly.

1 Like

I ordered the ultrasonic sensor, should be here in a couple days. How did you get the percentage full on your icon from the ultrasonic polling sensor, I didnt see anywhere you could map the tank height to the sensor measured distance? Or is that math in the Device handler?

static st::PS_Ultrasonic sensor1(F(“ultrasonic1”), 60, 0, PIN_ULTRASONIC_T, PIN_ULTRASONIC_E)

It is in the “Child Ultrasonic Sensor” DTH. You may need to customize it for your specific use case, as a cylindrical tank is assumed. This was originally developed by @Saif76 and he was gracious enough to add it to the ST_Anything project.

Apologies if this has been discussed previously but I haven’t seen it. I have ST_Anything set up and running - it’s been great, even have an instance going to Hubitat.

The one thing I have seen on ST for two different “nodes” is the when I have a motion sensor (PIR) it is reporting active and then inactive in the recent logs every check in w/ 10 secs in between. As you can imagine this is problematic for any automation. What’s even more interesting is that it doesn’t show up in the serial monitor when looking for events.

Any suggestions?

What PIR motion sensor do you have attached to your microcontroller? Some of them have a jumper to select what type of output signal is desired: constant vs toggling.

Question about how ST Anything connects. I have a feather MO wifi board that’s far away and at the edge of wifi connectivity. Sometimes it locks up the board if it has trouble connecting. To get it back up again I have implemented the “Sleepy_Dog” watchdog timer but the max that can wait before a board reset is 16 seconds. So if its stuck connecting for more that 16sec the board resets. But its seems to be reseting several times a day erasing my sketch data each time.

So what is the behavior of ST Anything: Does it reconnect every time its time to send data and after st::Everything::run(); is called in the loop? Then disconnect…

Basically I want to enable the 16sec watchdog only during the time its connecting then disable. Or some how give up trying to connect if a certain time is elapsed (<16sec) and try again at the next polling interval.

Is the Feather M0 using the ATWINC1500 wifi chip? If so, I assume you’re using the WiFi101 library, correct?

If so, I am sorry to say that I have never been able to get the Arduino MKR1000 or an Arduino MEGA + ATWINC1500 WiFi Shield (both use WiFi101) to work reliably. They just never seem to stay connected to the WiFi network reliably, resulting in a lock-up / non-responsive microcontroller. I have tried to add reconnect logic, but I have never gotten the ATWINC1500 WiFi ship to be reliable using the WiFi101 library. If you look through the Arduino WiFi101 library’s GitHub repository, you’ll see a long history of ‘issues’ with this combination.

As for how ST_Anything works… During the sketch’s setup() routine, the WiFi connection is established. This creates a server and client. The server is used to receive data from the hub, while the client is used to send data to the hub. The WiFi connection is never dropped intentionally. The software does try to detect a loss of connectivity and then tries to reconnect. With the WiFi101 library, I have never seen very good results.

All of the networking code is contained in SmartThingsWiFi101.h and .cpp. If you can improve upon the logic, I am all ears! I would love to have another developer try to improve the reliability of this particular WiFi solution.

Yep, you mentioned that issue with ATWINC1500 before and I wasn’t having the lock ups until I moved this thing far away. I need that ufL connector the Feather MO board has so I can put an antenna on it otherwise I would use the esp32. Anyone now of a ESP32 with onboard LiPo charger and ufL connector?

This has the Lipo… Maybe you could add an WiFi access point closer to the location for this board?

Hendre, I got my sensor and playing with the device handler, Will you be able to share the changes you made to get the “Remaining” liters on the tile?

I figured it out, its working, at least working on my kitchen table. I had device name wrong. Also added “Air Gap” to the preferences to account for the distance the sensor sits above the water when the tank is full.


That Feather ESP32 's the same board I’m building up for my solar powered tank level sensor and temp . Gonna see if it can cover the 400feet from my tank to my ubiquiti outdoor access point which sits 15ft up on a poll. I think it can do it. The MO board with antenna is about 700+ feet away thru some trees, also using small solar panel and LiPo battery.

Now it is my turn to ask if you would care to share your changes?

It has been mentioned previously, but this may help others: The sensor becomes erratic when the level is less than about 4 inches from the sensor. I have not been able to determine exactly how it responds, but it appears to me that once the object gets closer that 4 inches, the distance reported increases (instead of decreasing). That happens until the object (water level) is about an inch from the sensor, at which point it gets totally crazy.

Point I wish to make - when you play around with the sensor to test your code, don’t bring the object closer to about 4 inches from the sensor.

Yep, specs on that sensor are 25 to 450cm (10 - 177in). I might run into a similar issue as when full I think the sensor will be about 6in from the top of the water. Here’s the changes I made to the device handler and the code. Include airgap preference and some tile additions. I do the inputs and calcs in CM then convert to gallons.

 metadata {
    	definition (name: "Child Ultrasonic Sensor, Tank Gallons", namespace: "Me", author: "Me") {
    		capability "Sensor"
    		attribute "lastUpdated", "String"
            attribute "ultrasonic", "Number"

    		command "generateEvent", ["string", "string"]

    	tiles(scale: 2) {
    		multiAttributeTile(name: "ultrasonic", type: "generic", width: 6, height: 4, canChangeIcon: true) {
    			tileAttribute("device.ultrasonic", key: "PRIMARY_CONTROL") {
    				attributeState("ultrasonic", label: '${currentValue}%', unit:"%", defaultState: true, 
    						backgroundColors: [
                            		[value: 80, color: "#44b621"], // Dark Green
                                    [value: 60, color: "#f1d801"], // Dark Yellow
                                    [value: 40, color: "#d04e00"], // Orange
                                    [value: 20, color: "#bc2323"]  // Red
                tileAttribute ("device.gallons", key: "SECONDARY_CONTROL") {
            		attributeState "power", label:'Remaining: ${currentValue} gallons', icon: "http://cdn.device-icons.smartthings.com/Bath/bath6-icn@2x.png"
     		valueTile("gallons", "device.gallons", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
        			state "default", label:'${currentValue} Left', unit:"gal", backgroundColor:"#000000"
            valueTile("capacity", "device.capacity", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
        			state "default", label:'${currentValue} Total', unit:"gal", backgroundColor:"#d04e00"
            valueTile("lastUpdated", "device.lastUpdated", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
        			state "default", label:'Last Updated ${currentValue}', backgroundColor:"#ffffff"
        preferences {
            input name: "height", type: "number", title: "Height", description: "Enter height of tank full level in cm", required: true
            input name: "diameter", type: "number", title: "Diameter", description: "Enter diameter of tank in cm", required: true
            input name: "airgap", type: "number", title: "AirGap", description: "Enter Sensor Height Above Full Level", required: true
    def generateEvent(String name, String value) {
    	log.debug("Passed values to routine generateEvent in device named $device: Name - $name  -  Value - $value")
        double sensorValue = value as float
        //Tank total Capacity
        def volume = 3.14159 * (diameter/2) * (diameter/2) * height // Volume of Tank
        double capacityLiters = volume / 3785 // divide by 3785 to get total capacity in gallons
        capacityLiters = capacityLiters as int  // Remove decimals
        sendEvent(name: "capacity", value: capacityLiters) // This is the total capacity when full
        // Ramaining in Tank
        double remainingVolume = (volume -  (3.14159 * (diameter/2) * (diameter/2) * (sensorValue-airgap))) / 3785 // divide by 3785 to get gallons
        remainingVolume = remainingVolume.round(2)
        sendEvent(name: "gallons", value: remainingVolume) // Use this for how much is left in the tank
        // Tank Percent full    
        double capacityValue = 100 - ((sensorValue-airgap)/height * 100 )  // Get the percent full
        if(capacityValue != 100)
        	capacityValue = capacityValue.round(2)
        	sendEvent(name: name, value: capacityValue)
        // Update lastUpdated date and time
        def nowDay = new Date().format("MMM dd", location.timeZone)
        def nowTime = new Date().format("h:mm a", location.timeZone)
        sendEvent(name: "lastUpdated", value: nowDay + " at " + nowTime, displayed: false)

    def installed() {