#include #include #include "MySensors.h" #include "nuvoton/Common.h" #include "nuvoton/N76E003.h" #include "nuvoton/functions.h" #include "nuvoton/SFR_Macro.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; unsigned char _byte; char _data[MY_RS485_MAX_MESSAGE_LENGTH]; uint8_t _packet_len; unsigned char _packet_from; void delay(uint32_t u32CNT) { clr_T0M; //T0M=0, Timer0 Clock = Fsys/12 TMOD |= 0x01; //Timer0 is 16-bit mode set_TR0; //Start Timer0 while (u32CNT != 0) { TL0 = LOBYTE(TIMER_DIV12_VALUE_1ms); //Find define in "Function_define.h" "TIMER VALUE" TH0 = HIBYTE(TIMER_DIV12_VALUE_1ms); while (TF0 != 1); //Check Timer0 Time-Out Flag clr_TF0; u32CNT --; } clr_TR0; //Stop Timer0 } _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 = ((uint8_t) P_ULONG32 << 5) + (uint8_t) 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) { ; } 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) { 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 (!RI) { return 0; } _byte = SBUF; RI = 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++) { delay(1); _serialProcess(); } timeout--; if (timeout == 0) { // Failed to transmit!!! return 0; } } rs485_out(); // Start of header by writing multiple SOH for(uint8_t w=0; wdestination; while (TI == 0); cs += data->destination; TI = 0; SBUF = MY_NODE_ID; while (TI == 0); cs += MY_NODE_ID; TI = 0; SBUF = ICSC_SYS_PACK; while (TI == 0); cs += ICSC_SYS_PACK; len = (data->version_length >> 3) + V2_MYS_HEADER_SIZE; TI = 0; SBUF = len; while (TI == 0); cs += len; TI = 0; SBUF = STX; while (TI == 0); for(i=0; i