Gate domotisation Sentinel OpenGate 1 + Live states

Hello,

I’ve finished automating my 24V Sentinel OpenGate 1 gate, which is based on a PCB100 board.
Like many others, I control it using two relay modules connected to an ESP32 and plugged into the dry contacts. I power everything with a small USB transformer from an old iPhone (I started by drawing 5V from the serial port of the PCB100 board, but my relays were drawing too much current).

Speaking of the serial port (or at least what resembles it), I couldn’t get anything out of it regardless of the baud rate…

I then installed a reed switch with a magnet on the hinge to determine if the gate was open. It’s simple and effective, but I didn’t find it sufficient (even though it’s perfect for determining if the gate has been opened, for example, after disengaging the motors).

So with my multimeter I analyzed what was happening between the different terminals available (motors and flashing light):

For the motors, it’s -24V to 24V (with peaks up to almost +/- 36V). The voltage direction indicates the direction of the motor, opening or closing.
I had started simulating assemblies that could have allowed me to exploit this by converting -36V to 0V, 0V to 1.5V, and 36V to 3V, but I preferred not to implement this because, in the end, I realized there were simpler ways to do it (personal opinion).

For the flashing light, it’s approximately 0-24V (with peaks above 28V, questionable accuracy of my multimeter?..), so very good, always positive voltage, and in my opinion, with my limited electronics knowledge, it’s more easily usable/safe.

It might not seem like it at first glance, but by carefully observing the gate’s operation and the light, using this simple “measurement” allows you to know the gate’s operating status live, provided you activate the automatic closing feature (dip switch 4 auto set to ON)!

Flashing: Gate open in all cases (opening or closing)
Slow flashing: Motors in operation (opening or closing)
Fast flashing: Motors paused (opening or closing)
No more flashing: Gate closed

With a small voltage reducer setup (two resistors, 100K and 6.8K) followed by an S8050 NPN transistor connected to an ESP32 development board, I can now retrieve almost the entire gate state (OPENING, CLOSING, OPENING_PAUSED, CLOSING_PAUSED, AUTO_CLOSING)!

These components are very common and cost less than €1, I’d say. I already had them on hand, so I made do with them. I didn’t bother to see if another transistor would do the job better. My dry runs with a 19V power supply worked, so I went with that.

No ADC to poll at regular intervals here because, thanks to the transistor, I’m not trying to read a voltage, but it activates a GPIO live with each state change, which is crucial for detecting the blink frequency.

Here’s the setup diagram:

I sized my voltage reducer to reduce to 36V while still remaining well below the 3.3V maximum of the ESP32 logic.

And the small, hastily made breadboard that works very well:

And the integration into the box:

In terms of integration, everything happens in ESPHome, resulting in the following entities:

Since I’m a big fan of the bridge Using HomeKit and Siri on my phone, to control my home by voice, I chose to map Locks to my relay switches, so I have to have my phone unlocked to open the gate, but nothing is mandatory.

The logic is entirely in ESPHome, mainly thanks to the multi-click management and its timing options, and finally, a timeout management for the autoclose.
In principle:
I consider the gate closed on the first start, then I apply the control sequencing logic to 1 single button on the remote control.

If slow flashing → manual opening
If fast flashing → opening pause + start timeout
If slow flashing and timeout not expired → manual closing
If slow flashing and timeout → automatic closing
If fast flashing → closing pause + start timeout

And the logic loops back from there
If slow flashing → manual opening…

I didn’t bother to make the same electronic circuit for beam break detection for closing, so the timeout duration is fixed. I’ll see if I feel the need to achieve “perfection” in practice, but I doubt it.

For the really annoying closing in exclusive OR mode (if the left button is used to open, then to close, you also have to reuse the same button), I also fixed this bug in ESPHome. It doesn’t matter if I’ve opened the pedestrian or double-leaf mode, or if I close it using one of my two switches, it closes, period.

Here is my ESPHome configuration, I put aside the smartlocks, temperature sensor, wifi power part because that is not the goal and it is already long enough as it is:


binary_sensor:
  - platform: gpio
    pin:
      number: GPIO33
      mode: INPUT_PULLUP
    name: "Reed Portail"
    

  - platform: gpio
    id: light_portal_internal
    internal: true
    pin:
      number: GPIO32
      mode: INPUT_PULLUP
    filters:
      - invert:
    name: "Light Portal"
    on_multi_click:
    - timing:
        - ON for at most 0.4s
        - OFF for at most 0.4s
      then:
        - logger.log: "Pause autoclose, next action is to close if 30s timeout or manual open/close"
        - lambda: |-
            if( not id(timer_started)){
              id(timer_started)=true;
              id(timer_started_time) = id(ha_time).now().timestamp;
              id(portail_auto_close_in).publish_state(String(30).c_str());
            }else{
              id(timer_elapsed_seconds)= id(ha_time).now().timestamp - id(timer_started_time);
              id(portail_auto_close_in).publish_state(String(30 - id(timer_elapsed_seconds)).c_str());
            }

            if(id(is_opening_paused) ){
              id(is_opening)=false;
            }

            if( id(is_opening) ){
              id(is_opening_paused)=true;
              id(is_closing_paused)=false;
              id(portail_state_text).publish_state("OPENING PAUSED");
            }

            if( id(is_closing_paused) ){
              id(is_closing)=false;
            }

            if( id(is_closing) ){
              id(is_closing_paused)=true;
              id(is_opening_paused)=false;
              id(portail_state_text).publish_state("CLOSING PAUSED");
            }

            

    - timing:
        - ON for 0.3s to 0.9s
        - OFF for 0.3s to 0.9s
      then:
        - logger.log: "OPENING/CLOSING"
        - lambda: |-
            if( id(timer_started)){
              id(timer_started)=false;
              if( id(timer_elapsed_seconds) >=28 ){
                // AUTOCLOSE AFTER 30s
                id(portail_state_text).publish_state("AUTOCLOSING");
                id(portail_auto_close_in).publish_state(String(0).c_str());
                id(is_closing)=true;
                id(is_opening)=false;
                id(is_opening_paused)=false;
                id(is_closing_paused)=false;
                id(is_closed)=false;
              }
            }

            if( id(is_closed) ){
              id(portail_state_text).publish_state("OPENING");
              id(is_opening)=true;
              id(is_closing)=false;
              id(is_opening_paused)=false;
              id(is_closing_paused)=false;
              id(is_closed)=false;
            }
            if( id(is_opening_paused) ){
              id(portail_state_text).publish_state("CLOSING");
              id(is_closing)=true;
              id(is_opening)=false;
              id(is_opening_paused)=false;
              id(is_closing_paused)=false;
              id(is_closed)=false;
            }
            if( id(is_closing_paused) ){
              id(portail_state_text).publish_state("OPENING");
              id(is_opening)=true;
              id(is_closing)=false;
              id(is_opening_paused)=false;
              id(is_closing_paused)=false;
              id(is_closed)=false;
            }
           

    - timing:
        - OFF for at least 1.5s
      then:
        - lambda: |-
              id(portail_state_text).publish_state("CLOSED");
              id(is_opening)=false;
              id(is_closing)=false;
              id(is_opening_paused)=false;
              id(is_closing_paused)=false;
              id(is_closed)=true;
 

  - platform: copy
    source_id: light_portal_internal
    name: "Light Portail"
    id: portail_state
    filters:
      - delayed_off: 1.5s
    on_state:
      then:
        - lambda: |-
            if( ! id(portail_state).state ){
              id(portail_state_text).publish_state("CLOSED");
              id(is_opening)=false;
              id(is_closing)=false;
              id(is_opening_paused)=false;
              id(is_closing_paused)=false;
              id(is_closed)=true;
            }

switch:
  - platform: gpio
    pin: GPIO27
    id: relay_pieton
    name: "Portillon"
    icon: "mdi:gate"
    on_turn_on:
    - delay: 500ms
    - switch.turn_off: relay_pieton

  - platform: template
    name: "pieton partiel"
    id: pieton_partiel
    turn_on_action:
      then:
        - if:
            condition: 
              lambda: 'return id(portail_state).state;'
            then:
              # Si ouvert on referme
              lambda: |-
                if ( id(portail_last_mode).state=="DOUBLE BATTANT" ) {
                  // on ferme DOUBLE BATTANT
                  id(relay_portail_voiture).turn_on();
                  ESP_LOGD("main", "FERMETURE DOUBLE BATTANT");
                } else {
                  // on ferme PIETON
                  ESP_LOGD("main", "FERMETURE PIETON");
                  id(relay_pieton).turn_on();
                }
            else:
              - lambda : 'id(portail_last_mode).publish_state("PIETON");'
              # Sinon on ouvre
              - logger.log: "OPENING quick partial"
              - switch.turn_on: relay_pieton
              - delay: 7s
              - switch.turn_on: relay_pieton

  - platform: gpio
    pin: 
      number: GPIO26
    id: relay_portail_voiture
    name: "en_grand"
    icon: "mdi:gate"
    on_turn_on:
    - delay: 500ms
    - switch.turn_off: relay_portail_voiture

  - platform: template
    name: "portail voiture"
    id: portail_voiture
    turn_on_action:
      then:
        - if:
            condition: 
              lambda: 'return id(portail_state).state;'
            then:
              # Si ouvert on referme
              lambda: |-
                if ( id(portail_last_mode).state=="DOUBLE BATTANT" ) {
                  // on ferme DOUBLE BATTANT
                  id(relay_portail_voiture).turn_on();
                  ESP_LOGD("main", "FERMETURE DOUBLE BATTANT");
                } else {
                  // on ferme PIETON
                  ESP_LOGD("main", "FERMETURE PIETON");
                  id(relay_pieton).turn_on();
                }
            else:
              - lambda : 'id(portail_last_mode).publish_state("DOUBLE BATTANT");'
              # Sinon on ouvre
              - logger.log: "OPENING quick partial"
              - switch.turn_on: relay_portail_voiture

As for the implementation, I’m aware that a minimum knowledge of ESP32/ESPHome is required, as well as the ability to solder 3-4 times and understand what you’re doing. This is why I’m not trying to create a complete tutorial that would encourage a novice to embark on this project.

However, I believe I’ve documented it well enough to ensure it’s clear enough for anyone with sufficient experience/confidence to get started if they wish, assuming their own responsibility and hardware warranty.

Note that this logic should also be able to be applied to other gate brands with some adaptations to the timings…

There’s no negative voltage, just polarity swap. Between control board GND and motor wires there is either 0 and +24 or +24 and 0 or 0 and 0. With two inputs you can detect motor state.

1 Like

Nice to know, thanks !