sinricpro / esp8266-esp32-sdk

Library for https://sinric.pro - simple way to connect your device to Alexa, Google Home, SmartThings and cloud
https://sinric.pro
227 stars 121 forks source link

Garage Door opener #345

Closed LongingIngernot closed 8 months ago

LongingIngernot commented 8 months ago

Hello! I am trying to make a smart garage door opener with an esp32 and a relay with a contact sensor. My garage door's principle of operation is pretty simple: it uses a momentary push button to send a signal that tells the motor to open/close it. This is my sketch so far, but it only triggers the relay when pressing the close button and the contact sensor isn't showing any information.


  #define DEBUG_ESP_PORT Serial
  #define NODEBUG_WEBSOCKETS
  #define NDEBUG
#endif 

#include <Arduino.h>
#if defined(ESP8266)
  #include <ESP8266WiFi.h>
#elif defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
  #include <WiFi.h>
#endif

#include "SinricPro.h"
#include "SinricProGarageDoor.h"

#define WIFI_SSID         ""    
#define WIFI_PASS         ""
#define APP_KEY           ""      
#define APP_SECRET        ""  
#define GARAGEDOOR_ID     ""    "
#define BAUD_RATE         115200                     
#define DOOR_PIN            14
#define DOOR_STATE_PIN      27

bool lastDoorState;

bool onDoorState(String deviceId, bool &doorState) {
  Serial.printf("Garagedoor is %s now.\r\n", doorState?"closed":"open");
  digitalWrite(DOOR_PIN, doorState);
  delay(250);
  digitalWrite(DOOR_PIN, LOW);
  return true;
}

void checkDoorState() {
  bool currentDoorState = digitalRead(DOOR_STATE_PIN);
  if (currentDoorState == lastDoorState) return;
  Serial.printf("Door has been %s manually\r\n", currentDoorState?"open":"closed");
  lastDoorState = currentDoorState;
  SinricProGarageDoor &myGarageDoor = SinricPro[GARAGEDOOR_ID];
  bool success = myGarageDoor.sendDoorStateEvent(currentDoorState);
  if(!success) {
      Serial.printf("Something went wrong...could not send event to server!\r\n");  
  }
 }
void setupWiFi() {
  Serial.printf("\r\n[Wifi]: Connecting");

  #if defined(ESP8266)
    WiFi.setSleepMode(WIFI_NONE_SLEEP); 
    WiFi.setAutoReconnect(true);
  #elif defined(ESP32)
    WiFi.setSleep(false); 
    WiFi.setAutoReconnect(true);
  #endif

  WiFi.begin(WIFI_SSID, WIFI_PASS); 

  while (WiFi.status() != WL_CONNECTED) {
    Serial.printf(".");
    delay(250);
  }
  IPAddress localIP = WiFi.localIP();
  Serial.printf("connected!\r\n[WiFi]: IP-Address is %d.%d.%d.%d\r\n", localIP[0], localIP[1], localIP[2], localIP[3]);
}

void setupSinricPro() {
  SinricProGarageDoor &myGarageDoor = SinricPro[GARAGEDOOR_ID];
  myGarageDoor.onDoorState(onDoorState);

  // setup SinricPro
  SinricPro.onConnected([](){ Serial.printf("Connected to SinricPro\r\n"); }); 
  SinricPro.onDisconnected([](){ Serial.printf("Disconnected from SinricPro\r\n"); });
  SinricPro.begin(APP_KEY, APP_SECRET);
}

void setup() {
  Serial.begin(BAUD_RATE); Serial.printf("\r\n\r\n");

  pinMode(DOOR_PIN, OUTPUT);
  pinMode(DOOR_STATE_PIN, INPUT_PULLUP);
  setupWiFi();
  setupSinricPro();
}

void loop() {
  SinricPro.handle();
} 
sivar2311 commented 8 months ago

HI @LongingIngernot

... but it only triggers the relay when pressing the close button

This is unclear to me. Which button do you mean exactly?

the contact sensor isn't showing any information

As far as I can see, the function checkDoorState() never gets called in your sketch. Did you forget to call this function inside the loop function like this:

void loop() {
  SinricPro.handle();
  checkDoorState();
} 

I think the sketch is not yet fully adapted to your needs.

You should add a check of the current status in the onDoorState() that excludes invalid commands (e.g. the command "Close door" if the door is already closed and vice versa).

sivar2311 commented 8 months ago

Here is an example snippet that might help you:

void triggerDoor() {
  digitalWrite(DOOR_PIN, HIGH);
  delay(250);
  digitalWrite(DOOR_PIN, LOW);
}

bool doorIsOpen() {
  return digitalRead(DOOR_STATE_PIN) == true;
}

bool doorIsClosed() {
  return digitalRead(DOOR_STATE_PIN) == false;
}

void openDoor() {
  if (doorIsClosed()) triggerDoor();
}

void closeDoor() {
  if (doorIsOpen()) triggerDoor();
}

bool onDoorState(const String& deviceId, bool &doorState) {
  if (doorState) {
    closeDoor();
  } else {
    openDoor();
  }
  return true;
}

Note: The values true and false in the doorIsOpen and doorIsClosed functions may need to be swapped (depending on your wiring)

sivar2311 commented 8 months ago

Thanks for the clearification.

When you press the close button, onDoorState gets called with doorState set to false (which indicates to open the door) In this case the line digitalWrite(DOOR_PIN, doorState); is equivalent to digitalWrite(DOOR_PIN, LOW).

Please check the snippet i posted above.

LongingIngernot commented 8 months ago

I used your snippet and it works for me now. The only thing I don't like is after pressing open/close, the status changes even if doesn't open/close. Can this be fixed?

sivar2311 commented 8 months ago

The parameter doorState is a reference and can be used to report the current state back to the server. So you can alter the value inside the onDoorState function depending to the current door state. (see documentation)

In this case, a state change must (or should) be reported by sending a corresponding event (done by checkDoorState function)

LongingIngernot commented 8 months ago

I do not understand how I should implement that

sivar2311 commented 8 months ago

Try this snippet:

void triggerDoor() {
  digitalWrite(DOOR_PIN, HIGH);
  delay(250);
  digitalWrite(DOOR_PIN, LOW);
}

bool getDoorState() {
  return digitalRead(DOOR_STATE_PIN);
}

bool doorIsOpen() {
  return getDoorState() == true;
}

bool doorIsClosed() {
  return getDoorState() == false;
}

void openDoor() {
  if (doorIsClosed()) triggerDoor();
}

void closeDoor() {
  if (doorIsOpen()) triggerDoor();
}

bool onDoorState(const String& deviceId, bool &doorState) {
  bool doorShouldOpen = doorState == false;
  bool doorShouldClose = doorState == true;

  doorState = getDoorState(); // this reports the current state back to server

  if (doorShouldOpen) openDoor();
  if (doorShouldClose) closeDoor();

  return true;
}

void checkDoorState() {
  static bool lastDoorState;
  bool currentDoorState = getDoorState();
  bool doorStateHasNotChanged = currentDoorState == lastDoorState;

  if (doorStateHasNotChanged) return;

  lastDoorState = currentDoorState;

  SinricProGarageDoor &myGarageDoor = SinricPro[GARAGEDOOR_ID];
  myGarageDoor.sendDoorStateEvent(currentDoorState);
}

Note: The line doorState = getDoorState(); might be negated by using ! to doorState = !getDoorState(); (depending on how your sensor reports "open" and "close")

LongingIngernot commented 8 months ago

Thank you so much! Got it fully working now!