initial commit
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
HCPBridge/.vscode
 | 
			
		||||
							
								
								
									
										5
									
								
								HCPBridge/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								HCPBridge/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
.pio
 | 
			
		||||
.vscode/.browse.c_cpp.db*
 | 
			
		||||
.vscode/c_cpp_properties.json
 | 
			
		||||
.vscode/launch.json
 | 
			
		||||
.vscode/ipch
 | 
			
		||||
							
								
								
									
										39
									
								
								HCPBridge/include/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								HCPBridge/include/README
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										46
									
								
								HCPBridge/lib/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								HCPBridge/lib/README
									
									
									
									
									
										Normal file
									
								
							@@ -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 <Foo.h>
 | 
			
		||||
#include <Bar.h>
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										17
									
								
								HCPBridge/platformio.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								HCPBridge/platformio.ini
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										425
									
								
								HCPBridge/src/hciemulator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										425
									
								
								HCPBridge/src/hciemulator.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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<millis()){
 | 
			
		||||
            m_statemachine = WAITING;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!m_skipFrame){
 | 
			
		||||
 | 
			
		||||
            processFrame();
 | 
			
		||||
 | 
			
		||||
            // send response
 | 
			
		||||
            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;
 | 
			
		||||
                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<millis()){
 | 
			
		||||
                        m_txbuffer[7]= 0x01;
 | 
			
		||||
                        m_txbuffer[8]= 0x10;
 | 
			
		||||
                        m_statemachine = WAITING; 
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                // close Door
 | 
			
		||||
                case STARTCLOSEDOOR:
 | 
			
		||||
                    m_txbuffer[7]= 0x02;
 | 
			
		||||
                    m_txbuffer[8]= 0x20;
 | 
			
		||||
                    m_statemachine = STARTCLOSEDOOR_RELEASE;
 | 
			
		||||
                    m_lastStateTime = millis();
 | 
			
		||||
                    break;
 | 
			
		||||
                case STARTCLOSEDOOR_RELEASE:
 | 
			
		||||
                    if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
 | 
			
		||||
                        m_txbuffer[7]= 0x01;
 | 
			
		||||
                        m_txbuffer[8]= 0x20;
 | 
			
		||||
                        m_statemachine = WAITING; 
 | 
			
		||||
                    }
 | 
			
		||||
                    break;   
 | 
			
		||||
 | 
			
		||||
                // stop Door
 | 
			
		||||
                case STARTSTOPDOOR:
 | 
			
		||||
                    m_txbuffer[7]= 0x02;
 | 
			
		||||
                    m_txbuffer[8]= 0x40;
 | 
			
		||||
                    m_statemachine = STARTSTOPDOOR_RELEASE;
 | 
			
		||||
                    m_lastStateTime = millis();
 | 
			
		||||
                    break;
 | 
			
		||||
                case STARTSTOPDOOR_RELEASE:
 | 
			
		||||
                    if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
 | 
			
		||||
                        m_txbuffer[7]= 0x01;
 | 
			
		||||
                        m_txbuffer[8]= 0x40;
 | 
			
		||||
                        m_statemachine = WAITING; 
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                // Ventilation
 | 
			
		||||
                case STARTVENTPOSITION:
 | 
			
		||||
                    m_txbuffer[7]= 0x02;
 | 
			
		||||
                    m_txbuffer[9]= 0x40;
 | 
			
		||||
                    m_statemachine = STARTVENTPOSITION_RELEASE;
 | 
			
		||||
                    m_lastStateTime = millis();
 | 
			
		||||
                    break;
 | 
			
		||||
                case STARTVENTPOSITION_RELEASE:
 | 
			
		||||
                    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;
 | 
			
		||||
                    m_txbuffer[9]= 0x04;
 | 
			
		||||
                    m_statemachine = STARTOPENDOORHALF_RELEASE;
 | 
			
		||||
                    m_lastStateTime = millis();
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case STARTOPENDOORHALF_RELEASE:
 | 
			
		||||
                    if(m_lastStateTime+SIMULATEKEYPRESSDELAYMS<millis()){
 | 
			
		||||
                        m_txbuffer[7]= 0x01;
 | 
			
		||||
                        m_txbuffer[9]= 0x04;
 | 
			
		||||
                        m_statemachine = WAITING; 
 | 
			
		||||
                    }
 | 
			
		||||
                    break;                                          
 | 
			
		||||
 | 
			
		||||
                // Toggle Lamp
 | 
			
		||||
                case STARTTOGGLELAMP:
 | 
			
		||||
                    m_txbuffer[7]= 0x10;
 | 
			
		||||
                    m_txbuffer[9]= 0x02;
 | 
			
		||||
                    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){
 | 
			
		||||
            //      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     
 | 
			
		||||
            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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										130
									
								
								HCPBridge/src/hciemulator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								HCPBridge/src/hciemulator.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
#ifndef __hciemulator_h
 | 
			
		||||
#define __hciemulator_h
 | 
			
		||||
 | 
			
		||||
#include <Stream.h>
 | 
			
		||||
#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<void(const SHCIState&)> 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
 | 
			
		||||
							
								
								
									
										1
									
								
								HCPBridge/src/index_html.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								HCPBridge/src/index_html.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										163
									
								
								HCPBridge/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								HCPBridge/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
#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();    
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								HCPBridge/src/webpage/buildindex.cmd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								HCPBridge/src/webpage/buildindex.cmd
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
python compress.py
 | 
			
		||||
							
								
								
									
										0
									
								
								HCPBridge/src/webpage/command
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								HCPBridge/src/webpage/command
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										42
									
								
								HCPBridge/src/webpage/compress.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								HCPBridge/src/webpage/compress.py
									
									
									
									
									
										Normal file
									
								
							@@ -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"<script>(.+?)<\/script>"
 | 
			
		||||
content = re.sub(regex, lambda x: "<script>"+jsmin(x.group(1))+"</script>" ,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("};");
 | 
			
		||||
							
								
								
									
										159
									
								
								HCPBridge/src/webpage/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								HCPBridge/src/webpage/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,159 @@
 | 
			
		||||
<!DOCTYPE HTML><html>
 | 
			
		||||
<head>
 | 
			
		||||
  <title>Garagentor Steuerung</title>
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
  <meta charset="UTF-8">
 | 
			
		||||
  <link rel="icon" href="data:,">
 | 
			
		||||
  <style>
 | 
			
		||||
    html {font-family: Arial; display: inline-block; text-align: center;}
 | 
			
		||||
    h2 {font-size: 3.0rem;}
 | 
			
		||||
    p {font-size: 3.0rem;}
 | 
			
		||||
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
 | 
			
		||||
    .switch {position: relative; display: inline-block; width: 80px; height: 48px} 
 | 
			
		||||
    .switch input {display: none}
 | 
			
		||||
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 6px}
 | 
			
		||||
    .slider:before {position: absolute; content: ""; height: 32px; width: 32px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
 | 
			
		||||
    input:checked+.slider {background-color: #b30000}
 | 
			
		||||
    input:checked+.slider:before {-webkit-transform: translateX(32px); -ms-transform: translateX(32px); transform: translateX(32px)}
 | 
			
		||||
	
 | 
			
		||||
	.button {
 | 
			
		||||
		border: 4px;
 | 
			
		||||
		border-style: solid;
 | 
			
		||||
		color: black;
 | 
			
		||||
		width: 100px;
 | 
			
		||||
		height: 100px;
 | 
			
		||||
		text-align: center;
 | 
			
		||||
		display: inline;
 | 
			
		||||
		text-decoration: none;
 | 
			
		||||
		display: inline-block;
 | 
			
		||||
		font-weight: bold;
 | 
			
		||||
		font-size: 20px;
 | 
			
		||||
		margin: 4px 2px;
 | 
			
		||||
		cursor: pointer;
 | 
			
		||||
		border-radius: 50px;
 | 
			
		||||
	}
 | 
			
		||||
	.buttonred {border-color: #ED5961;}
 | 
			
		||||
	.buttonyellow {border-color: #fcca00;}
 | 
			
		||||
	.buttongreen {border-color: #48bd81;}  
 | 
			
		||||
  </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <h2>Garagentor</h2>  
 | 
			
		||||
	<canvas width="250px" height="250px" id="dc"></canvas>
 | 
			
		||||
	<h3 id="status">warte auf Verbindung.</h3>	
 | 
			
		||||
	
 | 
			
		||||
	<hr/>
 | 
			
		||||
	<button class="button buttonred" onclick="doCommand(1)">Auf</button> <button class="button buttonyellow" onclick="doCommand(2)">Stop</button> <button class="button buttongreen" onclick="doCommand(0)">Zu</button>
 | 
			
		||||
	<hr/>
 | 
			
		||||
	<h4>Licht</h4><label class="switch"><input type="checkbox" onchange="doCommand(5)" id="light"><span class="slider"></span></label>
 | 
			
		||||
	<hr/>
 | 
			
		||||
					
 | 
			
		||||
	<script>
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	doorpos = 10;
 | 
			
		||||
	dir = -1;
 | 
			
		||||
	isconnected = false;
 | 
			
		||||
	var animation;
 | 
			
		||||
	function startDoorAnimation() {
 | 
			
		||||
		if(!animation){
 | 
			
		||||
			animation = setInterval(Draw, 200);
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
	function stopDoorAnimation() {
 | 
			
		||||
		if(animation){
 | 
			
		||||
			clearInterval(animation);
 | 
			
		||||
		}
 | 
			
		||||
		animation = null;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	function Draw(){		
 | 
			
		||||
		var svg =document.getElementById("svg");
 | 
			
		||||
		var dc = document.getElementById("dc");		
 | 
			
		||||
		var ctx = dc.getContext("2d");
 | 
			
		||||
		width = dc.width;
 | 
			
		||||
		height = dc.height;
 | 
			
		||||
		ctx.clearRect(0, 0, width, height);		
 | 
			
		||||
		xa= [18,18,10,3,174,347,340,332,332,297,297,52,52,18];
 | 
			
		||||
		ya= [327,114,117,100,22,100,117,114,327,327,112,112,327,327];
 | 
			
		||||
		
 | 
			
		||||
		ctx.fillStyle = "black";
 | 
			
		||||
		ctx.beginPath();
 | 
			
		||||
		for (i =0;i<xa.length;i++){			
 | 
			
		||||
			if(i==0){
 | 
			
		||||
				ctx.moveTo((xa[i]/350) * width , (ya[i]/350)*width);
 | 
			
		||||
			}else{
 | 
			
		||||
				ctx.lineTo((xa[i]/350) * width, (ya[i]/350)*width);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		ctx.fill();
 | 
			
		||||
		
 | 
			
		||||
		for(i=0;i<doorpos;i++){			
 | 
			
		||||
			ctx.fillRect((62/350) * width, ((120+21*i)/350) * width, (225/350) * width, (18/350) * width);
 | 
			
		||||
		}
 | 
			
		||||
		doorpos+=dir;	
 | 
			
		||||
		
 | 
			
		||||
		if(dir<0 && doorpos<=0){
 | 
			
		||||
			dir=dir*-1;			
 | 
			
		||||
		}
 | 
			
		||||
		if(dir>0 &&doorpos>=10){
 | 
			
		||||
			dir=dir*-1;			
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	function doCommand(action) {
 | 
			
		||||
      if(!isconnected){return;}
 | 
			
		||||
	  var xhr = new XMLHttpRequest();
 | 
			
		||||
	  xhr.open("GET", "/command?action="+action, true); 	  
 | 
			
		||||
	  xhr.send();
 | 
			
		||||
	}  
 | 
			
		||||
	
 | 
			
		||||
	function setDoorState(state){
 | 
			
		||||
		stopDoorAnimation();		
 | 
			
		||||
		if(state == 0) {doorpos = 10;dir = -1};
 | 
			
		||||
		if(state == 1) {doorpos = 0; dir = 1};		
 | 
			
		||||
		if(state == 2) {doorpos = 5; dir = 1};		
 | 
			
		||||
		Draw();		
 | 
			
		||||
		if(state == 3) startDoorAnimation();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	function getStatusText(id){
 | 
			
		||||
		result = "Das Tor "
 | 
			
		||||
		switch(id)
 | 
			
		||||
		{
 | 
			
		||||
			case 0x20: setDoorState(1); return result+"ist geöffnet.";
 | 
			
		||||
			case 0x40: setDoorState(0); return result+"ist geschlossen.";
 | 
			
		||||
			case 0x80: setDoorState(2); return result+"ist teilgeöffnet.";
 | 
			
		||||
			case 0x00: setDoorState(2); return result+"ist teilgeöffnet.";
 | 
			
		||||
			case 0x02: setDoorState(3); return result+"schließt.";
 | 
			
		||||
			case 0x01: setDoorState(3); return result+"öffnet.";
 | 
			
		||||
			case -1: setDoorState(0); return "keine Verbindung zum Tor.";
 | 
			
		||||
			default: return "unbekanter Status: " + id;
 | 
			
		||||
		}    
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	function updateData(){
 | 
			
		||||
			var xmlhttp = new XMLHttpRequest();
 | 
			
		||||
			xmlhttp.onreadystatechange = function() {
 | 
			
		||||
			  if (this.readyState == 4 && this.status == 200) {
 | 
			
		||||
				var status = JSON.parse(this.responseText);	
 | 
			
		||||
                isconnected = status.valid;
 | 
			
		||||
				document.getElementById("status").innerHTML = getStatusText(status.valid?status.doorstate:-1);
 | 
			
		||||
				document.getElementById("light").checked = status.lamp & status.valid;
 | 
			
		||||
				return;
 | 
			
		||||
			  }
 | 
			
		||||
			  isconnected= false;
 | 
			
		||||
			};			
 | 
			
		||||
			xmlhttp.open("GET", "/status", true);
 | 
			
		||||
			xmlhttp.send();
 | 
			
		||||
	}
 | 
			
		||||
		
 | 
			
		||||
	Draw();
 | 
			
		||||
	updateData();	
 | 
			
		||||
	setInterval(updateData, 3000);
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
</script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										1
									
								
								HCPBridge/src/webpage/runtestserver.cmd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								HCPBridge/src/webpage/runtestserver.cmd
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
python -m http.server 
 | 
			
		||||
							
								
								
									
										9
									
								
								HCPBridge/src/webpage/status
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								HCPBridge/src/webpage/status
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "valid" : true,
 | 
			
		||||
  "doorstate" : 1,
 | 
			
		||||
  "doorposition" : 0,
 | 
			
		||||
  "doortarget" : 0,
 | 
			
		||||
  "lamp" : true,
 | 
			
		||||
  "debug" : 0,
 | 
			
		||||
  "lastresponse" : 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								HCPBridge/test/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								HCPBridge/test/README
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										71
									
								
								Investigation/ProtocolAnalyse.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								Investigation/ProtocolAnalyse.txt
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										5
									
								
								Investigation/bussniffer/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Investigation/bussniffer/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
.pio
 | 
			
		||||
.vscode/.browse.c_cpp.db*
 | 
			
		||||
.vscode/c_cpp_properties.json
 | 
			
		||||
.vscode/launch.json
 | 
			
		||||
.vscode/ipch
 | 
			
		||||
							
								
								
									
										39
									
								
								Investigation/bussniffer/include/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Investigation/bussniffer/include/README
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										46
									
								
								Investigation/bussniffer/lib/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								Investigation/bussniffer/lib/README
									
									
									
									
									
										Normal file
									
								
							@@ -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 <Foo.h>
 | 
			
		||||
#include <Bar.h>
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										14
									
								
								Investigation/bussniffer/platformio.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Investigation/bussniffer/platformio.ini
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										81
									
								
								Investigation/bussniffer/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								Investigation/bussniffer/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <SoftwareSerial.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								Investigation/bussniffer/test/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Investigation/bussniffer/test/README
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Investigation/oszi/busscan.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Investigation/oszi/busscan.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 106 KiB  | 
							
								
								
									
										53
									
								
								Investigation/readme.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								Investigation/readme.txt
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
		Reference in New Issue
	
	Block a user