Compare commits
11 Commits
8198d8f62e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
1f7c437c3e
|
|||
|
d4edf31a8b
|
|||
|
d0c2b3993b
|
|||
|
273cd9e434
|
|||
|
3b88f1e988
|
|||
|
9d3930af59
|
|||
|
3ef68a9539
|
|||
|
31861e7785
|
|||
|
b8581dcf31
|
|||
|
2809b1a167
|
|||
|
d3f0520123
|
50
5gModemPower.yaml
Normal file
50
5gModemPower.yaml
Normal file
@@ -0,0 +1,50 @@
|
||||
esphome:
|
||||
name: modempower
|
||||
|
||||
esp8266:
|
||||
board: sonoff_basic
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
fast_connect: true
|
||||
ap:
|
||||
ssid: modempower_rescue
|
||||
password: !secret rescue_ap_password
|
||||
|
||||
captive_portal:
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: !secret k5gModem
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret p5gModem
|
||||
|
||||
logger:
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin:
|
||||
number: GPIO0
|
||||
mode:
|
||||
input: true
|
||||
pullup: true
|
||||
inverted: true
|
||||
name: "5G Modem Power Button"
|
||||
on_press:
|
||||
- switch.toggle: relay
|
||||
|
||||
switch:
|
||||
- platform: gpio
|
||||
name: "5G Modem Power Relay"
|
||||
pin: GPIO12
|
||||
restore_mode: ALWAYS_ON
|
||||
id: relay
|
||||
|
||||
status_led:
|
||||
pin:
|
||||
number: GPIO13
|
||||
inverted: yes
|
||||
|
||||
49
docs/GENERAL_DEVICE.md
Normal file
49
docs/GENERAL_DEVICE.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# What is this:
|
||||
This is the general documentation for esphome configs deemed too short to deserve their own documantation. Stuff like the 5gModem, the sonoffStecker or the nbs-light-room are just too short for me to believe it necessary.
|
||||
|
||||
So, let's get through the basics of any esphome config developed by me.
|
||||
|
||||
# General configs
|
||||
Before I start, I should mention what I consider to be general configurations: modules like esphome, esp8266 or esp32, as well as wifi, logger, ota are all part of that. Nearly all of them require all of them (with exception to esp8266 and esp32 as they are mutually exclusive).
|
||||
|
||||
## esphome
|
||||
The `esphome` defines general points, and is required by any esphome config to function. Weirdly, in the last few years this has become a mere holder for `name` and maybe `version`.
|
||||
|
||||
For `name` I recommand something that actually give the device a name that is clear what it does.
|
||||
For `version` when used, I recommand using semantic versioning that makes sense. But if you actually use it or not is up to you.
|
||||
|
||||
## esp8266/esp32
|
||||
The `esp8266` or `esp32` has a special point about only being their to define the actually board being used.
|
||||
Most common boards for esp8266 that we use is the `nodemcuv2`. While for the esp32 it is the `wemos_d1_mini32` also sometimes referenced as `esp32dev`.
|
||||
|
||||
A special note about `esp32` is that they have multiple frameworks that are supported. This is defined as `esp32.framework.type`:
|
||||
- arduino
|
||||
- esp-idf
|
||||
|
||||
## wifi
|
||||
The wifi part is not confusing at all. Normally we use `ssid`, `password` and `fast_connect`. You will see that there is also a captive_portal defined at times should the esp somehow fail to connect to the wifi at all. This captive portal and it's own wifi that it will open in such an event can be used to rescue the device without having to manually flash it again.
|
||||
|
||||
SSID is the wifis name, while password is, well, its password. `fast_connect` just means that the esp will NOT attempt to verify that the network exists by scanning for it. This is important if you need to connect to a hidden wifi.
|
||||
|
||||
## logger
|
||||
We actually keep this empty. Just having it around is enough.
|
||||
|
||||
## api
|
||||
The api just defines the encryption key for the esphome native api used by esphome and home assistant.
|
||||
|
||||
## ota
|
||||
Ahh, the usual over the air update component.
|
||||
The platform used is the normal esphome platform used by the esphome cli as well as the esphome builder in HomeAssistant from there we define a password. Just to be sure.
|
||||
|
||||
## status_led
|
||||
The status led is specific to each esp device. but I use it usually to detect if a device is connected to the wifi or not.
|
||||
|
||||
# More device specific configuration
|
||||
This is the section for more device specific configuration options that might be important
|
||||
|
||||
## 5gModemPower
|
||||
This is a normal sonoff power relay. It has a pin defined for the relay. A pin defined for the actual button should you need to press it directly on the device and that's about it.
|
||||
|
||||
The relay that controls the power is controlled by an gpio pin (Number 12) and starts out as always on.
|
||||
|
||||
There is a binary sensor included for the button on the actual device. This is an inverted pullup pin on gpio 0, pressing it toggles the relay.
|
||||
@@ -0,0 +1,73 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins, automation
|
||||
from esphome.components import uart
|
||||
from esphome.const import (
|
||||
CONF_ID
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@nbsgames"]
|
||||
DEPENDENCIES=["uart"]
|
||||
|
||||
hoermann_door_ns = cg.esphome_ns.namespace('hoermann_door')
|
||||
HoermannDoor = hoermann_door_ns.class_('HoermannMainComponent', cg.Component)
|
||||
StopPolling = hoermann_door_ns.class_('StopPollingAction', automation.Action)
|
||||
StartPolling = hoermann_door_ns.class_('StartPollingAction', automation.Action)
|
||||
|
||||
CONF_UART_ENTRY = "uart_connection"
|
||||
CONF_TX_PIN = "tx_pin"
|
||||
CONF_LOG_LEVEL = "log_level"
|
||||
|
||||
#define LL_OFF 0
|
||||
#define LL_ERROR 1
|
||||
#define LL_WARN 2
|
||||
#define LL_INFO 3
|
||||
#define LL_DEBUG 4
|
||||
|
||||
CONF_LOG_LEVELS = {
|
||||
"NONE": 0,
|
||||
"ERROR": 1,
|
||||
"WARN": 2,
|
||||
"INFO": 3,
|
||||
"DEBUG": 4,
|
||||
}
|
||||
|
||||
def validate_config(config):
|
||||
return config
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.COMPONENT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(HoermannDoor),
|
||||
cv.Required(CONF_UART_ENTRY): cv.use_id(uart.UARTComponent),
|
||||
cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_LOG_LEVEL, default="INFO"): cv.enum(CONF_LOG_LEVELS, upper=True),
|
||||
}),
|
||||
validate_config
|
||||
)
|
||||
|
||||
ACTION_SCHEMA = automation.maybe_simple_id({
|
||||
cv.GenerateID(): cv.use_id(HoermannDoor)
|
||||
})
|
||||
|
||||
@automation.register_action("hoermann_door.stop_polling", StopPolling, ACTION_SCHEMA)
|
||||
async def stop_polling(config, action_id, template_arg, args):
|
||||
parent = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, parent)
|
||||
|
||||
@automation.register_action("hoermann_door.start_polling", StartPolling, ACTION_SCHEMA)
|
||||
async def start_polling(config, action_id, template_arg, args):
|
||||
parent = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, parent)
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
code = await cg.get_variable(config[CONF_UART_ENTRY])
|
||||
cg.add(var.set_seriel_connection(code))
|
||||
|
||||
code2 = await cg.gpio_pin_expression(config[CONF_TX_PIN])
|
||||
cg.add(var.set_tx_on_pin(code2))
|
||||
|
||||
log_level = config[CONF_LOG_LEVEL]
|
||||
cg.add(var.set_log_level(log_level))
|
||||
@@ -3,18 +3,23 @@ import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.components import cover
|
||||
from esphome.const import CONF_ID
|
||||
from . import HoermannDoor
|
||||
|
||||
hoermann_cover_ns = cg.esphome_ns.namespace('hoermann_door')
|
||||
HoermannDoor = hoermann_cover_ns.class_('HoermanDoor', cover.Cover, cg.Component)
|
||||
HoermannCover = hoermann_cover_ns.class_('HoermannDoor', cover.Cover, cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(HoermannDoor)
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
CONF_HOERMANN_CONTROLLER = "hoermann_controller"
|
||||
|
||||
CONFIG_SCHEMA = cover.cover_schema(HoermannCover).extend({
|
||||
cv.Required(CONF_HOERMANN_CONTROLLER): cv.use_id(HoermannDoor)
|
||||
})
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield cover.register_cover(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await cover.register_cover(var, config)
|
||||
|
||||
controller = await cg.get_variable(config[CONF_HOERMANN_CONTROLLER])
|
||||
cg.add(var.set_emulator_component(controller))
|
||||
|
||||
cg.add_library("plerup/EspSoftwareSerial", "8.2.0")
|
||||
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "door_singleton.h"
|
||||
|
||||
#ifdef USE_COVER
|
||||
//#ifdef USE_COVER
|
||||
|
||||
#include "esphome.h"
|
||||
#include "Arduino.h"
|
||||
#include "hoermann.h"
|
||||
#include "hciemulator.h"
|
||||
//#include "cover.h"
|
||||
|
||||
@@ -13,14 +11,13 @@
|
||||
#define TX_ON 25
|
||||
//#define configMAX_PRIORITIES 25
|
||||
|
||||
#define TAG "hoermann_door"
|
||||
|
||||
namespace esphome {
|
||||
namespace hoermann_door {
|
||||
|
||||
void modBusPolling(void *parameter);
|
||||
class HoermanDoor : public Component, public cover::Cover
|
||||
class HoermannDoor : public Component, public cover::Cover
|
||||
{
|
||||
private:
|
||||
HoermannMainComponent* mainComponent;
|
||||
public:
|
||||
|
||||
cover::CoverTraits get_traits() override {
|
||||
@@ -48,52 +45,35 @@ public:
|
||||
|
||||
|
||||
if(pos == 1.0){
|
||||
HoermannSingleton::getInstance()->getEmulator()->openDoor();
|
||||
mainComponent->getEmulator()->openDoor();
|
||||
manual = false;
|
||||
}
|
||||
else if(pos == 0.0){
|
||||
HoermannSingleton::getInstance()->getEmulator()->closeDoor();
|
||||
mainComponent->getEmulator()->closeDoor();
|
||||
manual = false;
|
||||
}
|
||||
else{
|
||||
ESP_LOGD("Door controller", "Not yet supported");
|
||||
ESP_LOGD(COMP_TAG, "Not yet supported");
|
||||
}
|
||||
|
||||
}
|
||||
if (call.get_stop()) {
|
||||
HoermannSingleton::getInstance()->getEmulator()->stopDoor();
|
||||
mainComponent->getEmulator()->stopDoor();
|
||||
}
|
||||
//if(call.get_close()) {
|
||||
//emulator.closeDoor();
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void modBusPolling(void *parameter)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (lastCall > 0)
|
||||
{
|
||||
maxPeriod = _max(micros() - lastCall, maxPeriod);
|
||||
}
|
||||
lastCall = micros();
|
||||
emulator.poll();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
*/
|
||||
|
||||
void setup() override
|
||||
{
|
||||
HoermannSingleton::getInstance()->initializeEmulator();
|
||||
//HoermannSingleton::getInstance()->initializeEmulator();
|
||||
}
|
||||
|
||||
void set_emulator_component(HoermannMainComponent* component){
|
||||
this->mainComponent = component;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void open(){
|
||||
//emulator.openDoor();
|
||||
}
|
||||
@@ -109,7 +89,7 @@ void modBusPolling(void *parameter)
|
||||
u_int8_t pos = 255;
|
||||
void loop() override
|
||||
{
|
||||
u_int8_t position = HoermannSingleton::getInstance()->getEmulator()->getState().doorCurrentPosition;
|
||||
u_int8_t position = mainComponent->getEmulator()->getState().doorCurrentPosition;
|
||||
if(pos != position){
|
||||
this->position = (float) position / 200.0;
|
||||
this->publish_state();
|
||||
@@ -124,4 +104,4 @@ void modBusPolling(void *parameter)
|
||||
} // namespace hoermann
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
//#endif
|
||||
@@ -1,79 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome.h"
|
||||
#include "Arduino.h"
|
||||
#include "hciemulator.h"
|
||||
//#include "cover.h"
|
||||
|
||||
#define RS485 Serial2
|
||||
#define TX_ON 25
|
||||
//#define configMAX_PRIORITIES 25
|
||||
|
||||
#define TAG "hoermann_door"
|
||||
|
||||
|
||||
void modBusPolling(void *parameter);
|
||||
|
||||
class HoermannSingleton {
|
||||
public:
|
||||
HoermannSingleton() {
|
||||
emulator;
|
||||
}
|
||||
static HoermannSingleton* instance_;
|
||||
HCIEmulator emulator;
|
||||
TaskHandle_t modBusTask;
|
||||
bool hasBeenInitialized = false;
|
||||
|
||||
public:
|
||||
static HoermannSingleton* getInstance(){
|
||||
if(instance_ == nullptr){
|
||||
instance_ = new HoermannSingleton();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void initializeEmulator(){
|
||||
if(hasBeenInitialized) return;
|
||||
hasBeenInitialized = true;
|
||||
RS485.begin(57600, SERIAL_8E1, 16, 17);
|
||||
pinMode(TX_ON, OUTPUT);
|
||||
digitalWrite(TX_ON, LOW);
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
modBusPolling, // Function to implement the task
|
||||
"ModBusTask", // Name of the task
|
||||
10000, // Stack size in words
|
||||
NULL, // Task input parameter
|
||||
// 1, // Priority of the task
|
||||
configMAX_PRIORITIES - 1,
|
||||
&modBusTask, // Task handle.
|
||||
1 // Core where the task should run
|
||||
);
|
||||
}
|
||||
HCIEmulator *getEmulator(){
|
||||
return &emulator;
|
||||
}
|
||||
};
|
||||
|
||||
HoermannSingleton* HoermannSingleton::instance_ = nullptr;
|
||||
|
||||
//DoorManager *DoorManager::getInstance()
|
||||
|
||||
|
||||
volatile unsigned long lastCall = 0;
|
||||
volatile unsigned long maxPeriod = 0;
|
||||
void modBusPolling(void *parameter)
|
||||
{
|
||||
auto emulator = HoermannSingleton::getInstance()->getEmulator();
|
||||
while (true)
|
||||
{
|
||||
if (lastCall > 0)
|
||||
{
|
||||
maxPeriod = _max(micros() - lastCall, maxPeriod);
|
||||
}
|
||||
lastCall = micros();
|
||||
emulator->poll();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "hciemulator.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
|
||||
namespace esphome {
|
||||
namespace hoermann_door {
|
||||
|
||||
#define CHECKCHANGEDSET(Target, Value, Flag) \
|
||||
if ((Target) != (Value)) \
|
||||
{ \
|
||||
@@ -8,9 +12,9 @@
|
||||
}
|
||||
int hciloglevel = LL_DEBUG;
|
||||
|
||||
#define SOFTSERIAL 1
|
||||
//#define SOFTSERIAL 1
|
||||
|
||||
#ifdef SOFTSERIAL
|
||||
//#ifdef SOFTSERIAL
|
||||
#define Log(Level, Message) LogCore(Level, Message)
|
||||
#define Log3(Level, Message, Buffer, Len) LogCore(Level, Message, Buffer, Len)
|
||||
// LOGLEVEL
|
||||
@@ -22,24 +26,29 @@ void LogCore(int Level, const char *msg, const unsigned char *data = NULL, size_
|
||||
}
|
||||
if (data != NULL && datalen > 0)
|
||||
{
|
||||
String newmsg(msg);
|
||||
//std::string newmsg(msg);
|
||||
char* newmsg = (char*)malloc(strlen(msg) + (datalen * 3) + 1);
|
||||
strncpy(newmsg, msg, strlen(msg) + 1);
|
||||
newmsg[strlen(msg)] = '\0';
|
||||
char str[4];
|
||||
for (size_t i = 0; i < datalen; i++)
|
||||
{
|
||||
snprintf(str, sizeof(str), "%02x ", data[i]);
|
||||
newmsg += str;
|
||||
str[3] = '\0';
|
||||
strncat(newmsg, str, sizeof(str));
|
||||
}
|
||||
Serial.println(newmsg);
|
||||
ESP_LOGD(TAG, newmsg);
|
||||
free(newmsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(msg);
|
||||
ESP_LOGD(TAG, msg);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define Log(Level, Message)
|
||||
#define Log3(Level, Message, Buffer, Len)
|
||||
#endif
|
||||
//#else
|
||||
//#define Log(Level, Message)
|
||||
//#define Log3(Level, Message, Buffer, Len)
|
||||
//#endif
|
||||
|
||||
int HCIEmulator::getLogLevel()
|
||||
{
|
||||
@@ -50,6 +59,12 @@ void HCIEmulator::setLogLevel(int level)
|
||||
hciloglevel = level;
|
||||
}
|
||||
|
||||
//#ifdef USE_ESP_IDF
|
||||
uint16_t HCIEmulator::word(uint8_t high, uint8_t low){
|
||||
return (uint16_t) ((high << 8) | low);
|
||||
}
|
||||
//#endif
|
||||
|
||||
// modbus crc calculation borrowed from:
|
||||
// https://github.com/yaacov/ArduinoModbusSlave
|
||||
#define MODBUS_CRC_LENGTH 2
|
||||
@@ -98,16 +113,16 @@ uint16_t calculateCRC(uint8_t *buffer, int length)
|
||||
return crc;
|
||||
}
|
||||
|
||||
HCIEmulator::HCIEmulator()
|
||||
HCIEmulator::HCIEmulator(esphome::InternalGPIOPin* pin, esphome::uart::UARTComponent* uart)
|
||||
{
|
||||
m_state.valid = false;
|
||||
m_statemachine = WAITING;
|
||||
m_rxlen = m_txlen = 0;
|
||||
m_recvTime = m_lastStateTime = 0;
|
||||
m_skipFrame = false;
|
||||
m_port = &Serial2;
|
||||
m_port = uart;
|
||||
m_pin = pin;
|
||||
m_statusCallback = NULL;
|
||||
setLogLevel(DEFAULTLOGLEVEL);
|
||||
};
|
||||
|
||||
#define TX_ON 25
|
||||
@@ -122,7 +137,13 @@ void HCIEmulator::poll()
|
||||
if (m_port->available() > 0)
|
||||
{
|
||||
// Serial.println("got data");
|
||||
m_rxlen += m_port->readBytes((char *)(m_rxbuffer + m_rxlen), _min((int)(255 - m_rxlen), m_port->available()));
|
||||
int bytesToRead = std::min((int)(255 - m_rxlen), m_port->available());
|
||||
if(m_port->read_array((uint8_t *)(m_rxbuffer + m_rxlen), bytesToRead)) {
|
||||
m_rxlen += bytesToRead;
|
||||
} else {
|
||||
Log(LL_ERROR, "Error reading from UART");
|
||||
//m_port->flush();
|
||||
}
|
||||
if (m_rxlen > 254)
|
||||
{
|
||||
Log(LL_ERROR, "RX Bufferoverflow, skip next Frame");
|
||||
@@ -130,16 +151,16 @@ void HCIEmulator::poll()
|
||||
m_rxlen = 0;
|
||||
m_skipFrame = true;
|
||||
}
|
||||
m_recvTime = micros();
|
||||
m_recvTime = esphome::micros();
|
||||
}
|
||||
|
||||
// Serial.printf("Data % x\n", m_txbuffer);
|
||||
// check frame, process frame
|
||||
if (m_rxlen > 0 && (micros() - m_recvTime > T3_5))
|
||||
if (m_rxlen > 0 && (esphome::micros() - m_recvTime > T3_5))
|
||||
{
|
||||
// Serial.printf("Act on it % x\n", m_txbuffer);
|
||||
// check last action timeout -> reset > then 2sec
|
||||
if (m_statemachine != WAITING && m_lastStateTime + 2000 < millis())
|
||||
if (m_statemachine != WAITING && m_lastStateTime + 2000 < esphome::millis())
|
||||
{
|
||||
m_statemachine = WAITING;
|
||||
}
|
||||
@@ -158,22 +179,23 @@ void HCIEmulator::poll()
|
||||
m_txbuffer[(m_txlen - MODBUS_CRC_LENGTH) + 1] = crc >> 8;
|
||||
|
||||
// send data
|
||||
m_lastSendTime = micros() - m_recvTime;
|
||||
m_lastSendTime = esphome::micros() - m_recvTime;
|
||||
|
||||
// Log(LL_DEBUG, ("ST:"+String(m_lastSendTime)).c_str());
|
||||
|
||||
digitalWrite(TX_ON, HIGH);
|
||||
m_pin->digital_write(true);
|
||||
|
||||
// Log3(LL_DEBUG, "write data: ");
|
||||
m_port->write(m_txbuffer, m_txlen);
|
||||
m_port->write_array(m_txbuffer, m_txlen);
|
||||
Log3(LL_DEBUG, "Response: ", m_txbuffer, m_txlen);
|
||||
delayMicroseconds(m_txlen * 9 * 22); // 8 bits + par * Bittime 18 micros on 57600 bauds
|
||||
digitalWrite(TX_ON, LOW);
|
||||
esphome::delayMicroseconds(m_txlen * 9 * 22); // 8 bits + par * Bittime 18 micros on 57600 bauds
|
||||
m_pin->digital_write(false);
|
||||
m_txlen = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("skipped frame");
|
||||
ESP_LOGD(TAG, "skipped frame");
|
||||
}
|
||||
|
||||
m_skipFrame = false;
|
||||
@@ -184,6 +206,7 @@ void HCIEmulator::poll()
|
||||
void HCIEmulator::processFrame()
|
||||
{
|
||||
m_txlen = 0; // clear send buffer
|
||||
Log3(LL_DEBUG, "Incomming Data: ", m_rxbuffer, m_rxlen);
|
||||
|
||||
if (m_rxlen < 5)
|
||||
{
|
||||
@@ -274,10 +297,10 @@ void HCIEmulator::processDeviceStatusFrame()
|
||||
m_txbuffer[7] = 0x02;
|
||||
m_txbuffer[8] = 0x10;
|
||||
m_statemachine = STARTOPENDOOR_RELEASE;
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
break;
|
||||
case STARTOPENDOOR_RELEASE:
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < esphome::millis())
|
||||
{
|
||||
m_txbuffer[7] = 0x01;
|
||||
m_txbuffer[8] = 0x10;
|
||||
@@ -290,10 +313,10 @@ void HCIEmulator::processDeviceStatusFrame()
|
||||
m_txbuffer[7] = 0x02;
|
||||
m_txbuffer[8] = 0x20;
|
||||
m_statemachine = STARTCLOSEDOOR_RELEASE;
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
break;
|
||||
case STARTCLOSEDOOR_RELEASE:
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < esphome::millis())
|
||||
{
|
||||
m_txbuffer[7] = 0x01;
|
||||
m_txbuffer[8] = 0x20;
|
||||
@@ -306,10 +329,10 @@ void HCIEmulator::processDeviceStatusFrame()
|
||||
m_txbuffer[7] = 0x02;
|
||||
m_txbuffer[8] = 0x40;
|
||||
m_statemachine = STARTSTOPDOOR_RELEASE;
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
break;
|
||||
case STARTSTOPDOOR_RELEASE:
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < esphome::millis())
|
||||
{
|
||||
m_txbuffer[7] = 0x01;
|
||||
m_txbuffer[8] = 0x40;
|
||||
@@ -322,10 +345,10 @@ void HCIEmulator::processDeviceStatusFrame()
|
||||
m_txbuffer[7] = 0x02;
|
||||
m_txbuffer[9] = 0x40;
|
||||
m_statemachine = STARTVENTPOSITION_RELEASE;
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
break;
|
||||
case STARTVENTPOSITION_RELEASE:
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < esphome::millis())
|
||||
{
|
||||
m_txbuffer[7] = 0x01;
|
||||
m_txbuffer[9] = 0x40;
|
||||
@@ -338,11 +361,11 @@ void HCIEmulator::processDeviceStatusFrame()
|
||||
m_txbuffer[7] = 0x02;
|
||||
m_txbuffer[9] = 0x04;
|
||||
m_statemachine = STARTOPENDOORHALF_RELEASE;
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
break;
|
||||
|
||||
case STARTOPENDOORHALF_RELEASE:
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < esphome::millis())
|
||||
{
|
||||
m_txbuffer[7] = 0x01;
|
||||
m_txbuffer[9] = 0x04;
|
||||
@@ -355,10 +378,10 @@ void HCIEmulator::processDeviceStatusFrame()
|
||||
m_txbuffer[7] = 0x10;
|
||||
m_txbuffer[9] = 0x02;
|
||||
m_statemachine = STARTTOGGLELAMP_RELEASE;
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
break;
|
||||
case STARTTOGGLELAMP_RELEASE:
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
|
||||
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < esphome::millis())
|
||||
{
|
||||
m_txbuffer[7] = 0x08;
|
||||
m_txbuffer[9] = 0x02;
|
||||
@@ -432,7 +455,7 @@ void HCIEmulator::openDoor()
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
m_statemachine = STARTOPENDOOR;
|
||||
}
|
||||
|
||||
@@ -442,7 +465,7 @@ void HCIEmulator::openDoorHalf()
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
m_statemachine = STARTOPENDOORHALF;
|
||||
}
|
||||
|
||||
@@ -452,7 +475,7 @@ void HCIEmulator::closeDoor()
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
m_statemachine = STARTCLOSEDOOR;
|
||||
}
|
||||
|
||||
@@ -462,7 +485,7 @@ void HCIEmulator::stopDoor()
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
m_statemachine = STARTSTOPDOOR;
|
||||
}
|
||||
|
||||
@@ -472,7 +495,7 @@ void HCIEmulator::toggleLamp()
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
m_statemachine = STARTTOGGLELAMP;
|
||||
}
|
||||
|
||||
@@ -482,7 +505,7 @@ void HCIEmulator::ventilationPosition()
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_lastStateTime = millis();
|
||||
m_lastStateTime = esphome::millis();
|
||||
m_statemachine = STARTVENTPOSITION;
|
||||
}
|
||||
|
||||
@@ -490,3 +513,6 @@ void HCIEmulator::onStatusChanged(callback_function_t handler)
|
||||
{
|
||||
m_statusCallback = handler;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#ifndef __hciemulator_h
|
||||
#define __hciemulator_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Stream.h>
|
||||
#include <SoftwareSerial.h>
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
#define LL_OFF 0
|
||||
#define LL_ERROR 1
|
||||
@@ -11,8 +9,6 @@
|
||||
#define LL_INFO 3
|
||||
#define LL_DEBUG 4
|
||||
|
||||
#define DEFAULTLOGLEVEL LL_INFO
|
||||
|
||||
#define DEVICEID 0x02
|
||||
#define BROADCASTID 0x00
|
||||
#define SIMULATEKEYPRESSDELAYMS 100
|
||||
@@ -26,7 +22,12 @@
|
||||
// intercharacter must be 1.5T or 1.5 times longer than a normal character and thus
|
||||
// 1.5T = 1.04167ms * 1.5 = 1.5625ms. A frame delay is 3.5T.
|
||||
#define T1_5 750
|
||||
#define T3_5 4800 // 1750
|
||||
#define T3_5 4800 //
|
||||
|
||||
namespace esphome {
|
||||
namespace hoermann_door {
|
||||
|
||||
static const char *const TAG = "HCIEmulator";
|
||||
|
||||
enum DoorState : uint8_t
|
||||
{
|
||||
@@ -75,7 +76,7 @@ class HCIEmulator
|
||||
public:
|
||||
typedef std::function<void(const SHCIState &)> callback_function_t;
|
||||
|
||||
HCIEmulator();
|
||||
HCIEmulator(InternalGPIOPin* pin, uart::UARTComponent* uart);
|
||||
|
||||
void poll();
|
||||
|
||||
@@ -111,10 +112,16 @@ protected:
|
||||
void processDeviceStatusFrame();
|
||||
void processDeviceBusScanFrame();
|
||||
void processBroadcastStatusFrame();
|
||||
//#ifdef USE_ESP_IDF
|
||||
uint16_t word(uint8_t high, uint8_t low);
|
||||
//#endif
|
||||
|
||||
private:
|
||||
callback_function_t m_statusCallback;
|
||||
Stream *m_port;
|
||||
|
||||
InternalGPIOPin* m_pin;
|
||||
uart::UARTComponent* m_port;
|
||||
|
||||
SHCIState m_state;
|
||||
StateMachine m_statemachine;
|
||||
|
||||
@@ -125,14 +132,17 @@ private:
|
||||
size_t m_rxlen;
|
||||
size_t m_txlen;
|
||||
|
||||
unsigned char m_rxbuffer[255] = {
|
||||
uint8_t m_rxbuffer[255] = {
|
||||
0,
|
||||
};
|
||||
unsigned char m_txbuffer[255] = {
|
||||
uint8_t m_txbuffer[255] = {
|
||||
0,
|
||||
};
|
||||
|
||||
bool m_skipFrame;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
138
external_components/hoermann_door/hoermann.h
Normal file
138
external_components/hoermann_door/hoermann.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace esphome {
|
||||
namespace hoermann_door {
|
||||
class HoermannMainComponent;
|
||||
}
|
||||
}
|
||||
#include "hciemulator.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace hoermann_door {
|
||||
static const char *const COMP_TAG = "Hoermann";
|
||||
|
||||
void dispatcherFn(void *arg);
|
||||
|
||||
class HoermannMainComponent: public Component{
|
||||
protected:
|
||||
HCIEmulator* emulator;
|
||||
TaskHandle_t modBusTask;
|
||||
int log_level = 3; // Defaults to INFO
|
||||
|
||||
uart::UARTComponent* _uart;
|
||||
InternalGPIOPin* _tx_on;
|
||||
|
||||
volatile bool set_continue_ = true;
|
||||
volatile unsigned long lastCall = 0;
|
||||
volatile unsigned long maxPeriod = 0;
|
||||
|
||||
public:
|
||||
|
||||
void set_seriel_connection(uart::UARTComponent* uart){
|
||||
this->_uart = uart;
|
||||
}
|
||||
void set_tx_on_pin(InternalGPIOPin* pin){
|
||||
this->_tx_on = pin;
|
||||
}
|
||||
|
||||
void set_log_level(int level){
|
||||
this->log_level = level;
|
||||
}
|
||||
|
||||
HCIEmulator* getEmulator(){
|
||||
return emulator;
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
|
||||
this->_tx_on->setup();
|
||||
this->emulator = new HCIEmulator(this->_tx_on, this->_uart);
|
||||
this->emulator->setLogLevel(this->log_level);
|
||||
|
||||
this->start_polling();
|
||||
}
|
||||
|
||||
void stop_polling(){
|
||||
this->set_continue_ = false;
|
||||
if(modBusTask != NULL){
|
||||
// Wait for the task to be deleted
|
||||
while(eTaskGetState(modBusTask) != eDeleted){
|
||||
vTaskDelay(1);
|
||||
}
|
||||
modBusTask = NULL;
|
||||
}
|
||||
this->_tx_on->digital_write(false);
|
||||
}
|
||||
|
||||
void start_polling() {
|
||||
if(modBusTask == NULL){
|
||||
this->_tx_on->digital_write(false);
|
||||
this->set_continue_ = true;
|
||||
xTaskCreatePinnedToCore(
|
||||
dispatcherFn, // Function to implement the task
|
||||
"ModBusTask", // Name of the task
|
||||
10000, // Stack size in words
|
||||
this, // Task input parameter
|
||||
// 1, // Priority of the task
|
||||
configMAX_PRIORITIES - 1,
|
||||
&modBusTask, // Task handle.
|
||||
1 // Core where the task should run
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void modBusPolling(void *parameter)
|
||||
{
|
||||
while (set_continue_)
|
||||
{
|
||||
if (lastCall > 0)
|
||||
{
|
||||
maxPeriod = std::max((micros() - lastCall), (unsigned long)maxPeriod);
|
||||
}
|
||||
lastCall = micros();
|
||||
emulator->poll();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void dump_config() override {
|
||||
ESP_LOGCONFIG(COMP_TAG, "hoermann_door_component:");
|
||||
ESP_LOGCONFIG(COMP_TAG, " UART: ");
|
||||
ESP_LOGCONFIG(COMP_TAG, " UART is configured");
|
||||
LOG_PIN(" TX_ON_PIN", this->_tx_on);
|
||||
ESP_LOGCONFIG(COMP_TAG, " Log Level: %d", this->log_level);
|
||||
}
|
||||
};
|
||||
|
||||
void dispatcherFn(void *arg)
|
||||
{
|
||||
HoermannMainComponent *x = (HoermannMainComponent *)arg;
|
||||
x->modBusPolling(arg);
|
||||
}
|
||||
|
||||
template<typename... Ts> class StopPollingAction: public Action<Ts...> {
|
||||
public:
|
||||
StopPollingAction(HoermannMainComponent *motor) : motor_(motor) {}
|
||||
|
||||
void play(Ts... x) override { this->motor_->stop_polling(); }
|
||||
|
||||
protected:
|
||||
HoermannMainComponent *motor_;
|
||||
};
|
||||
template<typename... Ts> class StartPollingAction: public Action<Ts...> {
|
||||
public:
|
||||
StartPollingAction(HoermannMainComponent *motor) : motor_(motor) {}
|
||||
|
||||
void play(Ts... x) override { this->motor_->start_polling(); }
|
||||
|
||||
protected:
|
||||
HoermannMainComponent *motor_;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,31 +2,31 @@
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
|
||||
class NbsLightManager;
|
||||
|
||||
#include "esphome.h"
|
||||
#include "Arduino.h"
|
||||
#include "hciemulator.h"
|
||||
#include "door_singleton.h"
|
||||
#include "hoermann.h"
|
||||
//#include "cover.h"
|
||||
|
||||
#define RS485 Serial2
|
||||
#define TX_ON 25
|
||||
//#define configMAX_PRIORITIES 25
|
||||
|
||||
#define TAG "hoermann_door"
|
||||
|
||||
namespace esphome {
|
||||
namespace hoermann_door {
|
||||
|
||||
// Custom binary output, for exposing binary states
|
||||
class NbsLightManager: public binary::BinaryLightOutput, public light::LightState {
|
||||
protected:
|
||||
HoermannSingleton *single;
|
||||
public:
|
||||
void setup() override {
|
||||
//single = HoermannSingleton::getInstance();
|
||||
//single->initializeGetInstance
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool lastState = false;
|
||||
bool firstState = false;
|
||||
void loop() override {
|
||||
@@ -45,38 +45,41 @@ class NbsLightManager: public binary::BinaryLightOutput, public light::LightStat
|
||||
class NbsLightOutput: public output::BinaryOutput, public Component{
|
||||
|
||||
light::LightState *callback;
|
||||
HoermannSingleton *single;
|
||||
bool lastState = false;
|
||||
bool firstState = true;
|
||||
HoermannMainComponent *mainComponent;
|
||||
|
||||
public:
|
||||
|
||||
virtual void write_state(bool state){
|
||||
if(lastState != state){
|
||||
single->getEmulator()->toggleLamp();
|
||||
mainComponent->getEmulator()->toggleLamp();
|
||||
//lastState = state;
|
||||
}
|
||||
}
|
||||
|
||||
void set_state_callback(light::LightState *callback){
|
||||
if(callback == nullptr) ESP_LOGW("Hoermann_door(Light)", "Got Nullable callback");
|
||||
if(callback == nullptr) ESP_LOGW(COMP_TAG, "Got Nullable callback");
|
||||
this->callback = callback;
|
||||
}
|
||||
void set_emulator_component(HoermannMainComponent* component){
|
||||
this->mainComponent = component;
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
if(!single->getEmulator()->getState().valid) false;
|
||||
if(firstState || single->getEmulator()->getState().lampOn != lastState){
|
||||
if(!mainComponent->getEmulator()->getState().valid) false;
|
||||
if(firstState || mainComponent->getEmulator()->getState().lampOn != lastState){
|
||||
//ESP_LOGD("Test", "I have no idea");
|
||||
lastState = single->getEmulator()->getState().lampOn;
|
||||
lastState = mainComponent->getEmulator()->getState().lampOn;
|
||||
if(lastState == true){
|
||||
ESP_LOGD("Hoermann_door(Light)", "Light State ON");
|
||||
ESP_LOGD(COMP_TAG, "Light State ON");
|
||||
|
||||
auto call = callback->make_call();
|
||||
call.set_state(true);
|
||||
call.perform();
|
||||
}
|
||||
else {
|
||||
ESP_LOGD("Hoermann_door(Light)", "Light State OFF");
|
||||
ESP_LOGD(COMP_TAG, "Light State OFF");
|
||||
auto call = callback->make_call();
|
||||
call.set_state(false);
|
||||
call.perform();
|
||||
@@ -87,8 +90,7 @@ class NbsLightOutput: public output::BinaryOutput, public Component{
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
single = HoermannSingleton::getInstance();
|
||||
single->initializeEmulator();
|
||||
|
||||
}
|
||||
|
||||
void turn_on() override {
|
||||
|
||||
@@ -2,23 +2,29 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import output, light
|
||||
from esphome.const import CONF_ID
|
||||
from . import HoermannDoor
|
||||
|
||||
hoermann_cover_ns = cg.esphome_ns.namespace('hoermann_door')
|
||||
HoermannCoverLight = hoermann_cover_ns.class_('NbsLightOutput', output.BinaryOutput, cg.Component)
|
||||
CONF_STATE_CALLBACK = "state_callback"
|
||||
CONF_HOERMANN_CONTROLLER = "hoermann_controller"
|
||||
|
||||
CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(HoermannCoverLight),
|
||||
cv.Required(CONF_STATE_CALLBACK): cv.use_id(light.LightState)
|
||||
cv.Required(CONF_STATE_CALLBACK): cv.use_id(light.LightState),
|
||||
cv.Required(CONF_HOERMANN_CONTROLLER): cv.use_id(HoermannDoor)
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield output.register_output(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await output.register_output(var, config)
|
||||
|
||||
callback = yield cg.get_variable(config[CONF_STATE_CALLBACK])
|
||||
callback = await cg.get_variable(config[CONF_STATE_CALLBACK])
|
||||
cg.add(var.set_state_callback(callback))
|
||||
|
||||
controller = await cg.get_variable(config[CONF_HOERMANN_CONTROLLER])
|
||||
cg.add(var.set_emulator_component(controller))
|
||||
|
||||
cg.add_library("plerup/EspSoftwareSerial", "8.2.0")
|
||||
# cg.add_library("plerup/EspSoftwareSerial", "8.2.0")
|
||||
42
garage1.yaml
42
garage1.yaml
@@ -1,14 +1,26 @@
|
||||
substitutions:
|
||||
garageSide: sy # sy, wo
|
||||
version: "2.1.3"
|
||||
|
||||
esphome:
|
||||
name: garage${garageSide}
|
||||
platform: ESP32
|
||||
board: esp32dev
|
||||
|
||||
esp32:
|
||||
board: wemos_d1_mini32
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
ota:
|
||||
- platform: esphome
|
||||
password: !secret gd_passwd
|
||||
on_begin:
|
||||
then:
|
||||
- hoermann_door.stop_polling: door_controll_internal
|
||||
- logger.log: "OTA-Update started, stopped polling"
|
||||
on_error:
|
||||
then:
|
||||
- hoermann_door.start_polling: door_controll_internal
|
||||
- logger.log: "OTA-Update failed, restarted polling"
|
||||
|
||||
api:
|
||||
encryption:
|
||||
@@ -17,7 +29,6 @@ api:
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
power_save_mode: none
|
||||
fast_connect: true
|
||||
logger:
|
||||
#level: VERY_VERBOSE
|
||||
@@ -29,8 +40,27 @@ external_components:
|
||||
components: [ hoermann_door ]
|
||||
|
||||
cover:
|
||||
- platform: hoermann_door
|
||||
name: door_${garageSide}
|
||||
- platform: hoermann_door
|
||||
name: door_${garageSide}
|
||||
hoermann_controller: door_controll_internal
|
||||
|
||||
uart:
|
||||
rx_pin: 16
|
||||
tx_pin: 17
|
||||
baud_rate: 57600
|
||||
id: hm_connection
|
||||
data_bits: 8 # Standard, just defined for clarity
|
||||
parity: EVEN # The chip uses even parity
|
||||
stop_bits: 1 # Standard, just defined for clarity
|
||||
|
||||
hoermann_door:
|
||||
id: door_controll_internal
|
||||
uart_connection: hm_connection
|
||||
log_level: INFO
|
||||
tx_pin:
|
||||
number: 25
|
||||
inverted: false # Just for clarity, false is standard
|
||||
|
||||
|
||||
light:
|
||||
- platform: binary
|
||||
@@ -42,5 +72,7 @@ output:
|
||||
- id: light_out
|
||||
platform: hoermann_door
|
||||
state_callback: lamp_id_${garageSide}
|
||||
hoermann_controller: door_controll_internal
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user