commit 10894d2a6ee812d40b9320dee3a903e8bbfbcbff Author: Maik Hofmann Date: Sun Feb 14 13:07:08 2021 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b830d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +HCPBridge/.vscode diff --git a/HCPBridge/.gitignore b/HCPBridge/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/HCPBridge/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/HCPBridge/include/README b/HCPBridge/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/HCPBridge/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/HCPBridge/lib/README b/HCPBridge/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/HCPBridge/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/HCPBridge/platformio.ini b/HCPBridge/platformio.ini new file mode 100644 index 0000000..e4d26de --- /dev/null +++ b/HCPBridge/platformio.ini @@ -0,0 +1,17 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp12e] +platform = espressif8266 +board = esp12e +framework = arduino +lib_deps = + ottowinter/ESPAsyncWebServer-esphome@^1.2.7 + bblanchon/ArduinoJson@^6.17.2 diff --git a/HCPBridge/src/hciemulator.cpp b/HCPBridge/src/hciemulator.cpp new file mode 100644 index 0000000..eb015d9 --- /dev/null +++ b/HCPBridge/src/hciemulator.cpp @@ -0,0 +1,425 @@ +#include "hciemulator.h" +#define CHECKCHANGEDSET(Target,Value,Flag) if((Target)!=(Value)){Target=Value;Flag=true;} +int hciloglevel = DEFAULTLOGLEVEL; + +#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){ + return; + } + if(data!=NULL && datalen>0){ + String newmsg(msg); + char str[4]; + for (size_t i = 0; i < datalen; i++){ + snprintf(str,sizeof(str),"%02x ", data[i]); + newmsg+=str; + } + Serial.println(newmsg); + }else{ + Serial.println(msg); + } +} +#else + #define Log(Level,Message) + #define Log3(Level,Message,Buffer, Len) +#endif + + +int HCIEmulator::getLogLevel(){ + return hciloglevel; + +} +void HCIEmulator::setLogLevel(int level){ + hciloglevel=level; +} + +//modbus crc calculation borrowed from: +//https://github.com/yaacov/ArduinoModbusSlave +#define MODBUS_CRC_LENGTH 2 +#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]) +/** + * Calculate the CRC of the passed byte array from zero up to the passed length. + * + * @param buffer The byte array containing the data. + * @param length The length of the byte array. + * + * @return The calculated CRC as an unsigned 16 bit integer. + * + * Calculate and add the CRC. + * uint16_t crc = Modbus::calculateCRC(_responseBuffer, _responseBufferLength - MODBUS_CRC_LENGTH); + * _responseBuffer[_responseBufferLength - MODBUS_CRC_LENGTH] = crc & 0xFF; + * _responseBuffer[(_responseBufferLength - MODBUS_CRC_LENGTH) + 1] = crc >> 8; + * + * + * #define MODBUS_FRAME_SIZE 4 + * #define MODBUS_CRC_LENGTH 2 + * uint16_t crc = readCRC(_requestBuffer, _requestBufferLength); + * #define readUInt16(arr, index) word(arr[index], arr[index + 1]) + * #define readCRC(arr, length) word(arr[(length - MODBUS_CRC_LENGTH) + 1], arr[length - MODBUS_CRC_LENGTH]) + */ +uint16_t calculateCRC(uint8_t *buffer, int length) +{ + int i, j; + uint16_t crc = 0xFFFF; + uint16_t tmp; + + // Calculate the CRC. + for (i = 0; i < length; i++) + { + crc = crc ^ buffer[i]; + for (j = 0; j < 8; j++) + { + tmp = crc & 0x0001; + crc = crc >> 1; + if (tmp) + { + crc = crc ^ 0xA001; + } + } + } + return crc; +} + +HCIEmulator::HCIEmulator(Stream * port) { + m_state.valid = false; + m_statemachine=WAITING; + m_rxlen = m_txlen = 0; + m_recvTime=m_lastStateTime=0; + m_skipFrame=false; + m_port = port; + m_statusCallback = NULL; + setLogLevel(DEFAULTLOGLEVEL); +}; + +void HCIEmulator::poll(){ + + if(m_port==NULL) return; + + // receive Data + if(m_port->available() >0) + { + 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"); + Log3(LL_DEBUG,"Buffer Data: ", m_rxbuffer, m_rxlen); + m_rxlen=0; + m_skipFrame = true; + } + m_recvTime = micros(); + } + + + // check frame, process frame + if(m_rxlen>0 && (micros()-m_recvTime > T3_5)) + { + // check last action timeout -> reset > then 2sec + if(m_statemachine!= WAITING && m_lastStateTime+2000 0){ + + // fix crc + 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) + 1] = crc >> 8; + + // send data + m_lastSendTime = micros()-m_recvTime; + + //Log(LL_DEBUG, ("ST:"+String(m_lastSendTime)).c_str()); + + m_port->write(m_txbuffer, m_txlen); + Log3(LL_DEBUG,"Response: ", m_txbuffer, m_txlen); + m_txlen = 0; + } + } + + m_skipFrame = false; + m_rxlen=0; + } +} + +void HCIEmulator::processFrame(){ + m_txlen = 0; // clear send buffer + + 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){ + Log(LL_DEBUG,"Frame skipped, unsupported device id"); + Log3(LL_DEBUG,"Data:", m_rxbuffer,m_rxlen); + return; + } + + // check crc + uint16_t crc = readCRC(m_rxbuffer, m_rxlen); + if(crc != calculateCRC(m_rxbuffer,m_rxlen-MODBUS_CRC_LENGTH)){ + Log3(LL_ERROR,"Frame skipped, wrong crc", m_rxbuffer,m_rxlen); + return; + } + + Log3(LL_DEBUG,"Incomming Data: ", m_rxbuffer, m_rxlen); + + // dispatch modbus function + switch(m_rxbuffer[1]){ + case 0x10:{ // Write Multiple registers + if(m_rxlen == 0x1b && m_rxbuffer[0] == BROADCASTID) + { + processBroadcastStatusFrame(); + return; + } + break; + } + + 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_Cmd04_L02 []= {0x02,0x17,0x04,0x0F,0x00,0x04,0xFD,0x0A,0x72}; +void HCIEmulator::processDeviceStatusFrame(){ + if(m_rxlen==0x11){ + unsigned char counter = m_rxbuffer[11]; + unsigned char cmd = m_rxbuffer[12]; + 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 + //res=> 02 17 10 3E 00 03 01 00 00 00 00 00 00 00 00 00 00 00 00 74 1B + memcpy_P(m_txbuffer, ResponseTemplate_Fcn17_Cmd03_L08, sizeof(ResponseTemplate_Fcn17_Cmd03_L08)); + m_txbuffer[0] = m_rxbuffer[0]; + m_txbuffer[3] = counter; + m_txbuffer[5] = cmd; + m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd03_L08); + + + switch(m_statemachine) + { + // open Door + case STARTOPENDOOR: + m_txbuffer[7]= 0x02; + m_txbuffer[8]= 0x10; + m_statemachine = STARTOPENDOOR_RELEASE; + m_lastStateTime = millis(); + break; + case STARTOPENDOOR_RELEASE: + if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS 02 17 04 0F 00 04 FD 0A 72 + memcpy_P(m_txbuffer, ResponseTemplate_Fcn17_Cmd04_L02, sizeof(ResponseTemplate_Fcn17_Cmd04_L02)); + m_txbuffer[0] = m_rxbuffer[0]; + m_txbuffer[3] = counter; + m_txbuffer[5] = cmd; + m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd04_L02); + return; + } + } + + 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}; +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 + unsigned char counter = m_rxbuffer[11]; + unsigned char cmd = m_rxbuffer[12]; + memcpy_P(m_txbuffer, ResponseTemplate_Fcn17_Cmd02_L05, sizeof(ResponseTemplate_Fcn17_Cmd02_L05)); + m_txbuffer[0] = m_rxbuffer[0]; + m_txbuffer[3] = counter; + m_txbuffer[5] = cmd; + m_txlen = sizeof(ResponseTemplate_Fcn17_Cmd02_L05); + + Log(LL_INFO,"Busscan received"); +} + +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); + CHECKCHANGEDSET(m_state.doorCurrentPosition,m_rxbuffer[10],hasChanged); + CHECKCHANGEDSET(m_state.doorTargetPosition, m_rxbuffer[9],hasChanged); + CHECKCHANGEDSET(m_state.doorState, m_rxbuffer[11],hasChanged); + CHECKCHANGEDSET(m_state.reserved, m_rxbuffer[17],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(m_statemachine != WAITING){ + return; + } + m_lastStateTime = millis(); + m_statemachine = STARTOPENDOOR; +} + +void HCIEmulator::openDoorHalf(){ + if(m_statemachine != WAITING){ + return; + } + m_lastStateTime = millis(); + m_statemachine = STARTOPENDOORHALF; +} + +void HCIEmulator::closeDoor(){ + if(m_statemachine != WAITING){ + return; + } + m_lastStateTime = millis(); + m_statemachine = STARTCLOSEDOOR; +} + +void HCIEmulator::stopDoor(){ + if(m_statemachine != WAITING){ + return; + } + m_lastStateTime = millis(); + m_statemachine = STARTSTOPDOOR; +} + +void HCIEmulator::toggleLamp(){ + if(m_statemachine != WAITING){ + return; + } + m_lastStateTime = millis(); + m_statemachine = STARTTOGGLELAMP; +} + +void HCIEmulator::ventilationPosition(){ + if(m_statemachine != WAITING){ + return; + } + m_lastStateTime = millis(); + m_statemachine = STARTVENTPOSITION; +} + +void HCIEmulator::onStatusChanged(callback_function_t handler) { + m_statusCallback = handler; +} \ No newline at end of file diff --git a/HCPBridge/src/hciemulator.h b/HCPBridge/src/hciemulator.h new file mode 100644 index 0000000..f496025 --- /dev/null +++ b/HCPBridge/src/hciemulator.h @@ -0,0 +1,130 @@ +#ifndef __hciemulator_h +#define __hciemulator_h + +#include +#include "SoftwareSerial.h" + +#define LL_OFF 0 +#define LL_ERROR 1 +#define LL_WARN 2 +#define LL_INFO 3 +#define LL_DEBUG 4 + +#define DEFAULTLOGLEVEL LL_WARN + +#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. +// 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 +// 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 +// 1.5T = 1.04167ms * 1.5 = 1.5625ms. A frame delay is 3.5T. +#define T1_5 750 +#define T3_5 4800 //1750 + + +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{ + bool valid; + bool lampOn; + uint8_t doorState; // see DoorState + uint8_t doorCurrentPosition; + uint8_t doorTargetPosition; + uint8_t reserved; +}; + +enum StateMachine: uint8_t{ + WAITING, + + STARTOPENDOOR, + STARTOPENDOOR_RELEASE, + + STARTOPENDOORHALF, + STARTOPENDOORHALF_RELEASE, + + STARTCLOSEDOOR, + STARTCLOSEDOOR_RELEASE, + + STARTSTOPDOOR, + STARTSTOPDOOR_RELEASE, + + STARTTOGGLELAMP, + STARTTOGGLELAMP_RELEASE, + + STARTVENTPOSITION, + STARTVENTPOSITION_RELEASE +}; + +class HCIEmulator { +public: + typedef std::function callback_function_t; + + HCIEmulator(Stream * port); + + void poll(); + + void openDoor(); + void openDoorHalf(); + void closeDoor(); + void stopDoor(); + void toggleLamp(); + void ventilationPosition(); + + const SHCIState& getState() { + if(micros()-m_recvTime > 2000000){ + // 2 sec without statusmessage + m_state.valid = false; + } + return m_state; + }; + + unsigned long getMessageAge(){ + return micros()-m_recvTime; + } + + int getLogLevel(); + void setLogLevel(int level); + + void onStatusChanged(callback_function_t handler); + +protected: + void processFrame(); + void processDeviceStatusFrame(); + void processDeviceBusScanFrame(); + void processBroadcastStatusFrame(); + +private: + callback_function_t m_statusCallback; + Stream *m_port; + SHCIState m_state; + StateMachine m_statemachine; + + unsigned long m_recvTime; + unsigned long m_lastStateTime; + unsigned long m_lastSendTime; + + size_t m_rxlen; + size_t m_txlen; + + unsigned char m_rxbuffer[255]; + unsigned char m_txbuffer[255]; + + bool m_skipFrame; +}; + + +#endif \ No newline at end of file diff --git a/HCPBridge/src/index_html.h b/HCPBridge/src/index_html.h new file mode 100644 index 0000000..4d8feaa --- /dev/null +++ b/HCPBridge/src/index_html.h @@ -0,0 +1 @@ +const uint8_t index_html[] PROGMEM = {0x78,0xDA,0xA5,0x57,0x6D,0x6F,0xE3,0x36,0x12,0xFE,0xBC,0xFE,0x15,0x5C,0x15,0x08,0xA4,0x8D,0xED,0x48,0xB2,0xB3,0xEB,0x4A,0x96,0x8B,0xED,0x66,0xAF,0xED,0x61,0x7B,0x2D,0x9A,0x5C,0xD1,0x17,0xEC,0x07,0x9A,0x1A,0xD9,0x44,0xA8,0x97,0xA3,0xA8,0xD8,0xAE,0xE1,0x4F,0xF7,0x9F,0x0E,0xF7,0xF9,0x0E,0xF7,0xBF,0x6E,0x48,0xCA,0xB6,0x9C,0x38,0xDB,0x02,0x0D,0xAC,0x88,0xE4,0xCC,0x3C,0xF3,0xC2,0x99,0x21,0x35,0x7D,0x79,0xF3,0xDD,0xBB,0xBB,0x9F,0xBF,0x7F,0x4F,0xBE,0xBE,0xFB,0xF6,0xC3,0x6C,0xBA,0x54,0xB9,0xC0,0xFF,0x40,0xD3,0xD9,0x54,0x71,0x25,0x60,0xF6,0x15,0x95,0x74,0x01,0x85,0x2A,0x25,0xB9,0x55,0xD0,0x80,0x6C,0x8A,0xC5,0xF4,0xCA,0xD2,0xA6,0x39,0x28,0x4A,0x0A,0x9A,0x43,0xF2,0xC0,0x61,0x55,0x95,0x52,0x11,0x56,0x16,0x0A,0xD9,0x13,0x67,0xC5,0x53,0xB5,0x4C,0x52,0x78,0xE0,0x0C,0x06,0x66,0xD2,0x27,0xBC,0xE0,0x8A,0x53,0x31,0xA8,0x19,0x15,0x90,0x04,0x4E,0x8B,0xC0,0x96,0x54,0xD6,0xA0,0x92,0xBF,0xDF,0xFD,0x65,0x30,0x99,0x4D,0x05,0x2F,0xEE,0x89,0x04,0x91,0x70,0x04,0x23,0x4B,0x09,0x59,0x92,0x52,0x45,0xA3,0xFE,0x6C,0x5A,0xAB,0x0D,0xEA,0xED,0x11,0xFC,0xD3,0xA6,0x92,0x6D,0x86,0xEA,0x06,0x19,0xCD,0xB9,0xD8,0x44,0xE4,0xAD,0x44,0xF0,0x98,0xA4,0xBC,0xAE,0x04,0xC5,0x39,0x2F,0x10,0x0A,0x06,0x73,0x51,0xB2,0xFB,0x98,0x28,0x58,0xAB,0x01,0x15,0x7C,0x51,0x44,0x84,0xA1,0x89,0x20,0xE3,0x9D,0x45,0x0A,0x5B,0x9C,0x9A,0xFF,0x06,0x11,0x19,0x0D,0x7D,0x09,0x79,0x4B,0xAB,0x9E,0x27,0xCD,0xCB,0x74,0x43,0xB6,0x39,0x5D,0x5B,0xEF,0x22,0xF2,0xDA,0xF7,0xAB,0x75,0x4C,0x72,0x2A,0x17,0xBC,0x88,0x70,0x4C,0x68,0xA3,0xCA,0x98,0x54,0x34,0x4D,0x79,0xB1,0x18,0xCC,0x4B,0xA5,0xCA,0x3C,0x22,0xE1,0x35,0xB2,0x59,0x8C,0x61,0xBD,0xE2,0x8A,0x2D,0xC9,0xB6,0x2A,0x6B,0x0C,0x4D,0x89,0xA6,0xA1,0xE3,0x54,0xF1,0x07,0x78,0xD6,0x8F,0x56,0xDB,0xC4,0x28,0x5B,0x02,0x5F,0x2C,0x55,0x44,0xC6,0x93,0x6A,0xBD,0x23,0x27,0x98,0xBC,0xA8,0x1A,0x45,0xB6,0x07,0x94,0xA2,0x2C,0x60,0xAF,0x55,0xF0,0x14,0x64,0x57,0x2B,0x9D,0xD7,0xA5,0x68,0x14,0x6A,0x55,0x65,0x15,0x11,0x3F,0x26,0x02,0x32,0x65,0x06,0xD2,0x6A,0xC0,0xD1,0xDE,0x01,0x3D,0xA4,0xEC,0x7E,0x21,0xCB,0xA6,0x48,0x07,0xAC,0x14,0xA5,0x8C,0xC8,0x67,0x8C,0x31,0xCD,0x22,0x11,0x79,0x20,0x69,0xCA,0x9B,0x1A,0x43,0x82,0x56,0x75,0x55,0x46,0x73,0xC8,0x4A,0x09,0xE7,0x35,0xB7,0xA9,0x13,0x11,0xC7,0x39,0x3A,0x36,0x0A,0xB5,0x9B,0xAD,0xCF,0x76,0x62,0x2D,0x9B,0xE8,0xE1,0xDE,0x22,0x3B,0x79,0x6A,0x53,0x96,0x65,0x31,0x19,0xAC,0x60,0x7E,0xCF,0xD5,0x40,0x49,0x5A,0xEC,0xB5,0x0E,0xC7,0x35,0xBA,0xFA,0x78,0xE1,0x91,0xF5,0xA3,0xBD,0xF5,0x26,0x94,0x11,0x5B,0x02,0xBB,0x87,0xF4,0xF2,0x10,0xBE,0x33,0x0A,0xE7,0x23,0x1F,0xFF,0x3E,0x21,0x75,0x88,0xC0,0x89,0x55,0xB8,0x84,0x5E,0x98,0x21,0xEE,0x3E,0xFC,0xE4,0x6A,0x4F,0x3D,0x34,0x3D,0xAF,0x3F,0xCD,0xF0,0x09,0xE2,0xAE,0xF7,0xA2,0xF7,0x62,0x38,0x6F,0x30,0x42,0x05,0xD9,0xF6,0x5E,0xBC,0xB0,0xDE,0x61,0xAE,0x60,0xB0,0x0E,0xD3,0x81,0x29,0xA9,0x88,0xE0,0x26,0xF0,0x54,0x2F,0xB7,0x9E,0xCC,0x05,0x3A,0xA7,0xE7,0x6D,0xE8,0x03,0x93,0xDC,0x38,0xDF,0x6F,0xCC,0x61,0xE1,0x4C,0x5D,0xE1,0xEA,0xA3,0xE4,0x3D,0x30,0xA6,0xC0,0x4A,0x49,0x6D,0xCC,0x75,0x46,0x9E,0xE1,0x6D,0x13,0x1D,0x09,0xA6,0xF4,0x56,0xAD,0xC2,0x79,0x29,0xD2,0xC3,0xA2,0xAD,0xC7,0xB0,0x35,0xA1,0x2D,0x39,0xED,0x19,0x09,0xED,0x12,0x6B,0x64,0xAD,0xFD,0xA8,0x4A,0xBE,0x37,0xE9,0xD1,0xEE,0x5E,0x5B,0xE1,0xDD,0x21,0x48,0x12,0x52,0xDC,0x53,0xCB,0xB4,0xDF,0xCF,0xF7,0x37,0xD7,0x9F,0xBF,0x0E,0xE2,0x23,0xD3,0x06,0x84,0x28,0x57,0x4F,0xF8,0x32,0xC6,0xA8,0xEF,0x77,0xF8,0x16,0x12,0xA0,0x78,0xC2,0x36,0x9E,0xCC,0xD3,0x09,0xC2,0x11,0x5D,0xAA,0xD3,0x2B,0xDB,0xCE,0xA6,0x57,0xB6,0xE1,0xEA,0x86,0x82,0xCD,0x37,0xEC,0xF4,0x5C,0x24,0x85,0xB3,0x29,0xA3,0xC5,0x03,0xAD,0x6D,0x11,0x24,0xA1,0xB6,0xBB,0x2D,0x8F,0x76,0xC2,0xD3,0x24,0x65,0x08,0x63,0xF9,0x10,0x62,0xA4,0x97,0x6A,0x45,0x55,0x53,0xCF,0x56,0x54,0x2A,0xC0,0x4E,0x94,0x91,0x1F,0x41,0xCE,0x79,0x91,0x62,0x03,0x1F,0x22,0xEE,0x08,0xF9,0x24,0x2A,0xB5,0xF9,0xC1,0x04,0xAD,0xEB,0xC4,0x69,0x67,0x87,0x78,0x38,0xA4,0x2C,0x98,0xE0,0xEC,0x3E,0x49,0xCB,0x77,0x65,0x9E,0xD3,0x22,0x75,0x03,0x6F,0xF6,0xB6,0xC9,0xA6,0x57,0x96,0xE9,0x93,0x08,0x36,0x58,0xE7,0x40,0x42,0x6F,0x76,0x8B,0x9D,0xE6,0x0F,0xA1,0x98,0x50,0x9E,0x03,0xF1,0xBD,0xD9,0x2F,0xCD,0x11,0x42,0xBB,0xB3,0x1C,0xCF,0x3E,0x70,0xB6,0x54,0xE8,0xE0,0x18,0x4F,0x12,0x3A,0x07,0xD1,0x62,0xDA,0xAE,0x38,0x9B,0xDA,0xB6,0xA8,0x36,0x15,0x24,0xA6,0x34,0xE7,0xE5,0x5A,0x43,0x2F,0x69,0xB1,0x80,0x0E,0xF6,0xB5,0xA7,0x63,0x28,0x74,0x94,0xF1,0xD8,0xA9,0xE8,0xDE,0x34,0x5B,0xC5,0x18,0x6C,0xBD,0x86,0x2F,0xA3,0xC2,0xEA,0xAE,0x99,0xE4,0x95,0x9A,0xA5,0x65,0x29,0xB1,0xBD,0x25,0x81,0x1F,0xA7,0x5C,0x26,0x83,0x20,0xE6,0x35,0xB6,0xB6,0x02,0x98,0x82,0x34,0xC9,0xA8,0xA8,0x21,0x7E,0xA0,0x92,0xD0,0x82,0xE7,0xA6,0x10,0xE2,0xAC,0x29,0x98,0x1E,0x10,0xDC,0x32,0xA9,0x6E,0x50,0xFE,0xED,0x9E,0xE6,0x7A,0x5B,0x9E,0xB9,0x2F,0x0F,0xBC,0xDE,0xF6,0x30,0x4C,0xF0,0xC8,0xFC,0x46,0xE7,0xF6,0x03,0x15,0xEE,0x8D,0xA4,0xAB,0x7E,0xE8,0xFB,0x5E,0xBC,0xDB,0xF5,0x3A,0x78,0x65,0x75,0x06,0xAE,0x83,0xC6,0x04,0x50,0x79,0x40,0x39,0x12,0x30,0x93,0x8F,0x8A,0x8A,0x46,0x88,0xB8,0x03,0xAB,0x95,0x21,0x92,0x76,0xA2,0x7E,0x58,0x60,0xD0,0x58,0x93,0x63,0xBA,0x0E,0x17,0xA0,0xDE,0x0B,0xD0,0xC3,0x2F,0x37,0xDF,0xA4,0xAE,0x83,0x44,0xC7,0x33,0xBE,0xA6,0xEC,0x79,0xAE,0x94,0xB5,0x4C,0x4C,0xAD,0x31,0x8F,0x35,0xFD,0x9D,0x3E,0x09,0xD6,0xCA,0x75,0xC2,0x14,0x69,0xED,0x4D,0x82,0x0D,0xCD,0x20,0x6E,0x53,0x1F,0xE7,0x76,0x14,0xA3,0xDC,0xD0,0xF8,0xF1,0x03,0x86,0xD8,0xF5,0xFB,0x7E,0xDF,0x5E,0x37,0x2C,0xD9,0x8B,0xD7,0x34,0xF9,0x35,0x98,0xF4,0xF5,0xCF,0xEF,0x8F,0xFA,0xC1,0x9B,0x71,0x7F,0x34,0x7E,0x83,0x0F,0xCE,0x46,0xA1,0x79,0xC2,0xCF,0xDF,0x98,0xE7,0x3A,0xD4,0xBF,0x60,0xF2,0x31,0xDE,0xA0,0xD0,0x28,0x7C,0xD3,0x0F,0x82,0x31,0x3E,0xF8,0xF6,0xFD,0x7E,0x18,0x9A,0x97,0x99,0xE2,0xB2,0x26,0x5B,0x96,0xD0,0x3C,0xED,0xFC,0xA3,0x31,0x28,0xE3,0x42,0xDC,0xEA,0xF2,0xC6,0x64,0xD6,0xAD,0xD4,0x31,0xAB,0x73,0xC0,0x3E,0xF5,0x3D,0x55,0x4B,0xD7,0x8B,0xB1,0x6B,0xBB,0x3C,0xF1,0x63,0x3E,0x5D,0xD3,0xA1,0x80,0x62,0x81,0xBE,0xF1,0xCB,0x4B,0xB3,0x43,0x3C,0x49,0x7C,0xDC,0x1C,0x94,0xC8,0xCB,0x07,0xB8,0x2B,0x5D,0x77,0x4D,0x7F,0xE5,0x1F,0xAF,0x46,0xD7,0xBE,0xF7,0xCA,0x7A,0xE7,0x6E,0x1E,0xAD,0xE0,0x9E,0x01,0xE6,0x96,0x91,0xD2,0x6D,0xF4,0x0F,0x4B,0xED,0x7A,0x7B,0x83,0x4F,0xAC,0x6A,0xF3,0xD8,0xDA,0xB4,0xE7,0x30,0x21,0x76,0x5F,0x87,0x27,0x98,0x6E,0x10,0xFA,0x97,0x61,0xF0,0x8A,0x7B,0x27,0xCB,0x61,0x78,0x7D,0x32,0x0F,0x26,0xA7,0x7A,0x7B,0xAD,0x86,0xCB,0x04,0xEB,0x24,0x46,0xAF,0xF1,0x35,0xF5,0x2F,0x2E,0xDA,0xE5,0xA9,0x0E,0x81,0xAE,0x20,0x7C,0x5E,0x0D,0x74,0x0F,0xB6,0x2C,0xB3,0x23,0xCB,0x0C,0x8B,0xEC,0x94,0xA7,0x93,0xA5,0xC7,0x5A,0xA6,0xCC,0x26,0xBB,0xAE,0xA4,0x4E,0x2D,0x7A,0x5B,0x09,0xAA,0x91,0x05,0x22,0xEB,0xF4,0x5B,0x2F,0x65,0x52,0xC0,0x8A,0xFC,0xF4,0xED,0x87,0xAF,0x95,0xAA,0x7E,0x80,0x7F,0x34,0x50,0x2B,0x0C,0x09,0x12,0x86,0x65,0x05,0x85,0xEB,0x7C,0xF5,0xFE,0xCE,0xE9,0x3B,0x57,0xCC,0xE2,0x7E,0x61,0x71,0x13,0xE7,0xD2,0x0E,0xFA,0x4A,0x36,0x60,0xD9,0x6B,0x40,0xB5,0x5E,0xB7,0x66,0xB0,0x58,0x75,0x25,0xDE,0x62,0x53,0x06,0x57,0xB7,0x66,0xF0,0xB6,0x67,0xCA,0x53,0x87,0xC1,0x50,0x4D,0x02,0x3C,0x69,0x25,0xBB,0x0E,0x3D,0x38,0xD2,0x2D,0xF9,0x84,0x1A,0x1E,0xA9,0xD7,0x7B,0xAA,0xAD,0xDB,0x0E,0xD3,0xC8,0x3B,0xD7,0x71,0xBA,0x66,0x63,0x31,0xDE,0x9A,0x73,0xE4,0x4E,0xD7,0x23,0x37,0x31,0xAB,0x1B,0x81,0xF7,0xFB,0x1B,0x3C,0x91,0xEE,0xF0,0xAB,0xC0,0xE9,0xD9,0xCE,0xAA,0x89,0xBD,0x2D,0xA3,0x35,0x10,0x7F,0x1D,0xFA,0xD1,0x89,0xC3,0x81,0x17,0xDB,0x58,0x13,0x2B,0x7E,0xE9,0xF0,0x5A,0x21,0xF8,0x7F,0xFF,0xF9,0x9F,0x7F,0x65,0x59,0x01,0x6A,0x88,0xF5,0x61,0x45,0xC7,0x8F,0x44,0xFD,0x67,0x44,0x6B,0xB6,0x14,0x65,0x8D,0x81,0x3E,0x8A,0x4E,0x1E,0x89,0x86,0x67,0x45,0x15,0x70,0x71,0x56,0xB3,0xFF,0x27,0xC5,0xC3,0x53,0xF1,0xD1,0x13,0x71,0x6D,0x32,0x47,0xD1,0xFF,0xFD,0xBB,0x2B,0x16,0xFC,0x9E,0xD8,0x63,0x5D,0x83,0xE0,0x99,0x08,0x39,0xF7,0x80,0x75,0xDF,0x39,0xE7,0xC9,0x6F,0x4D,0xAE,0x37,0x09,0x05,0x53,0xC8,0x28,0xA2,0x45,0x2D,0x67,0x53,0xCC,0xE1,0x9E,0xEA,0xBE,0x4F,0xEC,0xFE,0xE2,0xAD,0xFB,0x12,0x2F,0x80,0xDD,0x02,0x6A,0x2A,0xFC,0xEC,0x82,0x1B,0xFC,0xF4,0x6A,0x9B,0xFD,0x3A,0x17,0x4B,0xAC,0x8C,0x67,0xAA,0xC4,0x12,0x87,0xFA,0xDE,0x40,0xD3,0x8D,0xC9,0xB0,0xF6,0x54,0xDD,0x23,0xDA,0xD3,0x47,0x2D,0x79,0x3D,0x34,0x3C,0xB7,0x36,0x0B,0xC7,0x17,0x17,0x66,0xCD,0x5E,0x58,0x30,0x75,0xF1,0x18,0xB3,0x87,0x8B,0x5D,0xF8,0xEB,0xED,0x77,0x7F,0x1B,0x56,0xFA,0x13,0x71,0x2F,0x5B,0x57,0x65,0x51,0x83,0x4E,0x49,0xEF,0xE4,0x70,0xB5,0x02,0x43,0x3C,0xCA,0xD0,0x95,0xE7,0x8F,0x25,0xC3,0xE5,0x78,0x43,0x8E,0x72,0x52,0x7F,0xF8,0x26,0xA7,0x69,0xDE,0x85,0xF9,0xA2,0x9D,0xE8,0x62,0x32,0x3E,0x45,0x03,0x4C,0xE6,0x67,0xB1,0xCD,0x85,0x01,0xA1,0xDB,0x5B,0xFF,0xDE,0x22,0x41,0xF3,0xEA,0xE2,0xC4,0xBA,0x43,0xEB,0x79,0x7A,0x39,0xD8,0x1D,0x63,0xD9,0xED,0x3A,0xAD,0xD9,0xFB,0x2E,0xD3,0xB2,0x1C,0x3A,0x4D,0x5B,0xDC,0xDD,0x4D,0x8B,0xBB,0x37,0x84,0x23,0xA1,0xAF,0xBF,0x51,0xBC,0x18,0xAF,0x30,0xF6,0xC2,0x82,0x37,0x28,0x73,0xF3,0xBC,0x32,0x5F,0xFF,0xFF,0x07,0x0E,0x2F,0x8B,0x65}; \ No newline at end of file diff --git a/HCPBridge/src/main.cpp b/HCPBridge/src/main.cpp new file mode 100644 index 0000000..39055ed --- /dev/null +++ b/HCPBridge/src/main.cpp @@ -0,0 +1,163 @@ +#include +#include +#include "AsyncJson.h" +#include "ArduinoJson.h" +#include "hciemulator.h" +#include "index_html.h" + +/* create this file and add your wlan credentials + const char* ssid = "MyWLANSID"; + const char* password = "MYPASSWORD"; +*/ +#include "../../../private/credentials.h" + + +// switch relay sync to the lamp +// e.g. the Wifi Relay Board U4648 +#define USERELAY + +// use alternative uart pins +//#define SWAPUART + +#define RS485 Serial + +// Relay Board parameters +#define ESP8266_GPIO2 2 // Blue LED. +#define ESP8266_GPIO4 4 // Relay control. +#define ESP8266_GPIO5 5 // Optocoupler input. +#define LED_PIN ESP8266_GPIO2 + + +// Hörmann HCP2 based on modbus rtu @57.6kB 8E1 +HCIEmulator emulator(&RS485); + +// webserver on port 80 +AsyncWebServer server(80); + + +// switch GPIO4 und GPIO2 sync to the lamp +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){ + digitalWrite( ESP8266_GPIO4, state.lampOn ); + digitalWrite(LED_PIN, state.lampOn); + }else + { + digitalWrite( ESP8266_GPIO4, false ); + digitalWrite(LED_PIN, false); + } +} + +// toggle lamp to expected state +void switchLamp(bool on){ + bool toggle = (on && !emulator.getState().lampOn) || (!on && emulator.getState().lampOn); + if(toggle){ + emulator.toggleLamp(); + } +} + +// setup mcu +void setup(){ + + //setup modbus + RS485.begin(57600,SERIAL_8E1); + #ifdef SWAPUART + RS485.swap(); + #endif + + //setup wifi + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + WiFi.setAutoReconnect(true); + while (WiFi.status() != WL_CONNECTED) { + emulator.poll(); + } + + // setup http server + 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); + }); + + server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request){ + const SHCIState& doorstate = emulator.getState(); + AsyncResponseStream *response = request->beginResponseStream("application/json"); + DynamicJsonDocument root(1024); + root["valid"] = doorstate.valid; + root["doorstate"] = doorstate.doorState; + root["doorposition"] = doorstate.doorCurrentPosition; + root["doortarget"] = doorstate.doorTargetPosition; + root["lamp"] = doorstate.lampOn; + root["debug"] = doorstate.reserved; + root["lastresponse"] = emulator.getMessageAge()/1000; + serializeJson(root,*response); + request->send(response); + }); + + server.on("/command", HTTP_GET, [] (AsyncWebServerRequest *request) { + if (request->hasParam("action")) { + int actionid = request->getParam("action")->value().toInt(); + switch (actionid){ + case 0: + emulator.closeDoor(); + break; + case 1: + emulator.openDoor(); + break; + case 2: + emulator.stopDoor(); + break; + case 3: + emulator.ventilationPosition(); + break; + case 4: + emulator.openDoorHalf(); + break; + case 5: + emulator.toggleLamp(); + break; + default: + break; + } + } + request->send(200, "text/plain", "OK"); + }); + + server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) { + if (request->hasParam("channel") && request->hasParam("state")) { + String channel = request->getParam("channel")->value(); + String state = request->getParam("state")->value(); + if(channel.equals("door")){ + if(state=="1"){ + emulator.openDoor(); + }else{ + emulator.closeDoor(); + } + } + if(channel.equals("light")){ + switchLamp(state=="1"); + } + } + request->send(200, "text/plain", "OK"); + }); + + server.begin(); + + //setup relay board +#ifdef USERELAY + pinMode( ESP8266_GPIO4, OUTPUT ); // Relay control pin. + pinMode( ESP8266_GPIO5, INPUT_PULLUP ); // Input pin. + pinMode( LED_PIN, OUTPUT ); // ESP8266 module blue L + digitalWrite( ESP8266_GPIO4, 0 ); + digitalWrite(LED_PIN,0); + emulator.onStatusChanged(onStatusChanged); +#endif + +} + +// mainloop +void loop(){ + emulator.poll(); +} \ No newline at end of file diff --git a/HCPBridge/src/webpage/buildindex.cmd b/HCPBridge/src/webpage/buildindex.cmd new file mode 100644 index 0000000..6ed0ebd --- /dev/null +++ b/HCPBridge/src/webpage/buildindex.cmd @@ -0,0 +1 @@ +python compress.py diff --git a/HCPBridge/src/webpage/command b/HCPBridge/src/webpage/command new file mode 100644 index 0000000..e69de29 diff --git a/HCPBridge/src/webpage/compress.py b/HCPBridge/src/webpage/compress.py new file mode 100644 index 0000000..4ef9f95 --- /dev/null +++ b/HCPBridge/src/webpage/compress.py @@ -0,0 +1,42 @@ +#pip install htmlmin +#or python -m pip install htmlmin +#pip install jsmin +#or python -m pip install jsmin + +import gzip +import zlib +import htmlmin +from jsmin import jsmin + + + +content = "" +with open('index.html','rt') as f: + content=f.read() + + +content = htmlmin.minify(content, remove_comments=True, remove_empty_space=True, remove_all_empty_space=True, reduce_empty_attributes=True, reduce_boolean_attributes=False, remove_optional_attribute_quotes=True, convert_charrefs=True, keep_pre=False) + + +import re +regex = r"" ,content, 0, re.DOTALL) + + +#with gzip.open('htmltest.html.gz', 'wb') as f: +# f.write(content.encode("UTF-8")) + +#with open('htmltest.html.z','wb') as f: +# f.write(zlib.compress(content.encode("UTF-8"),9)) + +result ="" +for c in zlib.compress(content.encode("UTF-8"),9): + result= result + ("0x%02X" %c) + if len(result)> 0: + result=result + "," + + +with open('../index_html.h',"wt") as f: + f.write("const uint8_t index_html[] PROGMEM = {"); + f.write(result.strip(",")) + f.write("};"); \ No newline at end of file diff --git a/HCPBridge/src/webpage/index.html b/HCPBridge/src/webpage/index.html new file mode 100644 index 0000000..303cbef --- /dev/null +++ b/HCPBridge/src/webpage/index.html @@ -0,0 +1,159 @@ + + + Garagentor Steuerung + + + + + + +

Garagentor

+ +

warte auf Verbindung.

+ +
+ +
+

Licht

+
+ + + + \ No newline at end of file diff --git a/HCPBridge/src/webpage/runtestserver.cmd b/HCPBridge/src/webpage/runtestserver.cmd new file mode 100644 index 0000000..092262b --- /dev/null +++ b/HCPBridge/src/webpage/runtestserver.cmd @@ -0,0 +1 @@ +python -m http.server diff --git a/HCPBridge/src/webpage/status b/HCPBridge/src/webpage/status new file mode 100644 index 0000000..c050664 --- /dev/null +++ b/HCPBridge/src/webpage/status @@ -0,0 +1,9 @@ +{ + "valid" : true, + "doorstate" : 1, + "doorposition" : 0, + "doortarget" : 0, + "lamp" : true, + "debug" : 0, + "lastresponse" : 0 +} \ No newline at end of file diff --git a/HCPBridge/test/README b/HCPBridge/test/README new file mode 100644 index 0000000..b94d089 --- /dev/null +++ b/HCPBridge/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html diff --git a/Investigation/ProtocolAnalyse.txt b/Investigation/ProtocolAnalyse.txt new file mode 100644 index 0000000..8ff7a0b --- /dev/null +++ b/Investigation/ProtocolAnalyse.txt @@ -0,0 +1,71 @@ +0011: 02 17 9C B9 00 08 9C 41 00 02 04 3E 03 00 00 EB CC (Lese 9CB9:08), (Sende) (zähler)3E (cmd)03 (Data) 0000 +0015: 02 17 10 3E 00 03 01 00 00 00 00 00 00 00 00 00 00 00 00 74 1B Default response +----------- + 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 +0015: 02 17 10 70 00 03 01 02 10 00 00 00 00 00 00 00 00 00 00 6C 88 <- Tor auf pressed +0015: 02 17 10 78 00 03 01 01 10 00 00 00 00 00 00 00 00 00 00 6F 4A <- Tor auf released +ORG: 00 00 +0015: 02 17 10 6C 00 03 01 02 20 00 00 00 00 00 00 00 00 00 00 9B 41 <- Tor zu pressed +0015: 02 17 10 78 00 03 01 01 20 00 00 00 00 00 00 00 00 00 00 90 4A <- Tor zu released +ORG: 00 00 +0015: 02 17 10 02 00 03 01 02 40 00 00 00 00 00 00 00 00 00 00 C9 0C <- Stop pressed +0015: 02 17 10 05 00 03 01 01 40 00 00 00 00 00 00 00 00 00 00 8F 3A <- Stop released +ORG 00 00 +0015: 02 17 10 48 00 03 01 02 00 40 00 00 00 00 00 00 00 00 00 29 4E <- Lüftunsschalter pressed +0015: 02 17 10 4C 00 03 01 01 00 40 00 00 00 00 00 00 00 00 00 2F 89 <- Lüftunsschalter released +ORG: 00 00 +0015: 02 17 10 44 00 03 01 02 00 04 00 00 00 00 00 00 00 00 00 6F 4A <- 1/2 pressed +0015: 02 17 10 53 00 03 01 01 00 04 00 00 00 00 00 00 00 00 00 24 B0 <- 1/2 released +ORG 00 00 +0015: 02 17 10 0A 00 03 01 10 00 02 00 00 00 00 00 00 00 00 00 02 F9 <- Lichtschalter pressed +0015: 02 17 10 0D 00 03 01 08 00 02 00 00 00 00 00 00 00 00 00 60 EB <- Lichtschalter released +ORG: 00 00 +----------- +0011: 02 17 9C B9 00 02 9C 41 00 02 04 0F04 1700 7B 21 <- 0F zähler, 04 cmd? data: 1700 (licht an) oder 1701 (licht aus) +0009: 02 17 04 0F00 04FD 0A 72 <- 0F00 zähler, 04FD 04 cmd, FD result? + + +################################################################################### +# Broadcast Message +################################################################################### +001B: 00 10 9D 31 00 09 12 65 00 00 00 40 60 00 00 00 00 00 00 00 10 00 01 00 00 5A 1D <- Licht startet +001B: 00 10 9D 31 00 09 12 66 00 00 00 40 60 00 00 00 00 00 00 00 14 00 01 00 00 5B 99 <- Licht an +001B: 00 10 9D 31 00 09 12 3A 00 00 00 40 60 00 00 00 00 00 00 00 04 00 01 00 00 48 18 <- Licht geht aus +001B: 00 10 9D 31 00 09 12 3D 00 00 00 40 60 00 00 00 00 00 00 00 00 00 01 00 00 08 AD <- Licht ist aus + 00 +------------------ + +AA: Zielwert? +BB: Istwert +CC: 0x40 Tor zu, 0x01 Tor öffnet, 0x20 Tor komplett auf, 0x02 Tor schließt, 0x05 1/2 öffnen, 0x80 Teilgeöffnet, 0x00 gestoppt + 0x42(66) kurz beim schließen + + +DD: ? Geschwindigkeit / Kraft? + + AA BB CC DD + 00 01 02 03 04 05 06 07 08 09 00 01 02 03 04 05 06 07 08 09 00 01 02 03 04 05 06 +001B: 00 10 9D 31 00 09 12 53 00 00 00 40 60 00 00 00 00 00 00 00 14 00 01 00 00 44 5B <- Tor zu +001B: 00 10 9D 31 00 09 12 55 00 C8 00 01 60 00 00 00 00 00 00 00 14 00 01 00 00 B2 05 <- Tor öffnet komplett +001B: 00 10 9D 31 00 09 12 5B 00 C8 04 01 60 00 00 00 00 0E 00 00 14 00 01 00 00 55 61 +001B: 00 10 9D 31 00 09 12 5D 00 C8 04 01 60 00 00 00 00 0E 00 00 14 00 01 00 00 B5 E8 +001B: 00 10 9D 31 00 09 12 60 00 C8 06 01 60 00 00 00 00 0E 00 00 14 00 01 00 00 AA 49 +001B: 00 10 9D 31 00 09 12 63 00 C8 08 01 60 00 00 00 00 0E 00 00 14 00 01 00 00 D4 09 +001B: 00 10 9D 31 00 09 12 65 00 C8 0A 01 60 00 00 00 00 0E 00 00 14 00 01 00 00 B6 81 +001B: 00 10 9D 31 00 09 12 67 00 C8 0C 01 60 00 00 00 00 0E 00 00 14 00 01 00 00 91 3B +001B: 00 10 9D 31 00 09 12 68 00 C8 0E 01 60 00 00 00 00 0E 00 00 14 00 01 00 00 20 2D +001B: 00 10 9D 31 00 09 12 6A 00 C8 10 01 60 00 00 00 00 0E 00 00 14 00 01 00 00 1F 9D +001B: 00 10 9D 31 00 09 12 6C 00 C8 12 01 60 00 00 00 00 0E 00 00 14 00 01 00 00 7D 15 +.. +001B: 00 10 9D 31 00 09 12 2F 00 C8 C6 01 60 00 00 00 00 02 00 00 14 00 01 00 00 4C 8F +001B: 00 10 9D 31 00 09 12 30 00 C8 C8 20 60 00 00 00 00 00 00 00 14 00 01 00 00 55 A1 <- Tor komplett auf +.. +001B: 00 10 9D 31 00 09 12 11 00 00 38 02 60 00 00 00 00 09 00 00 14 00 01 00 00 4A 0D <- Tor schließt +001B: 00 10 9D 31 00 09 12 4F 00 00 16 02 60 00 00 00 00 06 00 00 14 00 01 00 00 D7 AB +001B: 00 10 9D 31 00 09 12 53 00 00 14 02 60 00 00 00 00 06 00 00 14 00 01 00 00 92 3C +001B: 00 10 9D 31 00 09 12 0A 00 00 00 02 60 00 00 00 00 03 00 00 14 00 01 00 00 84 83 +001B: 00 10 9D 31 00 09 12 0B 00 00 00 40 60 00 00 00 00 00 00 00 14 00 01 00 00 D7 28 <- Tor komplett zu + + +001B: 00 10 9D 31 00 09 12 30 00 C8 00 05 60 00 00 00 00 00 00 00 14 00 01 00 00 B9 12 <- 1/2 Öffnung +001B: 00 10 9D 31 00 09 12 59 00 18 18 80 60 00 00 00 00 00 00 00 14 00 01 00 00 B5 41 <- 1/2 Teilgeöffnet diff --git a/Investigation/bussniffer/.gitignore b/Investigation/bussniffer/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/Investigation/bussniffer/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/Investigation/bussniffer/include/README b/Investigation/bussniffer/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/Investigation/bussniffer/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/Investigation/bussniffer/lib/README b/Investigation/bussniffer/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/Investigation/bussniffer/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/Investigation/bussniffer/platformio.ini b/Investigation/bussniffer/platformio.ini new file mode 100644 index 0000000..2c7f0eb --- /dev/null +++ b/Investigation/bussniffer/platformio.ini @@ -0,0 +1,14 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp12e] +platform = espressif8266 +board = esp12e +framework = arduino diff --git a/Investigation/bussniffer/src/main.cpp b/Investigation/bussniffer/src/main.cpp new file mode 100644 index 0000000..d29532b --- /dev/null +++ b/Investigation/bussniffer/src/main.cpp @@ -0,0 +1,81 @@ +#include +#include + + +SoftwareSerial RS485(D6,D7); +#define RS485IF RS485 +//#define RS485IF Serial + + +void setup() +{ + // Hörmann HCP2 based on modbus rtu @57.6kB 8E1 + RS485IF.begin(57600,SWSERIAL_8E1); + Serial.begin(115200,SERIAL_8N1); +} + +#define T3_5 1750 + +#define BUFFERSIZE 255 +int rxlen = 0; +int txlen = 0; +byte rxbbuffer[BUFFERSIZE]; +byte txbbuffer[BUFFERSIZE]; + +unsigned long recvTime =0; +unsigned long recvStartTime =0; +unsigned long recvGapTime = 0; + +void sendUShort(ushort data){ + Serial.write(data>>8); + Serial.write(data&0xFF); +} + +int loopcount = 0; +void loop() +{ + if(loopcount==0) + { + rxlen=0; + rxbbuffer[rxlen++] = 0x8; + rxbbuffer[rxlen++] = 0xDE; + rxbbuffer[rxlen++] = 0xAD; + rxbbuffer[rxlen++] = 0xBE; + rxbbuffer[rxlen++] = 0xEF; + rxbbuffer[rxlen++] = 0xDE; + rxbbuffer[rxlen++] = 0xAD; + rxbbuffer[rxlen++] = 0xBE; + rxbbuffer[rxlen++] = 0xEF; + + Serial.write(rxbbuffer,rxlen); + + rxlen=0; + loopcount=1; + } + + if(RS485IF.available() >0) + { + if(rxlen==0){ + recvStartTime = micros(); + recvGapTime = recvStartTime-recvTime; + } + rxlen+= RS485IF.readBytes((char*)(rxbbuffer+rxlen), _min(BUFFERSIZE-rxlen,RS485IF.available())); + recvTime = micros(); + + } + + if(rxlen>0 && (micros()-recvTime > T3_5 || rxlen > 0x80)) + { + Serial.write(rxlen); // Len + sendUShort((short)recvGapTime); // Gap + sendUShort((short)recvTime-recvStartTime); // Packagelen + Serial.write(rxbbuffer,rxlen); + rxlen=0; + } + + if(Serial.available() >0) + { + rxlen= Serial.readBytes((char*)(rxbbuffer), _min(BUFFERSIZE,Serial.available())); + RS485IF.write(rxbbuffer,rxlen); + } +} diff --git a/Investigation/bussniffer/test/README b/Investigation/bussniffer/test/README new file mode 100644 index 0000000..b94d089 --- /dev/null +++ b/Investigation/bussniffer/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html diff --git a/Investigation/oszi/busscan.png b/Investigation/oszi/busscan.png new file mode 100644 index 0000000..bd7a013 Binary files /dev/null and b/Investigation/oszi/busscan.png differ diff --git a/Investigation/readme.txt b/Investigation/readme.txt new file mode 100644 index 0000000..3f4ab65 --- /dev/null +++ b/Investigation/readme.txt @@ -0,0 +1,53 @@ +Hardware/Protocol: +#################### +RS 485 Modbus 57,6 kB - 8E1 LSB +IOLevel 0-3,2 V + + +Protocol: +########## +https://modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf + +Request (Master): +92 SlaveID +17 Function code: Read/Write Multiple registers +9C B9 Read Starting Address +00 05 Quantity to Read +9C 41 Write Starting Address +00 03 Quantity to Write +06 Write Byte Count +00 02 00 00 01 00 +15 D9 CRC ModBUS + +Response (Slave) +92 SlaveID +17 Function code +05 Byte Count +XX XX XX XX XX +YY YY CRC ModBus + + +Antwortzeitverhalten Master<> Slave: +min 3974 ms +max 5916 ms + + +Kommunikation (siehe ProtocolAnalyse.txt): +nach dem BUS-Scan wird folgendes Muster immer wieder durchlaufen: + +Master: Statusabfrage + 02 17 9C B9 00 08 9C 41 00 02 04 3E 03 00 00 EB CC +Slave: Status des Erweiterungsboards + 02 17 10 3E 00 03 01 00 00 00 00 00 00 00 00 00 00 00 00 74 1B +Master (Broadcast): Status des Antriebs + 00 10 9D 31 00 09 12 16 00 C8 C8 20 60 00 00 00 00 00 00 00 14 00 01 00 00 BE E2 + + +Besondere Nachrichten: +1. Beim Lichtschalten: +Master: 02 17 9C B9 00 02 9C 41 00 02 04 0F 04 17 00 7B 21 <- 0F zähler, 04 cmd? data: 1700 (licht an) oder 1701 (licht aus) +Slave: 02 17 04 0F00 04FD 0A 72 <- 0F00 zähler, 04FD 04 cmd, FD result? Konstante Nachricht? + +2. Busscan +Master: 02 17 9C B9 00 05 9C 41 00 03 06 00 02 00 00 01 02 f8 35 +Slave: 02 17 0a 00 00 02 05 04 30 10 ff a8 45 0e df