diff --git a/external_components/hoermann_door/__init__.py b/external_components/hoermann_door/__init__.py index e69de29..cbfd3d6 100644 --- a/external_components/hoermann_door/__init__.py +++ b/external_components/hoermann_door/__init__.py @@ -0,0 +1,42 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +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) + +CONF_UART_ENTRY = "uart_connection" +CONF_TX_PIN = "tx_pin" + +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, + }), + validate_config +) + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + #yield mqtt.register_mqtt_component(var, config) + + #btnReset = yield cg.gpio_pin_expression(config[CONF_SENSOR_PIN]) + #cg.add(var.set_input_pin(btnReset)) + + 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)) # Needs be configured before sensor \ No newline at end of file diff --git a/external_components/hoermann_door/cover.py b/external_components/hoermann_door/cover.py index e8e206e..ed9a6b8 100644 --- a/external_components/hoermann_door/cover.py +++ b/external_components/hoermann_door/cover.py @@ -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") \ No newline at end of file diff --git a/external_components/hoermann_door/cover_component.h b/external_components/hoermann_door/cover_component.h index f63955d..7d3a58b 100644 --- a/external_components/hoermann_door/cover_component.h +++ b/external_components/hoermann_door/cover_component.h @@ -1,11 +1,11 @@ #pragma once -#include "door_singleton.h" - #ifdef USE_COVER +class HoermannDoorx; + #include "esphome.h" -#include "Arduino.h" +#include "hoermann.h" #include "hciemulator.h" //#include "cover.h" @@ -13,14 +13,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,11 +47,11 @@ 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{ @@ -61,39 +60,22 @@ public: } 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 +91,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(); diff --git a/external_components/hoermann_door/door_singleton.h b/external_components/hoermann_door/door_singleton.h deleted file mode 100644 index 685e08c..0000000 --- a/external_components/hoermann_door/door_singleton.h +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/external_components/hoermann_door/hciemulator.cpp b/external_components/hoermann_door/hciemulator.cpp index be233b9..fdf83c5 100644 --- a/external_components/hoermann_door/hciemulator.cpp +++ b/external_components/hoermann_door/hciemulator.cpp @@ -1,5 +1,5 @@ #include "hciemulator.h" -#include "Arduino.h" + #define CHECKCHANGEDSET(Target, Value, Flag) \ if ((Target) != (Value)) \ { \ @@ -33,7 +33,7 @@ void LogCore(int Level, const char *msg, const unsigned char *data = NULL, size_ } else { - Serial.println(msg); + ESP_LOGD(TAG, msg); } } #else @@ -98,14 +98,15 @@ 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); }; @@ -122,7 +123,7 @@ 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())); + m_rxlen += m_port->read_array((u_int8_t *)(m_rxbuffer + m_rxlen), _min((int)(255 - m_rxlen), m_port->available())); if (m_rxlen > 254) { Log(LL_ERROR, "RX Bufferoverflow, skip next Frame"); @@ -162,18 +163,19 @@ void HCIEmulator::poll() // Log(LL_DEBUG, ("ST:"+String(m_lastSendTime)).c_str()); - digitalWrite(TX_ON, HIGH); + m_pin->digital_write(HIGH); + // 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); + m_pin->digital_write(LOW); m_txlen = 0; } } else { - Serial.println("skipped frame"); + ESP_LOGD(TAG, "skipped frame"); } m_skipFrame = false; diff --git a/external_components/hoermann_door/hciemulator.h b/external_components/hoermann_door/hciemulator.h index be5296f..34ef0d3 100644 --- a/external_components/hoermann_door/hciemulator.h +++ b/external_components/hoermann_door/hciemulator.h @@ -1,9 +1,14 @@ #ifndef __hciemulator_h #define __hciemulator_h -#include -#include -#include +#ifdef USE_ESP_IDF + #include + #include + #include + #include "esp_idf.h" +#endif + +#include "esphome/components/uart/uart.h" #define LL_OFF 0 #define LL_ERROR 1 @@ -26,7 +31,9 @@ // 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 // + +static const char *const TAG = "HCIEmulator"; enum DoorState : uint8_t { @@ -75,7 +82,7 @@ class HCIEmulator public: typedef std::function callback_function_t; - HCIEmulator(); + HCIEmulator(esphome::InternalGPIOPin* pin, esphome::uart::UARTComponent* uart); void poll(); @@ -114,7 +121,10 @@ protected: private: callback_function_t m_statusCallback; - Stream *m_port; + + esphome::InternalGPIOPin* m_pin; + esphome::uart::UARTComponent* m_port; + SHCIState m_state; StateMachine m_statemachine; @@ -135,4 +145,20 @@ private: bool m_skipFrame; }; +// Now, let's just define micros and millis and delay and delayMicros just so we know +#ifdef USE_ESP_IDF + unsigned long micros(){ + return (unsigned long) esp_timer_get_time(); + } + unsigned long millis(){ + return (unsigned long) esp_timer_get_time() / 1000; + } + void delay(uint32 duration){ + + } + void delayMicroseconds(uint32 duration){ + + } +#endif + #endif diff --git a/external_components/hoermann_door/hoermann.h b/external_components/hoermann_door/hoermann.h new file mode 100644 index 0000000..2293991 --- /dev/null +++ b/external_components/hoermann_door/hoermann.h @@ -0,0 +1,92 @@ +#pragma once + +class HoermannMainComponent; +#include "hciemulator.h" +#include "esphome/components/uart/uart.h" + +namespace esphome { +namespace hoermann_door { + +static const char *const TAG = "Hoermann"; + +void dispatcherFn(void *arg); + +class HoermannMainComponent: public Component{ + protected: + HCIEmulator* emulator; + TaskHandle_t modBusTask; + + 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; + } + HCIEmulator* getEmulator(){ + return emulator; + } + + void setup() override { + + this->_tx_on->setup(); + this->emulator = new HCIEmulator(this->_tx_on, this->_uart); + + this->_tx_on->digital_write(LOW); + this->set_continue_ = false; + xTaskCreatePinnedToCore( + dispatcherFn, // 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 + ); + } + + void stop_polling(){ + this->_tx_on->digital_write(HIGH); + this->set_continue_ = false; + } + + void modBusPolling(void *parameter) + { + while (set_continue_) + { + if (lastCall > 0) + { + maxPeriod = _max(micros() - lastCall, maxPeriod); + } + lastCall = micros(); + emulator->poll(); + vTaskDelay(1); + } + vTaskDelete(NULL); + } + + void dump_config() override { + ESP_LOGCONFIG(TAG, "hoermann_door_component:"); + ESP_LOGCONFIG(TAG, " UART: %d", this->_uart->get_baud_rate()); + //LOG_PIN(TAG, this->_tx_on); + } +}; + +void dispatcherFn(void *arg) +{ + HoermannMainComponent *x = (HoermannMainComponent *)arg; + x->modBusPolling(arg); +} + + +} +} \ No newline at end of file diff --git a/external_components/hoermann_door/light_component.h b/external_components/hoermann_door/light_component.h index 4928649..1a6eba5 100644 --- a/external_components/hoermann_door/light_component.h +++ b/external_components/hoermann_door/light_component.h @@ -2,10 +2,11 @@ #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 @@ -20,13 +21,14 @@ 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,15 +47,15 @@ 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; } } @@ -62,21 +64,24 @@ class NbsLightOutput: public output::BinaryOutput, public Component{ if(callback == nullptr) ESP_LOGW("Hoermann_door(Light)", "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(TAG, "Hoermann_door(Light)", "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(TAG, "Hoermann_door(Light)", "Light State OFF"); auto call = callback->make_call(); call.set_state(false); call.perform(); @@ -87,8 +92,7 @@ class NbsLightOutput: public output::BinaryOutput, public Component{ } void setup() override { - single = HoermannSingleton::getInstance(); - single->initializeEmulator(); + } void turn_on() override { diff --git a/external_components/hoermann_door/output.py b/external_components/hoermann_door/output.py index 4cb75dc..4d5abc9 100644 --- a/external_components/hoermann_door/output.py +++ b/external_components/hoermann_door/output.py @@ -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") \ No newline at end of file +# cg.add_library("plerup/EspSoftwareSerial", "8.2.0") \ No newline at end of file diff --git a/garage1.yaml b/garage1.yaml index 3a24efd..92e67a9 100644 --- a/garage1.yaml +++ b/garage1.yaml @@ -3,8 +3,11 @@ substitutions: esphome: name: garage${garageSide} - platform: ESP32 - board: esp32dev + +esp32: + board: wemos_d1_mini32 + framework: + type: arduino ota: - platform: esphome @@ -31,6 +34,18 @@ external_components: cover: - platform: hoermann_door name: door_${garageSide} + hoermann_controller: door_controll_internal + +uart: + rx_pin: 16 + tx_pin: 17 + baud_rate: 57600 + id: hm_connection + +hoermann_door: + id: door_controll_internal + uart_connection: hm_connection + tx_pin: 25 light: - platform: binary @@ -42,5 +57,7 @@ output: - id: light_out platform: hoermann_door state_callback: lamp_id_${garageSide} + hoermann_controller: door_controll_internal + \ No newline at end of file