Initial working

Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at>
This commit is contained in:
Lukas Bachschwell 2023-04-09 19:46:06 +02:00
parent 6017cafad1
commit 3356acdf78
Signed by: lbsadmin
GPG Key ID: CCC6AA87CC8DF425
5 changed files with 430 additions and 342 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
HCPBridge/.vscode
HCPBridgeESP32/src/credentials.h
Investigation/bussniffer/tools/opendooralert/opendooralert.py.bak
tools/opendooralert/opendooralert.py.bak

View File

@ -1,24 +1,37 @@
#include "hciemulator.h"
#define CHECKCHANGEDSET(Target,Value,Flag) if((Target)!=(Value)){Target=Value;Flag=true;}
#define CHECKCHANGEDSET(Target, Value, Flag) \
if ((Target) != (Value)) \
{ \
Target = Value; \
Flag = true; \
}
int hciloglevel = DEFAULTLOGLEVEL;
#define SOFTSERIAL 1
#ifdef SOFTSERIAL
#define Log(Level, Message) LogCore(Level, Message)
#define Log3(Level, Message, Buffer, Len) LogCore(Level, Message, Buffer, Len)
// LOGLEVEL
void LogCore(int Level, const char* msg, const unsigned char * data=NULL, size_t datalen=0){
if(Level>hciloglevel){
void LogCore(int Level, const char *msg, const unsigned char *data = NULL, size_t datalen = 0)
{
if (Level > hciloglevel)
{
return;
}
if(data!=NULL && datalen>0){
if (data != NULL && datalen > 0)
{
String newmsg(msg);
char str[4];
for (size_t i = 0; i < datalen; i++){
for (size_t i = 0; i < datalen; i++)
{
snprintf(str, sizeof(str), "%02x ", data[i]);
newmsg += str;
}
Serial.println(newmsg);
}else{
}
else
{
Serial.println(msg);
}
}
@ -27,12 +40,12 @@ void LogCore(int Level, const char* msg, const unsigned char * data=NULL, size_t
#define Log3(Level, Message, Buffer, Len)
#endif
int HCIEmulator::getLogLevel(){
int HCIEmulator::getLogLevel()
{
return hciloglevel;
}
void HCIEmulator::setLogLevel(int level){
void HCIEmulator::setLogLevel(int level)
{
hciloglevel = level;
}
@ -84,7 +97,8 @@ uint16_t calculateCRC(uint8_t *buffer, int length)
return crc;
}
HCIEmulator::HCIEmulator(Stream * port) {
HCIEmulator::HCIEmulator(Stream *port)
{
m_state.valid = false;
m_statemachine = WAITING;
m_rxlen = m_txlen = 0;
@ -95,13 +109,18 @@ HCIEmulator::HCIEmulator(Stream * port) {
setLogLevel(DEFAULTLOGLEVEL);
};
void HCIEmulator::poll(){
#define TX_ON 25
if(m_port==NULL) return;
void HCIEmulator::poll()
{
if (m_port == NULL)
return;
// receive Data
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()));
if (m_rxlen > 254)
{
@ -113,22 +132,25 @@ void HCIEmulator::poll(){
m_recvTime = micros();
}
// Serial.printf("Data % x\n", m_txbuffer);
// check frame, process frame
if (m_rxlen > 0 && (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 < millis())
{
m_statemachine = WAITING;
}
if(!m_skipFrame){
if (!m_skipFrame)
{
processFrame();
// send response
if(m_txlen > 0){
if (m_txlen > 0)
{
// fix crc
uint16_t crc = calculateCRC(m_txbuffer, m_txlen - MODBUS_CRC_LENGTH);
m_txbuffer[m_txlen - MODBUS_CRC_LENGTH] = crc & 0xFF;
@ -139,28 +161,40 @@ void HCIEmulator::poll(){
// Log(LL_DEBUG, ("ST:"+String(m_lastSendTime)).c_str());
digitalWrite(TX_ON, HIGH);
delayMicroseconds(10);
Serial.println("write data");
m_port->write(m_txbuffer, m_txlen);
Log3(LL_DEBUG, "Response: ", m_txbuffer, m_txlen);
// delayMicroseconds(50);
digitalWrite(TX_ON, LOW);
m_txlen = 0;
}
}
else
{
Serial.println("skipped frame");
}
m_skipFrame = false;
m_rxlen = 0;
}
}
void HCIEmulator::processFrame(){
void HCIEmulator::processFrame()
{
m_txlen = 0; // clear send buffer
if(m_rxlen<5) {
if (m_rxlen < 5)
{
Log(LL_ERROR, "Frame skipped, invalid frame len");
Log3(LL_ERROR, "Data:", m_rxbuffer, m_rxlen);
return;
}
// check device id, pass only device id 2 and 0 (broadcast)
if(m_rxbuffer[0] != BROADCASTID && m_rxbuffer[0] != DEVICEID){
if (m_rxbuffer[0] != BROADCASTID && m_rxbuffer[0] != DEVICEID)
{
Log(LL_DEBUG, "Frame skipped, unsupported device id");
Log3(LL_DEBUG, "Data:", m_rxbuffer, m_rxlen);
return;
@ -168,7 +202,8 @@ void HCIEmulator::processFrame(){
// check crc
uint16_t crc = readCRC(m_rxbuffer, m_rxlen);
if(crc != calculateCRC(m_rxbuffer,m_rxlen-MODBUS_CRC_LENGTH)){
if (crc != calculateCRC(m_rxbuffer, m_rxlen - MODBUS_CRC_LENGTH))
{
Log3(LL_ERROR, "Frame skipped, wrong crc", m_rxbuffer, m_rxlen);
return;
}
@ -176,8 +211,10 @@ void HCIEmulator::processFrame(){
Log3(LL_DEBUG, "Incomming Data: ", m_rxbuffer, m_rxlen);
// dispatch modbus function
switch(m_rxbuffer[1]){
case 0x10:{ // Write Multiple registers
switch (m_rxbuffer[1])
{
case 0x10:
{ // Write Multiple registers
if (m_rxlen == 0x1b && m_rxbuffer[0] == BROADCASTID)
{
processBroadcastStatusFrame();
@ -186,17 +223,22 @@ void HCIEmulator::processFrame(){
break;
}
case 0x17:{ // Read/Write Multiple registers
if(m_rxbuffer[0] == DEVICEID){
switch(m_rxlen){
case 0x11:{
case 0x17:
{ // Read/Write Multiple registers
if (m_rxbuffer[0] == DEVICEID)
{
switch (m_rxlen)
{
case 0x11:
{
processDeviceStatusFrame();
return;
}
case 0x13:
processDeviceBusScanFrame();
return;;
return;
;
}
}
break;
@ -207,11 +249,14 @@ void HCIEmulator::processFrame(){
const unsigned char ResponseTemplate_Fcn17_Cmd03_L08[] = {0x02, 0x17, 0x10, 0x3E, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x1B};
const unsigned char ResponseTemplate_Fcn17_Cmd04_L02[] = {0x02, 0x17, 0x04, 0x0F, 0x00, 0x04, 0xFD, 0x0A, 0x72};
void HCIEmulator::processDeviceStatusFrame(){
if(m_rxlen==0x11){
void HCIEmulator::processDeviceStatusFrame()
{
if (m_rxlen == 0x11)
{
unsigned char counter = m_rxbuffer[11];
unsigned char cmd = m_rxbuffer[12];
if(m_rxbuffer[5] == 0x08){
if (m_rxbuffer[5] == 0x08)
{
// expose internal state
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
// 0011: 02 17 9C B9 00 08 9C 41 00 02 04 3E 03 00 00 EB CC
@ -222,7 +267,6 @@ void HCIEmulator::processDeviceStatusFrame(){
m_txbuffer[5] = cmd;
m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd03_L08);
switch (m_statemachine)
{
// open Door
@ -233,7 +277,8 @@ void HCIEmulator::processDeviceStatusFrame(){
m_lastStateTime = millis();
break;
case STARTOPENDOOR_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[8] = 0x10;
m_statemachine = WAITING;
@ -248,7 +293,8 @@ void HCIEmulator::processDeviceStatusFrame(){
m_lastStateTime = millis();
break;
case STARTCLOSEDOOR_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[8] = 0x20;
m_statemachine = WAITING;
@ -263,7 +309,8 @@ void HCIEmulator::processDeviceStatusFrame(){
m_lastStateTime = millis();
break;
case STARTSTOPDOOR_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[8] = 0x40;
m_statemachine = WAITING;
@ -278,14 +325,14 @@ void HCIEmulator::processDeviceStatusFrame(){
m_lastStateTime = millis();
break;
case STARTVENTPOSITION_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[9] = 0x40;
m_statemachine = WAITING;
}
break;
// Half Position
case STARTOPENDOORHALF:
m_txbuffer[7] = 0x02;
@ -295,7 +342,8 @@ void HCIEmulator::processDeviceStatusFrame(){
break;
case STARTOPENDOORHALF_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x01;
m_txbuffer[9] = 0x04;
m_statemachine = WAITING;
@ -310,7 +358,8 @@ void HCIEmulator::processDeviceStatusFrame(){
m_lastStateTime = millis();
break;
case STARTTOGGLELAMP_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x08;
m_txbuffer[9] = 0x02;
m_statemachine = WAITING;
@ -322,7 +371,8 @@ void HCIEmulator::processDeviceStatusFrame(){
}
return;
}
else if(m_rxbuffer[5] == 0x02){
else if (m_rxbuffer[5] == 0x02)
{
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
// 0011: 02 17 9C B9 00 02 9C 41 00 02 04 0F 04 17 00 7B 21
// res=> 02 17 04 0F 00 04 FD 0A 72
@ -339,7 +389,8 @@ void HCIEmulator::processDeviceStatusFrame(){
}
const unsigned char ResponseTemplate_Fcn17_Cmd02_L05[] = {0x02, 0x17, 0x0a, 0x00, 0x00, 0x02, 0x05, 0x04, 0x30, 0x10, 0xff, 0xa8, 0x45, 0x0e, 0xdf};
void HCIEmulator::processDeviceBusScanFrame(){
void HCIEmulator::processDeviceBusScanFrame()
{
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
// 0013: 02 17 9C B9 00 05 9C 41 00 03 06 00 02 00 00 01 02 f8 35
// res=> 02 17 0a 00 00 02 05 04 30 10 ff a8 45 0e df
@ -354,7 +405,8 @@ void HCIEmulator::processDeviceBusScanFrame(){
Log(LL_INFO, "Busscan received");
}
void HCIEmulator::processBroadcastStatusFrame(){
void HCIEmulator::processBroadcastStatusFrame()
{
// 001B: 00 10 9D 31 00 09 12 64 00 00 00 40 60 00 00 00 00 00 00 00 00 00 01 00 00 CA 22
bool hasChanged = false;
CHECKCHANGEDSET(m_state.lampOn, m_rxbuffer[20] == 0x14, hasChanged);
@ -364,62 +416,77 @@ void HCIEmulator::processBroadcastStatusFrame(){
CHECKCHANGEDSET(m_state.reserved, m_rxbuffer[17], hasChanged);
CHECKCHANGEDSET(m_state.valid, true, hasChanged);
if(hasChanged){
if (hasChanged)
{
Log3(LL_INFO, "New State: ", m_rxbuffer, m_rxlen);
if(m_statusCallback != NULL){
if (m_statusCallback != NULL)
{
m_statusCallback(m_state);
}
}
}
void HCIEmulator::openDoor(){
if(m_statemachine != WAITING){
void HCIEmulator::openDoor()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTOPENDOOR;
}
void HCIEmulator::openDoorHalf(){
if(m_statemachine != WAITING){
void HCIEmulator::openDoorHalf()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTOPENDOORHALF;
}
void HCIEmulator::closeDoor(){
if(m_statemachine != WAITING){
void HCIEmulator::closeDoor()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTCLOSEDOOR;
}
void HCIEmulator::stopDoor(){
if(m_statemachine != WAITING){
void HCIEmulator::stopDoor()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTSTOPDOOR;
}
void HCIEmulator::toggleLamp(){
if(m_statemachine != WAITING){
void HCIEmulator::toggleLamp()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTTOGGLELAMP;
}
void HCIEmulator::ventilationPosition(){
if(m_statemachine != WAITING){
void HCIEmulator::ventilationPosition()
{
if (m_statemachine != WAITING)
{
return;
}
m_lastStateTime = millis();
m_statemachine = STARTVENTPOSITION;
}
void HCIEmulator::onStatusChanged(callback_function_t handler) {
void HCIEmulator::onStatusChanged(callback_function_t handler)
{
m_statusCallback = handler;
}

View File

@ -11,13 +11,12 @@
#define LL_INFO 3
#define LL_DEBUG 4
#define DEFAULTLOGLEVEL LL_WARN
#define DEFAULTLOGLEVEL LL_DEBUG
#define DEVICEID 0x02
#define BROADCASTID 0x00
#define SIMULATEKEYPRESSDELAYMS 100
// Modbus states that a baud rate higher than 19200 must use a fixed 750 us
// for inter character time out and 1.75 ms for a frame delay.
// For baud rates below 19200 the timeing is more critical and has to be calculated.
@ -29,17 +28,17 @@
#define T1_5 750
#define T3_5 4800 // 1750
enum DoorState : uint8_t {
enum DoorState : uint8_t
{
DOOR_OPEN_POSITION = 0x20,
DOOR_CLOSE_POSITION = 0x40,
DOOR_HALF_POSITION = 0x80,
DOOR_MOVE_CLOSEPOSITION = 0x02,
DOOR_MOVE_OPENPOSITION = 0x01,
};
struct SHCIState{
struct SHCIState
{
bool valid;
bool lampOn;
uint8_t doorState; // see DoorState
@ -48,7 +47,8 @@ struct SHCIState{
uint8_t reserved;
};
enum StateMachine: uint8_t{
enum StateMachine : uint8_t
{
WAITING,
STARTOPENDOOR,
@ -70,7 +70,8 @@ enum StateMachine: uint8_t{
STARTVENTPOSITION_RELEASE
};
class HCIEmulator {
class HCIEmulator
{
public:
typedef std::function<void(const SHCIState &)> callback_function_t;
@ -85,15 +86,18 @@ public:
void toggleLamp();
void ventilationPosition();
const SHCIState& getState() {
if(micros()-m_recvTime > 2000000){
const SHCIState &getState()
{
if (micros() - m_recvTime > 2000000)
{
// 2 sec without statusmessage
m_state.valid = false;
}
return m_state;
};
unsigned long getMessageAge(){
unsigned long getMessageAge()
{
return micros() - m_recvTime;
}
@ -121,11 +125,14 @@ private:
size_t m_rxlen;
size_t m_txlen;
unsigned char m_rxbuffer[255];
unsigned char m_txbuffer[255];
unsigned char m_rxbuffer[255] = {
0,
};
unsigned char m_txbuffer[255] = {
0,
};
bool m_skipFrame;
};
#endif

File diff suppressed because one or more lines are too long

View File

@ -4,24 +4,21 @@
#include "AsyncJson.h"
#include "ArduinoJson.h"
#include "hciemulator.h"
#include "index_html.h"
// #include "index_html.h"
#include "../../WebUI/index_html.h"
/* create this file and add your wlan credentials
const char* ssid = "MyWLANSID";
const char* password = "MYPASSWORD";
*/
#include "../../../private/credentials.h"
#include "credentials.h"
// switch relay sync to the lamp
// e.g. the Wifi Relay Board U4648
#define USERELAY
// #define USERELAY
// use alternative uart pins
//#define SWAPUART
#define RS485 Serial
#define RS485 Serial2
#define TX_ON 25
// Relay Board parameters
#define ESP8266_GPIO2 2 // Blue LED.
@ -29,7 +26,6 @@
#define ESP8266_GPIO5 5 // Optocoupler input.
#define LED_PIN ESP8266_GPIO2
// Hörmann HCP2 based on modbus rtu @57.6kB 8E1
HCIEmulator emulator(&RS485);
@ -37,18 +33,22 @@ HCIEmulator emulator(&RS485);
AsyncWebServer server(80);
// called by ESPAsyncTCP-esphome:SyncClient.cpp (see patch) instead of delay to avoid connection breaks
void DelayHandler(void){
void DelayHandler(void)
{
emulator.poll();
}
// switch GPIO4 und GPIO2 sync to the lamp
void onStatusChanged(const SHCIState& state){
void onStatusChanged(const SHCIState &state)
{
// see https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/
// Setting GPIO4 high, causes the relay to close the NO contact with
if(state.valid){
if (state.valid)
{
digitalWrite(ESP8266_GPIO4, state.lampOn);
digitalWrite(LED_PIN, state.lampOn);
}else
}
else
{
digitalWrite(ESP8266_GPIO4, false);
digitalWrite(LED_PIN, false);
@ -56,9 +56,11 @@ void onStatusChanged(const SHCIState& state){
}
// toggle lamp to expected state
void switchLamp(bool on){
void switchLamp(bool on)
{
bool toggle = (on && !emulator.getState().lampOn) || (!on && emulator.getState().lampOn);
if(toggle){
if (toggle)
{
emulator.toggleLamp();
}
}
@ -66,9 +68,12 @@ void switchLamp(bool on){
volatile unsigned long lastCall = 0;
volatile unsigned long maxPeriod = 0;
void modBusPolling( void * parameter) {
while(true){
if(lastCall>0){
void modBusPolling(void *parameter)
{
while (true)
{
if (lastCall > 0)
{
maxPeriod = _max(micros() - lastCall, maxPeriod);
}
lastCall = micros();
@ -78,18 +83,25 @@ void modBusPolling( void * parameter) {
vTaskDelete(NULL);
}
TaskHandle_t modBusTask;
// setup mcu
void setup(){
void setup()
{
Serial.begin(115200);
Serial.println("Starting door control");
// setup modbus
RS485.begin(57600,SERIAL_8E1);
#ifdef SWAPUART
RS485.swap();
#endif
RS485.begin(57600, SERIAL_8E1, 16, 17);
pinMode(TX_ON, OUTPUT);
digitalWrite(TX_ON, LOW);
// digitalWrite(TX_ON, HIGH);
// for (;;)
//{
// RS485.write("hello there");
// delay(100);
// }
xTaskCreatePinnedToCore(
modBusPolling, /* Function to implement the task */
@ -101,23 +113,24 @@ void setup(){
&modBusTask, /* Task handle. */
1); /* Core where the task should run */
// setup wifi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
WiFi.setAutoReconnect(true);
while (WiFi.status() != WL_CONNECTED) {
while (WiFi.status() != WL_CONNECTED)
{
delay(100);
}
// setup http server
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
AsyncWebServerResponse *response = request->beginResponse_P( 200, "text/html", index_html,sizeof(index_html));
response->addHeader("Content-Encoding","deflate");
request->send(response);
});
request->send(response); });
server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request){
server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request)
{
const SHCIState& doorstate = emulator.getState();
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument root(1024);
@ -133,10 +146,10 @@ void setup(){
lastCall = maxPeriod = 0;
serializeJson(root,*response);
request->send(response);
});
request->send(response); });
server.on("/command", HTTP_GET, [] (AsyncWebServerRequest *request) {
server.on("/command", HTTP_GET, [](AsyncWebServerRequest *request)
{
if (request->hasParam("action")) {
int actionid = request->getParam("action")->value().toInt();
switch (actionid){
@ -162,10 +175,10 @@ void setup(){
break;
}
}
request->send(200, "text/plain", "OK");
});
request->send(200, "text/plain", "OK"); });
server.on("/sysinfo", HTTP_GET, [] (AsyncWebServerRequest *request) {
server.on("/sysinfo", HTTP_GET, [](AsyncWebServerRequest *request)
{
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument root(1024);
@ -177,8 +190,7 @@ void setup(){
root["resetreason"] =esp_reset_reason();
serializeJson(root,*response);
request->send(response);
});
request->send(response); });
AsyncElegantOTA.begin(&server);
@ -193,10 +205,10 @@ void setup(){
digitalWrite(LED_PIN, 0);
emulator.onStatusChanged(onStatusChanged);
#endif
}
// mainloop
void loop(){
void loop()
{
AsyncElegantOTA.loop();
}