State machine in ESPhome

Hi,
I’m relatively new to ESPhome but I have already successfully created some instances carrying out rather simple jobs.

Now, I’m wondering if I can implement something more elaborate/if it is possible in ESPhome?

I need to explain the background a bit more before I can ask my questions.
More than a decade ago I built a control for the ventilation of my basement. It is basically in two parts: an electronics box with an Atmega328 running a simple state machine which receives its instructions via a couple of cables from a raspi on which a python program runs the real “control software” (which also a state machine - but it’s much easier to make changes to a python program than to reflash the Atmega). I want to replace the python program by an automation in HA, but that’s not the question here.
The electronics box comprises the possibility to switch into an “independent”/“manual” mode where the very basic settings can be set using two switches having three states each. One controls three levels of “power”, the second allows to choose the fan directions (constantly a->b, constantly b->a or switching every few minutes between a->b and b->a). All in all, there are about 10 gpios to set depending on the state.

Now my question: I would like to replace the electronics box by a new one based on an ESP32 and ESPhome. Reading the two switches using ADC gpios is no problem. My idea was to use global variables and if “independent/manual” mode is chosen (using a switch), to evaluate these global variables in a state machine, preferably using the lambda framework. But here I’m struggling.

So far, I programmed my entities by reading the documentation, looking for examples, coding, getting errors, (jumping back to start) until it worked as I desired.

But here I don’t have an idea how (and where) to start or if it is doable at all.

Could anyone help me by providing some hints how and where I have to add this to the yaml?

Thanks in advance!

mabe

My yaml (with the necessary hardware definitions) looks at the moment like this:

globals:
  - id: manual
    type: bool
    initial_value: "true"
  - id: stufe
    type: int
    initial_value: '1'
  - id: richtung
    type: int     # 0 = rot (Keller SO -> Werkstatt); 1 = Wechsel; 2 = grün (Werkstatt -> Keller SO)
    initial_value: '1'
# All my outputs on the UI:
# three yellow LEDs indication the voltage levels: gb1, gb2, gb3
# the first one is always on => no GPIO necessary
# GPIOs 4, 5
# one output indication the direction: richtung
# GPIO 18
output:
  - platform: gpio
    pin: GPIO4
    id: gb2
  - platform: gpio
    pin: GPIO5
    id: gb3
  - platform: gpio
    pin: GPIO18
    id: richtungsLED      

# All my internal outputs:
# two for controlling the output voltage of the LM317
# GPIOs 25, 26
# two for driving the inner fan
# GPIOs 19, 22
# two for driving the outer fans
# GPIOs 21, 23
  - platform: gpio
    pin: GPIO25
    id: lm317a 
  - platform: gpio
    pin: GPIO26
    id: lm317b 
  - platform: gpio
    pin: GPIO19
    id: innen_rt 
  - platform: gpio
    pin: GPIO22
    id: innen_gn 
  - platform: gpio
    pin: GPIO21
    id: aussen_rt 
  - platform: gpio
    pin: GPIO23
    id: aussen_gn 

light:
  - platform: binary
    name: "LED Stufe 2"
    output: gb2
  - platform: binary
    name: "LED Stufe 3"
    output: gb3    
  - platform: binary
    name: "LED Richtungsanzeige"
    output: richtungsLED

# All my internal outputs:
# two for controlling the output voltage of the LM317
# GPIOs 25, 26
# two for driving the inner fan
# GPIOs 19, 22
# two for driving the outer fans
# GPIOs 21, 23
# perhaps only temporary for development
  - platform: binary
    name: "LM a"
    output: lm317a
  - platform: binary
    name: "LM b"
    output: lm317b
  - platform: binary
    name: "Innen gn"
    output: innen_gn    
  - platform: binary
    name: "Innen rt"
    output: innen_rt
  - platform: binary
    name: "Aussen gn"
    output: aussen_gn    
  - platform: binary
    name: "Aussen rt"
    output: aussen_rt

# My switches from the UI
# two three-way switches on analog inputs for direction and output voltage
# a binary switch to change between manual and auto
sensor:
  - platform: adc
    pin: GPIO32 #li
    name: "Richtungswahl"
    attenuation: auto
    update_interval: 10s
    on_value_range:
      - below: 1.0
        then:
          - logger.log: "rot"
          - globals.set:
              id: richtung
              value: "0"
      - above: 1.0 
        below: 2.5
        then:
          - logger.log: "Wechsel"
          - globals.set:
              id: richtung
              value: "1"
      - above: 2.5
        then: 
          - logger.log: "grün"
          - globals.set:
              id: richtung
              value: "2"

  - platform: adc
    pin: GPIO35 #or
    name: "Stufenwahl"
    attenuation: auto
    update_interval: 1s
    on_value_range:
      - below: 1.0
        then:
          - logger.log: "Stufe 1"
          - globals.set:
              id: stufe
              value: "1"
#          - output.turn_off: gb2
#          - output.turn_off: gb3
      - above: 1.0 
        below: 2.5
        then:
          - logger.log: "Stufe 2"
          - globals.set:
              id: stufe
              value: "2"
#          - output.turn_on: gb2
#          - output.turn_off: gb3
      - above: 2.5
        then: 
          - logger.log: "Stufe 3"
          - globals.set:
              id: stufe
              value: "3"
#          - output.turn_on: gb2
#          - output.turn_on: gb3

# It's always good to know the wifi strength    
  - platform: wifi_signal
    name: "WiFi Signal Sensor"
    update_interval: 60s


binary_sensor:
  - platform: gpio
    id: modeswitch
    name: "Mode"
    pin: 
      number: GPIO34 #bn
      mode: 
        input: true
#        pullup: true - external pullup since GPIO34 does not have an internal one
    on_state:
      then:
        - if:
            condition:
              - binary_sensor.is_on: modeswitch
            then:
              - logger.log: "Jetzt manuell!"
              - globals.set:
                  id: manual
                  value: "true"
            else: 
              - logger.log: "automatisch"
              - globals.set:
                  id: manual
                  value: "false"

It’s kind of hard to understand your setup.
Simple scheme could help.

What kind of switches are red with ADC and why?

1 Like

Welcome @mabe

It sounds like you want to control a fan that has three speed levels, some direction/oscillation settings, and maybe a few custom modes/routines.

I would be looking at bringing as much of it into ESPHome as possible, including as much of the Home Assistant automations as makes sense.

I would be trying to port your device into one of the various fan component options.

I would have a good read of the following before you get too committed to a solution, especially the speed fan.

The switches are three way switches with the following positions:
1 - short to ground
2 - in the middle of a voltage divider i.e. Vcc /2
3 - short to Vcc
(Vcc = 3.3V)
With an ADC I can determine all three positions as I get three clearly distinguishable readings (0V / 1.65V / 3.3V). I don’t know how to do it easily in another way and this worked with my old circuit. It was not too difficult to get this into the yaml.

I’m sorry, but I don’t have a schematic. Essentially, it’s just two switches of this kind (“Stufenwahl” and “Richtungswahl”) and an ordinary switch to switch between external and internal control (“modeswitch” in the yaml) and about 10 outputs.

Thanks for pointing me to this. But I fear that it does not help as the fans are kind of special: They have a common anode and a “cathode” for each of the directions. I just had a quick look at the documents to which you referred to. Perhaps it would be doable. But I fear it will become a total mess because I will have then for each fan and each condition to check if “internal” or “external” control is chosen.

That’s why I wanted to put everything into a state machine once internal control is chosen.

I have to take some time and think about both ways of doing it. Just because the state machine has worked well so far does not mean it’s the only good solution.

Thanks for your valuable input.

mabe

1 Like

Ok. There is also template fan which could be a better fit.

That’s not technically three way switch, switch can only be on or off. Anyway that explains what you are looking for…

Sorry for using a wrong technical term. English is not my mother tongue.

No problem, neither mine… :+1:
I’m too tired to read your yaml right now, but maybe template number (min1, max 3) could be useful for your setup.

I started to implement the necessary logic. To determine the initial states of the switches I found the on_boot core automation. The next mention in the documentation is the on_loop automation. That’s what I was looking for. I’ll try to implement my logic/state machine in the on_loop.

Thanks for all the tips, in particular for the fan abstraction, @Mahko_Mahko !

1 Like

Matthias, Around 10 years ago I recall reading a blog of a German guy who constructed a ventilation system - he had it very well documented and controlled the fans based on the internal and external relative humidity. I was not able to find the page since. Was this you or was this based on that system?

But see no switches in Your config.
Binary sensors will be evaluated as soon as possible, see no reason to store their states in globals.

This is very important information for me. I didn’t know that. Thanks, a lot @Masterzz !

It seems that my term “3 way switch” has caused a lot of confusion. I use a switch like this one:
https://www.reichelt.de/de/de/shop/produkt/miniatur-kippschalter_ein-aus-ein_3_a_250_v-285989

So, it’s a “ON-OFF-ON” switch.

Using the following circuit


allows me to distinguish three states:
0V - 1.65V - 3.3V (at least somewhere around these theoretical values).

In my setup I use two of these switches with the ADC sensors “Richtungswahl” and “Stufenwahl”.

Perhaps there’s a better way to detect the three states. At the moment I don’t know any other.

Hm, I don’t remember if I published it somewhere. I rather don’t think so. I developed it on my own. But there are various such systems “floating” around. I remember having seen one long after in the German “Make” magazine.

My own documentation is somehow non existing. :slightly_frowning_face: Perhaps on some old hard disk. But I have at least a paper copy of everything. :grinning:

I started documenting some projects on hackaday.io. including my steps into ESPhome: https://hackaday.io/project/198352-experiences-with-esphome Mostly for my own memory.

1 Like

You likely shouldn’t need to be dealing with on_boot and on_loop that much. The idea is that the ESPHome components generally do that work for you. You don’t approach an ESPHome program as “set_up” and “loop”.

Sounds like you’re used to working at a lower cpp level but with ESPHome there would typically be higher level components you can use where you can either avoid or use cpp (lambda) sparingly. It’s not to say that you can’t do it at that level, but the idea is you often shouldn’t need to. That said if it’s your first ESPHome project then it’s probably ok to stay more in your comfort zone. Maybe this video is of some help (English though).

For example if you want something with three states you could use your ADC thresholds to drive the value of either a Template Select or a Text Template sensor. Then you can also drive further actions off of their values.

The select is a newer thing and the examples aren’t great though. But it would basically be a bit of lambda.

@Mahko_Mahko: Thanks a lot for all your input. I’ll have a look at it before I start my next project. I already made some smaller projects where I saw (and used) the benefit of the abstraction in ESPhome.

Trying ESPhome on this project had a different reason: I want to replace my old box and get rid of the cable for the “information transfer”. As I have about 10 outputs, an ESP8266 is not enough and I have to use an ESP32. However, with the ESP32 I had stability issues when using MQTT (before I got to know ESPhome). To get rid of MQTT (which is great, but more “work”) and since I had good experiences already with ESPhome, I gave it a try.

Indeed, it might be a problem that I have my old approach in mind and perhaps should do it differently. But somehow my mind is a bit blocked, here.

After I sorted out how to access the outputs from lambda and also the many error messages about incorrect indentations, it took me less than an hour to code the on_loop part (no on_boot part) to implement the functionality. It’s really simple and I used the interval component to regularly change direction. This is much easier than my old code.

For the moment, I’m satisfied. And I learnt a lot.

I think, this thread can be closed.

2 Likes

Just in terms of your question about state-machines, there are some state-machines available in Node-Red which might be interesting to check out. I know that it’s not your objective to have external control of the system, but they appear to be quite functional.
The one that I used is called " XState State-machine" (find it in github). I would love to see a state-machine in ESPHome, that would be the ultimate add-on.