opener003.yaml
esphome:
name: opener003
friendly_name: opener003
libraries:
- SPI
includes:
- WindowOpener.h
esp32:
board: esp32dev
switch:
- platform: custom
lambda: |-
auto my_window_opener = new MyWindowOpener();
App.register_component(my_window_opener);
return {my_window_opener};
switches:
name: "WindowSwitch_003"
id: window_switch_003
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "**********************"
ota:
password: "**********************"
wifi:
ssid: *******************
password: *********************
use_address: 192.168.110.12
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Opener003 Fallback Hotspot"
password: "*********"
captive_portal:
WindowOpener.h (The current version uses a different motor driver IC than what I described in the original post, but you should get the general idea)
#include "esphome.h"
#include "SPI.h"
using namespace esphome;
#define DIR 17
#define PWM 16
#define DIS 4
#define nFAULT 0
#define nSLEEP 21
#define SPICLK 1000000
#define WRITE 0x40
#define READ 0x00
#define DEVICE_ID 0
#define MOTOR_CURRENT 34
#define REG_DEVICE_ID 0x00
#define REG_FAULT_SUMMARY 0x01
#define REG_STATUS1 0x02
#define REG_STATUS2 0x03
#define REG_COMMAND 0x08
#define REG_SPI_IN 0x09
#define REG_CONFIG1 0x0A
#define REG_CONFIG2 0x0B
#define REG_CONFIG3 0x0C
#define REG_CONFIG4 0x0D
#define CMD_CLR_FLT 0x80
#define BUTTON_OPEN 2
#define BUTTON_CLOSE 27
// Custom binary output, for exposing binary states
class MyWindowOpener:public Component, public Switch {
public:
void writeRegister( uint8_t addr, uint8_t value )
{
uint16_t valueToWrite = (addr & 0x3f );
valueToWrite <<= 8;
valueToWrite |= value;
digitalWrite(SS, LOW); // Set Slave Select pin LOW to start SPI communication
delay(1);
SPI.transfer16(valueToWrite); // Send and receive 16-bit word
delay(1);
digitalWrite(SS, HIGH); // Set Slave Select pin HIGH to end SPI communication
}
uint16_t readRegister( uint8_t addr )
{
uint16_t returnValue = 0;
uint16_t valueToWrite = (addr & 0x3f ) || READ;
valueToWrite <<= 8;
digitalWrite(SS, LOW); // Set Slave Select pin LOW to start SPI communication
delay(1);
returnValue = SPI.transfer16(valueToWrite); // Send and receive 16-bit word
delay(1);
digitalWrite(SS, HIGH); // Set Slave Select pin HIGH to end SPI communication
return returnValue;
}
void setup() override {
const int pwmFrequency = 10000; // PWM frequency in Hz
const int pwmResolution = 8; // PWM resolution in bits (1 to 16 bits)
pinMode(2, OUTPUT);
pinMode(DIR, OUTPUT);
pinMode(nSLEEP, OUTPUT);
pinMode(DIS, OUTPUT);
pinMode(nFAULT, INPUT);
pinMode(MOTOR_CURRENT, ANALOG);
pinMode(BUTTON_CLOSE, INPUT);
pinMode(BUTTON_OPEN, INPUT);
ledcSetup(0, pwmFrequency, pwmResolution);
ledcAttachPin(PWM, 0);
digitalWrite( 2, 1 );
digitalWrite( DIR, 1 );
ledcWrite(0, 255);
digitalWrite( nSLEEP, 0 );
digitalWrite( DIS, 0 );
pinMode(SCK, OUTPUT);
pinMode(MISO, INPUT);
pinMode(MOSI, OUTPUT);
pinMode(SS, OUTPUT);
SPI.begin(SCK, MISO, MOSI, SS);
SPI.setBitOrder(MSBFIRST); // Set bit order: Most Significant Bit first
SPI.setDataMode(SPI_MODE1); // Set data mode: SPI_MODE0, SPI_MODE1, SPI_MODE2, or SPI_MODE3
SPI.setClockDivider(SPI_CLOCK_DIV128); // Set clock speed: SPI_CLOCK_DIV2 to SPI_CLOCK_DIV128
SPI.setFrequency(100000);
delay(100);
int fault = digitalRead( nFAULT );
digitalWrite( nSLEEP, 1 );
while( fault == 1 )
{
fault = digitalRead( nFAULT );
}
writeRegister(REG_COMMAND, CMD_CLR_FLT );
myStateKnown = false;
stopClosingAt = millis();
stopOpeningAt = millis();
firstLoop = 1;
}
void write_state(bool state) override
{
digitalWrite( 2, state );
// if( !myStateKnown || ( myState != state ) )
{
if( state )
{
openDelay = initialDelay;
closeDelay = 0;
}
else
{
openDelay = 0;
closeDelay = initialDelay * 1.5;
}
ESP_LOGD( "DEBUG", "close: %d", closeDelay );
ESP_LOGD( "DEBUG", "open: %d", openDelay );
myState = state;
myStateKnown = true;
}
// Acknowledge new state by publishing it
publish_state(state);
}
typedef enum
{
OPEN, CLOSE, OFF
} BridgeState;
void setHBridge( BridgeState state )
{
if( state == OPEN )
{
digitalWrite( DIR, 0 );
ledcWrite(0, 255 ) ;
}
else if( state == CLOSE )
{
digitalWrite( DIR, 1 );
ledcWrite(0, 255 ) ;
//openDelay--;
}
else
{
digitalWrite( DIR, 0 );
ledcWrite(0, 0 ) ;
}
}
void loop() override
{
// This will be called by App.loop()
//ESP_LOGD( "DEBUG", "first loop: %d", firstLoop );
unsigned long timeElapsed = 0;
if( firstLoop )
{
//firstLoop = 0;
firstLoop--;
lastTime = millis();
}
else
{
publish_state(state);
// ESP_LOGD( "DEBUG", "publish %d", state);
timeElapsed = millis() - lastTime;
lastTime = millis();
if( digitalRead( BUTTON_CLOSE ) == 0 )
{
write_state( false );
}
if( digitalRead( BUTTON_OPEN ) == 0 )
{
write_state( true );
}
if( closeDelay > 0 )
{
//digitalWrite( 2, (millis()/250) % 2 );
setHBridge( CLOSE );
closeDelay -= timeElapsed;
// ESP_LOGD( "DEBUG", "close: %d", closeDelay );
// ESP_LOGD( "DEBUG", "open: %d", openDelay );
}
else if( openDelay > 0 )
{
setHBridge( OPEN );
openDelay -= timeElapsed;
// ESP_LOGD( "DEBUG", "close: %d", closeDelay );
// ESP_LOGD( "DEBUG", "open: %d", openDelay );
}
else
{
setHBridge( OFF );
}
}
}
protected:
static const int initialDelay = 18 * 1000;
int openDelay;
int closeDelay;
unsigned long stopClosingAt;
unsigned long stopOpeningAt;
unsigned long lastTime;
int firstLoop;
int reasonableActuatorTime;
bool myState;
bool myStateKnown;
const int LEFT = 1;
const int RIGHT = 3;
};