#include "main.h" #include #include #include "MySensors.h" #define ICSC_SYS_PACK 0x58 #define SOH 1 #define STX 2 #define ETX 3 #define EOT 4 // message buffers MyMessage _msg; // Buffer for incoming messages MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others) // Receiving header information char _header[6]; // Reception state machine control and storage variables unsigned char _recPhase; unsigned char _recPos; unsigned char _recCommand; unsigned char _recLen; unsigned char _recStation; unsigned char _recSender; unsigned char _recCS; unsigned char _recCalcCS; unsigned char _packet_received; UART_HandleTypeDef *_huart; char _data[MY_RS485_MAX_MESSAGE_LENGTH]; uint8_t _packet_len; unsigned char _packet_from; _Bool send(MyMessage *message, uint8_t data_type) { message->last = MY_NODE_ID; message->sender = MY_NODE_ID; message->destination = GATEWAY_ADDRESS; message->command_echo_payload = (data_type << 5) + C_SET; return transportSend(message); } _Bool sendHeartbeat(void) { _msgTmp.last = MY_NODE_ID; _msgTmp.sender = MY_NODE_ID; _msgTmp.destination = GATEWAY_ADDRESS; _msgTmp.command_echo_payload = (P_ULONG32 << 5) + C_INTERNAL; _msgTmp.type = I_HEARTBEAT_RESPONSE; _msgTmp.version_length = (4 << 3) + V2_MYS_HEADER_PROTOCOL_VERSION; _msgTmp.ulValue = 0; return transportSend(&_msgTmp); } _Bool present(const uint8_t childSensorId, const mysensors_sensor_t sensorType, char *desc) { _msgTmp.last = MY_NODE_ID; _msgTmp.sender = MY_NODE_ID; _msgTmp.destination = GATEWAY_ADDRESS; _msgTmp.command_echo_payload = (P_STRING << 5) + C_PRESENTATION; _msgTmp.type = sensorType; _msgTmp.sensor = childSensorId; _msgTmp.version_length = (strlen(desc) << 3) + V2_MYS_HEADER_PROTOCOL_VERSION; strcpy((char *)_msgTmp.data, desc); return transportSend(&_msgTmp); } void registerNode(void) { _msgTmp.last = MY_NODE_ID; _msgTmp.sender = MY_NODE_ID; _msgTmp.destination = GATEWAY_ADDRESS; _msgTmp.command_echo_payload = (P_BYTE << 5) + C_INTERNAL; _msgTmp.type = I_REGISTRATION_REQUEST; _msgTmp.sensor = 0; _msgTmp.version_length = (1 << 3) + V2_MYS_HEADER_PROTOCOL_VERSION; _msgTmp.bValue = MY_CORE_VERSION; transportSend(&_msgTmp); } void sendLibraryInfo(void) { _msgTmp.last = MY_NODE_ID; _msgTmp.sender = MY_NODE_ID; _msgTmp.destination = GATEWAY_ADDRESS; _msgTmp.command_echo_payload = (P_STRING << 5) + C_INTERNAL; _msgTmp.type = I_VERSION; _msgTmp.version_length = (strlen(MY_LIBRARY_VERSION) << 3) + V2_MYS_HEADER_PROTOCOL_VERSION; strcpy((char *)_msgTmp.data, MY_LIBRARY_VERSION); transportSend(&_msgTmp); } _Bool sendSketchInfo(const char *name, const char *version) { _Bool result = 1; if (name) { _msgTmp.last = MY_NODE_ID; _msgTmp.sender = MY_NODE_ID; _msgTmp.destination = GATEWAY_ADDRESS; _msgTmp.command_echo_payload = (P_STRING << 5) + C_INTERNAL; _msgTmp.type = I_SKETCH_NAME; _msgTmp.version_length = (strlen(name) << 3) + V2_MYS_HEADER_PROTOCOL_VERSION; strcpy((char *)_msgTmp.data, name); result &= transportSend(&_msgTmp); } if (version) { _msgTmp.last = MY_NODE_ID; _msgTmp.sender = MY_NODE_ID; _msgTmp.destination = GATEWAY_ADDRESS; _msgTmp.command_echo_payload = (P_STRING << 5) + C_INTERNAL; _msgTmp.type = I_SKETCH_VERSION; _msgTmp.version_length = (strlen(name) << 3) + V2_MYS_HEADER_PROTOCOL_VERSION; strcpy((char *)_msgTmp.data, version); result &= transportSend(&_msgTmp); } sendLibraryInfo(); return result; } // Message delivered through _msg _Bool _processInternalCoreMessage(void) { const uint8_t type = _msg.type; if (_msg.sender == GATEWAY_ADDRESS) { if (type == I_PRESENTATION) { // Re-send node presentation to controller present_node(); } else if (type == I_HEARTBEAT_REQUEST) { (void)sendHeartbeat(); } else if (type == I_REBOOT) { NVIC_SystemReset(); } else if (type == I_VERSION) { sendLibraryInfo(); } else { return 0; // further processing required } } else { return 0; // further processing required } return 1; // if not GW or no further processing required } void transportProcessMessage(void) { // get message length and limit size const uint8_t msgLength = (_msg.version_length & 0xF8) >> 3; // calculate expected length const uint8_t command = _msg.command_echo_payload & 0x07; const uint8_t type = _msg.type; const uint8_t sender = _msg.sender; const uint8_t destination = _msg.destination; // Is message addressed to this node? if (destination == MY_NODE_ID) { // null terminate data _msg.data[msgLength] = 0u; // Check if sender requests an echo. if (_msg.command_echo_payload & 0x08) { memcpy(&_msgTmp, &_msg, sizeof(_msg)); // Copy message // Reply without echo flag (otherwise we would end up in an eternal loop) _msgTmp.command_echo_payload = _msgTmp.command_echo_payload & 0xE7; _msgTmp.command_echo_payload = _msgTmp.command_echo_payload | 0x10; _msgTmp.sender = MY_NODE_ID; _msgTmp.destination = sender; transportSend(&_msgTmp); } if(!(_msg.command_echo_payload & 0x10)) { // only process if not ECHO if (command == C_INTERNAL) { if (type == I_ID_RESPONSE) { return; // no further processing required } // general if (type == I_PING) { _msgTmp.last = MY_NODE_ID; _msgTmp.sender = MY_NODE_ID; _msgTmp.destination = sender; _msgTmp.command_echo_payload = (P_BYTE << 5) + C_INTERNAL; _msgTmp.type = I_PONG; _msgTmp.bValue = 1; transportSend(&_msgTmp); return; // no further processing required } if (type == I_PONG) { return; // no further processing required } if (_processInternalCoreMessage()) { return; // no further processing required } } } // Call incoming message callback if available receive(&_msg); } else if (destination == BROADCAST_ADDRESS) { if (command == C_INTERNAL) { if (type == I_DISCOVER_REQUEST) { HAL_Delay(MY_NODE_ID * 50); _msgTmp.last = MY_NODE_ID; _msgTmp.sender = MY_NODE_ID; _msgTmp.destination = sender; _msgTmp.command_echo_payload = (P_BYTE << 5) + C_INTERNAL; _msgTmp.type = I_DISCOVER_RESPONSE; _msgTmp.bValue = GATEWAY_ADDRESS; transportSend(&_msgTmp); return; // no further processing required } } if (command != C_INTERNAL) { receive(&_msg); } } } //Reset the state machine and release the data pointer void _serialReset() { _recPhase = 0; _recPos = 0; _recLen = 0; _recCommand = 0; _recCS = 0; _recCalcCS = 0; } // This is the main reception state machine. Progress through the states // is keyed on either special control characters, or counted number of bytes // received. If all the data is in the right format, and the calculated // checksum matches the received checksum, AND the destination station is // our station ID, then look for a registered command that matches the // command code. If all the above is true, execute the command's // function. _Bool _serialProcess() { unsigned char i; if (!_byte_received) { return 0; } _byte_received = 0; switch(_recPhase) { // Case 0 looks for the header. Bytes arrive in the serial interface and get // shifted through a header buffer. When the start and end characters in // the buffer match the SOH/STX pair, and the destination station ID matches // our ID, save the header information and progress to the next state. case 0: memcpy(&_header[0],&_header[1],5); _header[5] = _byte; if ((_header[0] == SOH) && (_header[5] == STX) && (_header[1] != _header[2])) { _recCalcCS = 0; _recStation = _header[1]; _recSender = _header[2]; _recCommand = _header[3]; _recLen = _header[4]; for (i=1; i<=4; i++) { _recCalcCS += _header[i]; } _recPhase = 1; _recPos = 0; //Avoid _data[] overflow if (_recLen >= MY_RS485_MAX_MESSAGE_LENGTH) { _serialReset(); break; } //Check if we should process this message //We reject the message if we are the sender //We reject if we are not the receiver and message is not a broadcast if ((_recSender == MY_NODE_ID) || (_recStation != MY_NODE_ID && _recStation != BROADCAST_ADDRESS)) { _serialReset(); break; } if (_recLen == 0) { _recPhase = 2; } } break; // Case 1 receives the data portion of the packet. Read in "_recLen" number // of bytes and store them in the _data array. case 1: _data[_recPos++] = _byte; _recCalcCS += _byte; if (_recPos == _recLen) { _recPhase = 2; } break; // After the data comes a single ETX character. Do we have it? If not, // reset the state machine to default and start looking for a new header. case 2: // Packet properly terminated? if (_byte == ETX) { _recPhase = 3; } else { _serialReset(); } break; // Next comes the checksum. We have already calculated it from the incoming // data, so just store the incoming checksum byte for later. case 3: _recCS = _byte; _recPhase = 4; break; // The final state - check the last character is EOT and that the checksum matches. // If that test passes, then look for a valid command callback to execute. // Execute it if found. case 4: if (_byte == EOT) { if (_recCS == _recCalcCS) { // First, check for system level commands. It is possible // to register your own callback as well for system level // commands which will be called after the system default // hook. switch (_recCommand) { case ICSC_SYS_PACK: _packet_from = _recSender; _packet_len = _recLen; _packet_received = 1; break; } } } //Clear the data _serialReset(); //Return true, we have processed one command return 1; break; } return 1; } _Bool transportSend(MyMessage* data) { const char *datap = (const char *)data; unsigned char i; unsigned char len; unsigned char cs = 0; // This is how many times to try and transmit before failing. unsigned char timeout = 10; // Let's start out by looking for a collision. If there has been anything seen in // the last millisecond, then wait for a random time and check again. while (_serialProcess()) { unsigned char del; del = rand() % 20; for (i = 0; i < del; i++) { HAL_Delay(1); _serialProcess(); } timeout--; if (timeout == 0) { // Failed to transmit!!! return 0; } } HAL_GPIO_WritePin(TEN_GPIO_Port, TEN_Pin, SET); // Start of header by writing multiple SOH i = SOH; for(uint8_t w=0; wdestination, 1, 20); cs += data->destination; i = MY_NODE_ID; HAL_UART_Transmit(_huart, (uint8_t*)&i, 1, 20); cs += MY_NODE_ID; i = ICSC_SYS_PACK; // Command code HAL_UART_Transmit(_huart, (uint8_t*)&i, 1, 20); cs += ICSC_SYS_PACK; len = (data->version_length >> 3) + V2_MYS_HEADER_SIZE; HAL_UART_Transmit(_huart, (uint8_t*)&len, 1, 20); cs += len; i = STX; HAL_UART_Transmit(_huart, (uint8_t*)&i, 1, 20); HAL_UART_Transmit(_huart, (uint8_t*)datap, len, len + 20); for(i=0; i