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 HCPBridge/.vscode
HCPBridgeESP32/src/credentials.h
Investigation/bussniffer/tools/opendooralert/opendooralert.py.bak Investigation/bussniffer/tools/opendooralert/opendooralert.py.bak
tools/opendooralert/opendooralert.py.bak tools/opendooralert/opendooralert.py.bak

View File

@ -1,43 +1,56 @@
#include "hciemulator.h" #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; int hciloglevel = DEFAULTLOGLEVEL;
#define SOFTSERIAL 1
#ifdef SOFTSERIAL #ifdef SOFTSERIAL
#define Log(Level,Message) LogCore(Level,Message) #define Log(Level, Message) LogCore(Level, Message)
#define Log3(Level,Message,Buffer, Len) LogCore(Level,Message,Buffer,Len) #define Log3(Level, Message, Buffer, Len) LogCore(Level, Message, Buffer, Len)
//LOGLEVEL // LOGLEVEL
void LogCore(int Level, const char* msg, const unsigned char * data=NULL, size_t datalen=0){ void LogCore(int Level, const char *msg, const unsigned char *data = NULL, size_t datalen = 0)
if(Level>hciloglevel){ {
if (Level > hciloglevel)
{
return; return;
} }
if(data!=NULL && datalen>0){ if (data != NULL && datalen > 0)
{
String newmsg(msg); String newmsg(msg);
char str[4]; 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; snprintf(str, sizeof(str), "%02x ", data[i]);
newmsg += str;
} }
Serial.println(newmsg); Serial.println(newmsg);
}else{ }
else
{
Serial.println(msg); Serial.println(msg);
} }
} }
#else #else
#define Log(Level,Message) #define Log(Level, Message)
#define Log3(Level,Message,Buffer, Len) #define Log3(Level, Message, Buffer, Len)
#endif #endif
int HCIEmulator::getLogLevel()
int HCIEmulator::getLogLevel(){ {
return hciloglevel; return hciloglevel;
} }
void HCIEmulator::setLogLevel(int level){ void HCIEmulator::setLogLevel(int level)
hciloglevel=level; {
hciloglevel = level;
} }
//modbus crc calculation borrowed from: // modbus crc calculation borrowed from:
//https://github.com/yaacov/ArduinoModbusSlave // https://github.com/yaacov/ArduinoModbusSlave
#define MODBUS_CRC_LENGTH 2 #define MODBUS_CRC_LENGTH 2
#define readCRC(arr, length) word(arr[(length - MODBUS_CRC_LENGTH) + 1], arr[length - MODBUS_CRC_LENGTH]) #define readCRC(arr, length) word(arr[(length - MODBUS_CRC_LENGTH) + 1], arr[length - MODBUS_CRC_LENGTH])
#define readUInt16(arr, index) word(arr[index], arr[index + 1]) #define readUInt16(arr, index) word(arr[index], arr[index + 1])
@ -48,13 +61,13 @@ void HCIEmulator::setLogLevel(int level){
* @param length The length of the byte array. * @param length The length of the byte array.
* *
* @return The calculated CRC as an unsigned 16 bit integer. * @return The calculated CRC as an unsigned 16 bit integer.
* *
* Calculate and add the CRC. * Calculate and add the CRC.
* uint16_t crc = Modbus::calculateCRC(_responseBuffer, _responseBufferLength - MODBUS_CRC_LENGTH); * uint16_t crc = Modbus::calculateCRC(_responseBuffer, _responseBufferLength - MODBUS_CRC_LENGTH);
* _responseBuffer[_responseBufferLength - MODBUS_CRC_LENGTH] = crc & 0xFF; * _responseBuffer[_responseBufferLength - MODBUS_CRC_LENGTH] = crc & 0xFF;
* _responseBuffer[(_responseBufferLength - MODBUS_CRC_LENGTH) + 1] = crc >> 8; * _responseBuffer[(_responseBufferLength - MODBUS_CRC_LENGTH) + 1] = crc >> 8;
* *
* *
* #define MODBUS_FRAME_SIZE 4 * #define MODBUS_FRAME_SIZE 4
* #define MODBUS_CRC_LENGTH 2 * #define MODBUS_CRC_LENGTH 2
* uint16_t crc = readCRC(_requestBuffer, _requestBufferLength); * uint16_t crc = readCRC(_requestBuffer, _requestBufferLength);
@ -84,342 +97,396 @@ uint16_t calculateCRC(uint8_t *buffer, int length)
return crc; return crc;
} }
HCIEmulator::HCIEmulator(Stream * port) { HCIEmulator::HCIEmulator(Stream *port)
m_state.valid = false; {
m_statemachine=WAITING; m_state.valid = false;
m_statemachine = WAITING;
m_rxlen = m_txlen = 0; m_rxlen = m_txlen = 0;
m_recvTime=m_lastStateTime=0; m_recvTime = m_lastStateTime = 0;
m_skipFrame=false; m_skipFrame = false;
m_port = port; m_port = port;
m_statusCallback = NULL; m_statusCallback = NULL;
setLogLevel(DEFAULTLOGLEVEL); setLogLevel(DEFAULTLOGLEVEL);
}; };
void HCIEmulator::poll(){ #define TX_ON 25
if(m_port==NULL) return; void HCIEmulator::poll()
{
if (m_port == NULL)
return;
// receive Data // receive Data
if(m_port->available() >0) if (m_port->available() > 0)
{ {
m_rxlen+= m_port->readBytes((char*)(m_rxbuffer+m_rxlen), _min((int)(255-m_rxlen),m_port->available())); // Serial.println("got data");
if(m_rxlen > 254) m_rxlen += m_port->readBytes((char *)(m_rxbuffer + m_rxlen), _min((int)(255 - m_rxlen), m_port->available()));
if (m_rxlen > 254)
{ {
Log(LL_ERROR,"RX Bufferoverflow, skip next Frame"); Log(LL_ERROR, "RX Bufferoverflow, skip next Frame");
Log3(LL_DEBUG,"Buffer Data: ", m_rxbuffer, m_rxlen); Log3(LL_DEBUG, "Buffer Data: ", m_rxbuffer, m_rxlen);
m_rxlen=0; m_rxlen = 0;
m_skipFrame = true; m_skipFrame = true;
} }
m_recvTime = micros(); m_recvTime = micros();
} }
// check frame, process frame // Serial.printf("Data % x\n", m_txbuffer);
if(m_rxlen>0 && (micros()-m_recvTime > T3_5)) // check frame, process frame
if (m_rxlen > 0 && (micros() - m_recvTime > T3_5))
{ {
// check last action timeout -> reset > then 2sec // Serial.printf("Act on it % x\n", m_txbuffer);
if(m_statemachine!= WAITING && m_lastStateTime+2000<millis()){ // check last action timeout -> reset > then 2sec
if (m_statemachine != WAITING && m_lastStateTime + 2000 < millis())
{
m_statemachine = WAITING; m_statemachine = WAITING;
} }
if(!m_skipFrame){ if (!m_skipFrame)
{
processFrame(); processFrame();
// send response // send response
if(m_txlen > 0){ if (m_txlen > 0)
{
// fix crc // fix crc
uint16_t crc = calculateCRC(m_txbuffer, m_txlen - MODBUS_CRC_LENGTH); uint16_t crc = calculateCRC(m_txbuffer, m_txlen - MODBUS_CRC_LENGTH);
m_txbuffer[m_txlen - MODBUS_CRC_LENGTH] = crc & 0xFF; m_txbuffer[m_txlen - MODBUS_CRC_LENGTH] = crc & 0xFF;
m_txbuffer[(m_txlen - MODBUS_CRC_LENGTH) + 1] = crc >> 8; m_txbuffer[(m_txlen - MODBUS_CRC_LENGTH) + 1] = crc >> 8;
// send data // send data
m_lastSendTime = micros()-m_recvTime; m_lastSendTime = micros() - m_recvTime;
//Log(LL_DEBUG, ("ST:"+String(m_lastSendTime)).c_str()); // 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); m_port->write(m_txbuffer, m_txlen);
Log3(LL_DEBUG,"Response: ", m_txbuffer, m_txlen); Log3(LL_DEBUG, "Response: ", m_txbuffer, m_txlen);
// delayMicroseconds(50);
digitalWrite(TX_ON, LOW);
m_txlen = 0; m_txlen = 0;
} }
}
else
{
Serial.println("skipped frame");
} }
m_skipFrame = false; m_skipFrame = false;
m_rxlen=0; m_rxlen = 0;
} }
} }
void HCIEmulator::processFrame(){ void HCIEmulator::processFrame()
m_txlen = 0; // clear send buffer {
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); Log(LL_ERROR, "Frame skipped, invalid frame len");
Log3(LL_ERROR, "Data:", m_rxbuffer, m_rxlen);
return; return;
} }
// check device id, pass only device id 2 and 0 (broadcast) // 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); Log(LL_DEBUG, "Frame skipped, unsupported device id");
Log3(LL_DEBUG, "Data:", m_rxbuffer, m_rxlen);
return; return;
} }
// check crc // check crc
uint16_t crc = readCRC(m_rxbuffer, m_rxlen); 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); {
Log3(LL_ERROR, "Frame skipped, wrong crc", m_rxbuffer, m_rxlen);
return; return;
} }
Log3(LL_DEBUG,"Incomming Data: ", m_rxbuffer, m_rxlen); Log3(LL_DEBUG, "Incomming Data: ", m_rxbuffer, m_rxlen);
// dispatch modbus function // dispatch modbus function
switch(m_rxbuffer[1]){ switch (m_rxbuffer[1])
case 0x10:{ // Write Multiple registers {
if(m_rxlen == 0x1b && m_rxbuffer[0] == BROADCASTID) case 0x10:
{ { // Write Multiple registers
processBroadcastStatusFrame(); if (m_rxlen == 0x1b && m_rxbuffer[0] == BROADCASTID)
return; {
} processBroadcastStatusFrame();
break; return;
}
case 0x17:{ // Read/Write Multiple registers
if(m_rxbuffer[0] == DEVICEID){
switch(m_rxlen){
case 0x11:{
processDeviceStatusFrame();
return;
}
case 0x13:
processDeviceBusScanFrame();
return;;
}
}
break;
} }
break;
} }
Log3(LL_ERROR,"Frame skipped, unexpected data: ", m_rxbuffer, m_rxlen);
case 0x17:
{ // Read/Write Multiple registers
if (m_rxbuffer[0] == DEVICEID)
{
switch (m_rxlen)
{
case 0x11:
{
processDeviceStatusFrame();
return;
}
case 0x13:
processDeviceBusScanFrame();
return;
;
}
}
break;
}
}
Log3(LL_ERROR, "Frame skipped, unexpected data: ", m_rxbuffer, m_rxlen);
} }
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_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}; const unsigned char ResponseTemplate_Fcn17_Cmd04_L02[] = {0x02, 0x17, 0x04, 0x0F, 0x00, 0x04, 0xFD, 0x0A, 0x72};
void HCIEmulator::processDeviceStatusFrame(){ void HCIEmulator::processDeviceStatusFrame()
if(m_rxlen==0x11){ {
if (m_rxlen == 0x11)
{
unsigned char counter = m_rxbuffer[11]; unsigned char counter = m_rxbuffer[11];
unsigned char cmd = m_rxbuffer[12]; unsigned char cmd = m_rxbuffer[12];
if(m_rxbuffer[5] == 0x08){ if (m_rxbuffer[5] == 0x08)
// expose internal state {
// expose internal state
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 // 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 // 0011: 02 17 9C B9 00 08 9C 41 00 02 04 3E 03 00 00 EB CC
//res=> 02 17 10 3E 00 03 01 00 00 00 00 00 00 00 00 00 00 00 00 74 1B // res=> 02 17 10 3E 00 03 01 00 00 00 00 00 00 00 00 00 00 00 00 74 1B
memcpy(m_txbuffer, ResponseTemplate_Fcn17_Cmd03_L08, sizeof(ResponseTemplate_Fcn17_Cmd03_L08)); memcpy(m_txbuffer, ResponseTemplate_Fcn17_Cmd03_L08, sizeof(ResponseTemplate_Fcn17_Cmd03_L08));
m_txbuffer[0] = m_rxbuffer[0]; m_txbuffer[0] = m_rxbuffer[0];
m_txbuffer[3] = counter; m_txbuffer[3] = counter;
m_txbuffer[5] = cmd; m_txbuffer[5] = cmd;
m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd03_L08); m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd03_L08);
switch (m_statemachine)
switch(m_statemachine)
{ {
// open Door // open Door
case STARTOPENDOOR: case STARTOPENDOOR:
m_txbuffer[7]= 0x02; m_txbuffer[7] = 0x02;
m_txbuffer[8]= 0x10; m_txbuffer[8] = 0x10;
m_statemachine = STARTOPENDOOR_RELEASE; m_statemachine = STARTOPENDOOR_RELEASE;
m_lastStateTime = millis(); m_lastStateTime = millis();
break; break;
case STARTOPENDOOR_RELEASE: case STARTOPENDOOR_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){ if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
m_txbuffer[7]= 0x01; {
m_txbuffer[8]= 0x10; m_txbuffer[7] = 0x01;
m_statemachine = WAITING; m_txbuffer[8] = 0x10;
} m_statemachine = WAITING;
break; }
break;
// close Door // close Door
case STARTCLOSEDOOR: case STARTCLOSEDOOR:
m_txbuffer[7]= 0x02; m_txbuffer[7] = 0x02;
m_txbuffer[8]= 0x20; m_txbuffer[8] = 0x20;
m_statemachine = STARTCLOSEDOOR_RELEASE; m_statemachine = STARTCLOSEDOOR_RELEASE;
m_lastStateTime = millis(); m_lastStateTime = millis();
break; break;
case STARTCLOSEDOOR_RELEASE: case STARTCLOSEDOOR_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){ if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
m_txbuffer[7]= 0x01; {
m_txbuffer[8]= 0x20; m_txbuffer[7] = 0x01;
m_statemachine = WAITING; m_txbuffer[8] = 0x20;
} m_statemachine = WAITING;
break; }
break;
// stop Door // stop Door
case STARTSTOPDOOR: case STARTSTOPDOOR:
m_txbuffer[7]= 0x02; m_txbuffer[7] = 0x02;
m_txbuffer[8]= 0x40; m_txbuffer[8] = 0x40;
m_statemachine = STARTSTOPDOOR_RELEASE; m_statemachine = STARTSTOPDOOR_RELEASE;
m_lastStateTime = millis(); m_lastStateTime = millis();
break; break;
case STARTSTOPDOOR_RELEASE: case STARTSTOPDOOR_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){ if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
m_txbuffer[7]= 0x01; {
m_txbuffer[8]= 0x40; m_txbuffer[7] = 0x01;
m_statemachine = WAITING; m_txbuffer[8] = 0x40;
} m_statemachine = WAITING;
break; }
break;
// Ventilation // Ventilation
case STARTVENTPOSITION: case STARTVENTPOSITION:
m_txbuffer[7]= 0x02; m_txbuffer[7] = 0x02;
m_txbuffer[9]= 0x40; m_txbuffer[9] = 0x40;
m_statemachine = STARTVENTPOSITION_RELEASE; m_statemachine = STARTVENTPOSITION_RELEASE;
m_lastStateTime = millis(); m_lastStateTime = millis();
break; break;
case STARTVENTPOSITION_RELEASE: case STARTVENTPOSITION_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){ if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
m_txbuffer[7]= 0x01; {
m_txbuffer[9]= 0x40; m_txbuffer[7] = 0x01;
m_statemachine = WAITING; m_txbuffer[9] = 0x40;
} m_statemachine = WAITING;
break; }
break;
// Half Position
case STARTOPENDOORHALF:
m_txbuffer[7] = 0x02;
m_txbuffer[9] = 0x04;
m_statemachine = STARTOPENDOORHALF_RELEASE;
m_lastStateTime = millis();
break;
// Half Position case STARTOPENDOORHALF_RELEASE:
case STARTOPENDOORHALF: if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
m_txbuffer[7]= 0x02; {
m_txbuffer[9]= 0x04; m_txbuffer[7] = 0x01;
m_statemachine = STARTOPENDOORHALF_RELEASE; m_txbuffer[9] = 0x04;
m_lastStateTime = millis(); m_statemachine = WAITING;
break; }
break;
case STARTOPENDOORHALF_RELEASE: // Toggle Lamp
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){ case STARTTOGGLELAMP:
m_txbuffer[7]= 0x01; m_txbuffer[7] = 0x10;
m_txbuffer[9]= 0x04; m_txbuffer[9] = 0x02;
m_statemachine = WAITING; m_statemachine = STARTTOGGLELAMP_RELEASE;
} m_lastStateTime = millis();
break; break;
case STARTTOGGLELAMP_RELEASE:
if (m_lastStateTime + SIMULATEKEYPRESSDELAYMS < millis())
{
m_txbuffer[7] = 0x08;
m_txbuffer[9] = 0x02;
m_statemachine = WAITING;
}
break;
// Toggle Lamp case WAITING:
case STARTTOGGLELAMP: break;
m_txbuffer[7]= 0x10; }
m_txbuffer[9]= 0x02; return;
m_statemachine = STARTTOGGLELAMP_RELEASE;
m_lastStateTime = millis();
break;
case STARTTOGGLELAMP_RELEASE:
if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
m_txbuffer[7]= 0x08;
m_txbuffer[9]= 0x02;
m_statemachine = WAITING;
}
break;
case WAITING:
break;
}
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 // 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 // 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 // res=> 02 17 04 0F 00 04 FD 0A 72
memcpy(m_txbuffer, ResponseTemplate_Fcn17_Cmd04_L02, sizeof(ResponseTemplate_Fcn17_Cmd04_L02)); memcpy(m_txbuffer, ResponseTemplate_Fcn17_Cmd04_L02, sizeof(ResponseTemplate_Fcn17_Cmd04_L02));
m_txbuffer[0] = m_rxbuffer[0]; m_txbuffer[0] = m_rxbuffer[0];
m_txbuffer[3] = counter; m_txbuffer[3] = counter;
m_txbuffer[5] = cmd; m_txbuffer[5] = cmd;
m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd04_L02); m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd04_L02);
return; return;
} }
} }
Log3(LL_ERROR,"Frame skipped, unexpected data: ", m_rxbuffer, m_rxlen); Log3(LL_ERROR, "Frame skipped, unexpected data: ", m_rxbuffer, m_rxlen);
} }
const unsigned char ResponseTemplate_Fcn17_Cmd02_L05 []= {0x02,0x17,0x0a,0x00,0x00,0x02,0x05,0x04,0x30,0x10,0xff,0xa8,0x45,0x0e,0xdf}; 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 // 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 // 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 // res=> 02 17 0a 00 00 02 05 04 30 10 ff a8 45 0e df
unsigned char counter = m_rxbuffer[11]; unsigned char counter = m_rxbuffer[11];
unsigned char cmd = m_rxbuffer[12]; unsigned char cmd = m_rxbuffer[12];
memcpy(m_txbuffer, ResponseTemplate_Fcn17_Cmd02_L05, sizeof(ResponseTemplate_Fcn17_Cmd02_L05)); memcpy(m_txbuffer, ResponseTemplate_Fcn17_Cmd02_L05, sizeof(ResponseTemplate_Fcn17_Cmd02_L05));
m_txbuffer[0] = m_rxbuffer[0]; m_txbuffer[0] = m_rxbuffer[0];
m_txbuffer[3] = counter; m_txbuffer[3] = counter;
m_txbuffer[5] = cmd; m_txbuffer[5] = cmd;
m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd02_L05); m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd02_L05);
Log(LL_INFO,"Busscan received"); 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 {
// 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; bool hasChanged = false;
CHECKCHANGEDSET(m_state.lampOn,m_rxbuffer[20] == 0x14,hasChanged); CHECKCHANGEDSET(m_state.lampOn, m_rxbuffer[20] == 0x14, hasChanged);
CHECKCHANGEDSET(m_state.doorCurrentPosition,m_rxbuffer[10],hasChanged); CHECKCHANGEDSET(m_state.doorCurrentPosition, m_rxbuffer[10], hasChanged);
CHECKCHANGEDSET(m_state.doorTargetPosition, m_rxbuffer[9],hasChanged); CHECKCHANGEDSET(m_state.doorTargetPosition, m_rxbuffer[9], hasChanged);
CHECKCHANGEDSET(m_state.doorState, m_rxbuffer[11],hasChanged); CHECKCHANGEDSET(m_state.doorState, m_rxbuffer[11], hasChanged);
CHECKCHANGEDSET(m_state.reserved, m_rxbuffer[17],hasChanged); CHECKCHANGEDSET(m_state.reserved, m_rxbuffer[17], hasChanged);
CHECKCHANGEDSET(m_state.valid, true,hasChanged); CHECKCHANGEDSET(m_state.valid, true, hasChanged);
if(hasChanged){
Log3(LL_INFO,"New State: ",m_rxbuffer,m_rxlen);
if(m_statusCallback != NULL){
m_statusCallback(m_state);
}
}
}
void HCIEmulator::openDoor(){ if (hasChanged)
if(m_statemachine != WAITING){ {
Log3(LL_INFO, "New State: ", m_rxbuffer, m_rxlen);
if (m_statusCallback != NULL)
{
m_statusCallback(m_state);
}
}
}
void HCIEmulator::openDoor()
{
if (m_statemachine != WAITING)
{
return; return;
} }
m_lastStateTime = millis(); m_lastStateTime = millis();
m_statemachine = STARTOPENDOOR; m_statemachine = STARTOPENDOOR;
} }
void HCIEmulator::openDoorHalf(){ void HCIEmulator::openDoorHalf()
if(m_statemachine != WAITING){ {
if (m_statemachine != WAITING)
{
return; return;
} }
m_lastStateTime = millis(); m_lastStateTime = millis();
m_statemachine = STARTOPENDOORHALF; m_statemachine = STARTOPENDOORHALF;
} }
void HCIEmulator::closeDoor(){ void HCIEmulator::closeDoor()
if(m_statemachine != WAITING){ {
if (m_statemachine != WAITING)
{
return; return;
} }
m_lastStateTime = millis(); m_lastStateTime = millis();
m_statemachine = STARTCLOSEDOOR; m_statemachine = STARTCLOSEDOOR;
} }
void HCIEmulator::stopDoor(){ void HCIEmulator::stopDoor()
if(m_statemachine != WAITING){ {
if (m_statemachine != WAITING)
{
return; return;
} }
m_lastStateTime = millis(); m_lastStateTime = millis();
m_statemachine = STARTSTOPDOOR; m_statemachine = STARTSTOPDOOR;
} }
void HCIEmulator::toggleLamp(){ void HCIEmulator::toggleLamp()
if(m_statemachine != WAITING){ {
if (m_statemachine != WAITING)
{
return; return;
} }
m_lastStateTime = millis(); m_lastStateTime = millis();
m_statemachine = STARTTOGGLELAMP; m_statemachine = STARTTOGGLELAMP;
} }
void HCIEmulator::ventilationPosition(){ void HCIEmulator::ventilationPosition()
if(m_statemachine != WAITING){ {
if (m_statemachine != WAITING)
{
return; return;
} }
m_lastStateTime = millis(); m_lastStateTime = millis();
m_statemachine = STARTVENTPOSITION; m_statemachine = STARTVENTPOSITION;
} }
void HCIEmulator::onStatusChanged(callback_function_t handler) { void HCIEmulator::onStatusChanged(callback_function_t handler)
{
m_statusCallback = handler; m_statusCallback = handler;
} }

View File

@ -11,35 +11,34 @@
#define LL_INFO 3 #define LL_INFO 3
#define LL_DEBUG 4 #define LL_DEBUG 4
#define DEFAULTLOGLEVEL LL_WARN #define DEFAULTLOGLEVEL LL_DEBUG
#define DEVICEID 0x02 #define DEVICEID 0x02
#define BROADCASTID 0x00 #define BROADCASTID 0x00
#define SIMULATEKEYPRESSDELAYMS 100 #define SIMULATEKEYPRESSDELAYMS 100
// Modbus states that a baud rate higher than 19200 must use a fixed 750 us
// 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 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. // For baud rates below 19200 the timeing is more critical and has to be calculated.
// E.g. 9600 baud in a 10 bit packet is 960 characters per second // E.g. 9600 baud in a 10 bit packet is 960 characters per second
// In milliseconds this will be 960characters per 1000ms. So for 1 character // In milliseconds this will be 960characters per 1000ms. So for 1 character
// 1000ms/960characters is 1.04167ms per character and finaly modbus states an // 1000ms/960characters is 1.04167ms per character and finaly modbus states an
// intercharacter must be 1.5T or 1.5 times longer than a normal character and thus // 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. // 1.5T = 1.04167ms * 1.5 = 1.5625ms. A frame delay is 3.5T.
#define T1_5 750 #define T1_5 750
#define T3_5 4800 //1750 #define T3_5 4800 // 1750
enum DoorState : uint8_t
enum DoorState : uint8_t { {
DOOR_OPEN_POSITION = 0x20, DOOR_OPEN_POSITION = 0x20,
DOOR_CLOSE_POSITION = 0x40, DOOR_CLOSE_POSITION = 0x40,
DOOR_HALF_POSITION = 0x80, DOOR_HALF_POSITION = 0x80,
DOOR_MOVE_CLOSEPOSITION = 0x02, DOOR_MOVE_CLOSEPOSITION = 0x02,
DOOR_MOVE_OPENPOSITION = 0x01, DOOR_MOVE_OPENPOSITION = 0x01,
}; };
struct SHCIState{ struct SHCIState
{
bool valid; bool valid;
bool lampOn; bool lampOn;
uint8_t doorState; // see DoorState uint8_t doorState; // see DoorState
@ -48,7 +47,8 @@ struct SHCIState{
uint8_t reserved; uint8_t reserved;
}; };
enum StateMachine: uint8_t{ enum StateMachine : uint8_t
{
WAITING, WAITING,
STARTOPENDOOR, STARTOPENDOOR,
@ -70,11 +70,12 @@ enum StateMachine: uint8_t{
STARTVENTPOSITION_RELEASE STARTVENTPOSITION_RELEASE
}; };
class HCIEmulator { class HCIEmulator
{
public: public:
typedef std::function<void(const SHCIState&)> callback_function_t; typedef std::function<void(const SHCIState &)> callback_function_t;
HCIEmulator(Stream * port); HCIEmulator(Stream *port);
void poll(); void poll();
@ -85,16 +86,19 @@ public:
void toggleLamp(); void toggleLamp();
void ventilationPosition(); void ventilationPosition();
const SHCIState& getState() { const SHCIState &getState()
if(micros()-m_recvTime > 2000000){ {
// 2 sec without statusmessage if (micros() - m_recvTime > 2000000)
{
// 2 sec without statusmessage
m_state.valid = false; m_state.valid = false;
} }
return m_state; return m_state;
}; };
unsigned long getMessageAge(){ unsigned long getMessageAge()
return micros()-m_recvTime; {
return micros() - m_recvTime;
} }
int getLogLevel(); int getLogLevel();
@ -109,7 +113,7 @@ protected:
void processBroadcastStatusFrame(); void processBroadcastStatusFrame();
private: private:
callback_function_t m_statusCallback; callback_function_t m_statusCallback;
Stream *m_port; Stream *m_port;
SHCIState m_state; SHCIState m_state;
StateMachine m_statemachine; StateMachine m_statemachine;
@ -121,11 +125,14 @@ private:
size_t m_rxlen; size_t m_rxlen;
size_t m_txlen; size_t m_txlen;
unsigned char m_rxbuffer[255]; unsigned char m_rxbuffer[255] = {
unsigned char m_txbuffer[255]; 0,
};
unsigned char m_txbuffer[255] = {
0,
};
bool m_skipFrame; bool m_skipFrame;
}; };
#endif
#endif

File diff suppressed because one or more lines are too long

View File

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