Example #1
0
boolean _sendRoute(MyMessage &message) {
	// increment heartbeat counter
	_heartbeat++;
	#if defined(MY_CORE_ONLY)
		(void)message;
	#endif
	#if defined(MY_GATEWAY_FEATURE)
		if (message.destination == _nc.nodeId) {
			// This is a message sent from a sensor attached on the gateway node.
			// Pass it directly to the gateway transport layer.
			ledBlinkTx(1);
			return gatewayTransportSend(message);
		}
	#endif
	#if defined(MY_RADIO_FEATURE)
		return transportSendRoute(message);
	#else
		return false;
	#endif
}
bool gatewayTransportSend(MyMessage &message)
{
	bool ret = true;
	char *_ethernetMsg = protocolFormat(message);

    setIndication(INDICATION_GW_TX);

	_w5100_spi_en(true);
	#if defined(MY_CONTROLLER_IP_ADDRESS)
		#if defined(MY_USE_UDP)
			_ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT);
			_ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg));
			// returns 1 if the packet was sent successfully
			ret = _ethernetServer.endPacket();
		#else
			EthernetClient client;
			#if defined(MY_CONTROLLER_URL_ADDRESS)
	                	if (client.connected() || client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
	        	#else
	                	if (client.connected() || client.connect(_ethernetControllerIP, MY_PORT)) {
	        	#endif
	                	client.write(_ethernetMsg, strlen(_ethernetMsg));
	                }
	                else {
	                	// connecting to the server failed!
	                	ret = false;
	                }
		#endif
	#else
		// Send message to connected clients
		#if defined(MY_GATEWAY_ESP8266)
			for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++)
			{
				if (clients[i] && clients[i].connected())
				{
					clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg));
				}
			}
		#else
			_ethernetServer.write(_ethernetMsg);
		#endif
	#endif
	_w5100_spi_en(false);
	return ret;

}


#if defined(MY_GATEWAY_ESP8266)
	bool _readFromClient(uint8_t i) {
		while (clients[i].connected() && clients[i].available()) {
			char inChar = clients[i].read();
			if (inputString[i].idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
				// if newline then command is complete
				if (inChar == '\n' || inChar == '\r') {
					// Add string terminator and prepare for the next message
					inputString[i].string[inputString[i].idx] = 0;
					debug(PSTR("Client %d: %s\n"), i, inputString[i].string);
					inputString[i].idx = 0;
					if (protocolParse(_ethernetMsg, inputString[i].string)) {
						return true;
					}

				} else {
					// add it to the inputString:
					inputString[i].string[inputString[i].idx++] = inChar;
				}
			} else {
				// Incoming message too long. Throw away
				debug(PSTR("Client %d: Message too long\n"), i);
				inputString[i].idx = 0;
				// Finished with this client's message. Next loop() we'll see if there's more to read.
				break;
			}
		}
		return false;
	}
#else
	bool _readFromClient() {
		while (client.connected() && client.available()) {
			char inChar = client.read();
			if (inputString.idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
				// if newline then command is complete
				if (inChar == '\n' || inChar == '\r') {
					// Add string terminator and prepare for the next message
					inputString.string[inputString.idx] = 0;
					debug(PSTR("Eth: %s\n"), inputString.string);
					inputString.idx = 0;
					if (protocolParse(_ethernetMsg, inputString.string)) {
						return true;
					}

				} else {
					// add it to the inputString:
					inputString.string[inputString.idx++] = inChar;
				}
			} else {
				// Incoming message too long. Throw away
				debug(PSTR("Eth: Message too long\n"));
				inputString.idx = 0;
				// Finished with this client's message. Next loop() we'll see if there's more to read.
				break;
			}
		}
		return false;
	}
#endif


bool gatewayTransportAvailable()
{
	_w5100_spi_en(true);
	#if !defined(MY_IP_ADDRESS) && defined(MY_GATEWAY_W5100)
		// renew IP address using DHCP
		gatewayTransportRenewIP();
	#endif

	#ifdef MY_USE_UDP

		int packet_size = _ethernetServer.parsePacket();

		if (packet_size) {
			//debug(PSTR("UDP packet available. Size:%d\n"), packet_size);
            setIndication(INDICATION_GW_RX);
			#if defined(MY_GATEWAY_ESP8266)
				_ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
				inputString[0].string[packet_size] = 0;
				debug(PSTR("UDP packet received: %s\n"), inputString[0].string);
				return protocolParse(_ethernetMsg, inputString[0].string);
			#else
				_ethernetServer.read(inputString.string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
				inputString.string[packet_size] = 0;
				debug(PSTR("UDP packet received: %s\n"), inputString.string);
				_w5100_spi_en(false);
				return protocolParse(_ethernetMsg, inputString.string);
			#endif
		}
	#else
		#if defined(MY_GATEWAY_ESP8266)
			// ESP8266: Go over list of clients and stop any that are no longer connected.
			// If the server has a new client connection it will be assigned to a free slot.
			bool allSlotsOccupied = true;
			for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
				if (!clients[i].connected()) {
					if (clientsConnected[i]) {
						debug(PSTR("Client %d disconnected\n"), i);
						clients[i].stop();
					}
					//check if there are any new clients
					if (_ethernetServer.hasClient()) {
						clients[i] = _ethernetServer.available();
						inputString[i].idx = 0;
						debug(PSTR("Client %d connected\n"), i);
						gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
						if (presentation)
							presentation();
					}
				}
				bool connected = clients[i].connected();
				clientsConnected[i] = connected;
				allSlotsOccupied &= connected;
			}
			if (allSlotsOccupied && _ethernetServer.hasClient()) {
				//no free/disconnected spot so reject
				debug(PSTR("No free slot available\n"));
				EthernetClient c = _ethernetServer.available();
				c.stop();
			}
				// Loop over clients connect and read available data
			for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
				if (_readFromClient(i)) {
                    setIndication(INDICATION_GW_RX);
					_w5100_spi_en(false);
					return true;
				}
			}
		#else
			// W5100/ENC module does not have hasClient-method. We can only serve one client at the time.
			EthernetClient newclient = _ethernetServer.available();
			// if a new client connects make sure to dispose any previous existing sockets
			if (newclient) {
				if (client != newclient) {
					client.stop();
					client = newclient;
					debug(PSTR("Eth: connect\n"));
					_w5100_spi_en(false);
					gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
					_w5100_spi_en(true);
					if (presentation)
						presentation();
				}
			}
			if (client) {
				if (!client.connected()) {
					debug(PSTR("Eth: disconnect\n"));
					client.stop();
				} else {
					if (_readFromClient()) {
                        setIndication(INDICATION_GW_RX);
						_w5100_spi_en(false);
						return true;
					}
				}
			}
		#endif
	#endif
	_w5100_spi_en(false);
	return false;
}
Example #3
0
void transportProcessMessage(void)
{
	// Manage signing timeout
	(void)signerCheckTimer();
	// receive message
	setIndication(INDICATION_RX);
	uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
	// get message length and limit size

	const uint8_t msgLength = min(mGetLength(_msg), (uint8_t)MAX_PAYLOAD);
	// calculate expected length
	const uint8_t expectedMessageLength = HEADER_SIZE + (mGetSigned(_msg) ? MAX_PAYLOAD : msgLength);
#if defined(MY_RF24_ENABLE_ENCRYPTION)
	// payload length = a multiple of blocksize length for decrypted messages, i.e. cannot be used for payload length check
	payloadLength = expectedMessageLength;
#endif
	const uint8_t command = mGetCommand(_msg);
	const uint8_t type = _msg.type;
	const uint8_t sender = _msg.sender;
	const uint8_t last = _msg.last;
	const uint8_t destination = _msg.destination;

	TRANSPORT_DEBUG(PSTR("TSF:MSG:READ,%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
	                sender, last, destination, _msg.sensor, command, type, mGetPayloadType(_msg), msgLength,
	                mGetSigned(_msg), _msg.getString(_convBuf));

	// Reject payloads with incorrect length
	if (payloadLength != expectedMessageLength) {
		setIndication(INDICATION_ERR_LENGTH);
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:LEN,%d!=%d\n"), payloadLength,
		                expectedMessageLength); // invalid payload length
		return;
	}

	// Reject messages with incorrect protocol version
	if (mGetVersion(_msg) != PROTOCOL_VERSION) {
		setIndication(INDICATION_ERR_VERSION);
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%d=%d\n"), mGetVersion(_msg),
		                PROTOCOL_VERSION);	// protocol version mismatch
		return;
	}

	// Reject messages that do not pass verification
	if (!signerVerifyMsg(_msg)) {
		setIndication(INDICATION_ERR_SIGN);
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN VERIFY FAIL\n"));
		return;
	}

	// update routing table if msg not from parent
#if defined(MY_REPEATER_FEATURE)
#if !defined(MY_GATEWAY_FEATURE)
	if (last != _transportConfig.parentNodeId) {
#else
	// GW doesn't have parent
	{
#endif
		// Message is from one of the child nodes and not sent from this node. Add it to routing table.
		if (sender != _transportConfig.nodeId)
		{
			transportSetRoute(sender, last);
		}
	}
#endif // MY_REPEATER_FEATURE

	// set message received flag
	_transportSM.msgReceived = true;

	// Is message addressed to this node?
	if (destination == _transportConfig.nodeId) {
		// prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes)
		mSetLength(_msg, min(mGetLength(_msg),(uint8_t)MAX_PAYLOAD));
		// null terminate data
		_msg.data[msgLength] = 0u;
		// Check if sender requests an ack back.
		if (mGetRequestAck(_msg)) {
			TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n"));	// ACK requested
			_msgTmp = _msg;	// Copy message
			mSetRequestAck(_msgTmp,
			               false); // Reply without ack flag (otherwise we would end up in an eternal loop)
			mSetAck(_msgTmp, true); // set ACK flag
			_msgTmp.sender = _transportConfig.nodeId;
			_msgTmp.destination = sender;
			// send ACK, use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply
			(void)transportSendRoute(_msgTmp);
		}
		if(!mGetAck(_msg)) {
			// only process if not ACK
			if (command == C_INTERNAL) {
				// Process signing related internal messages
				if (signerProcessInternal(_msg)) {
					return; // Signer processing indicated no further action needed
				}
#if !defined(MY_GATEWAY_FEATURE)
				if (type == I_ID_RESPONSE) {
#if (MY_NODE_ID == AUTO)
					// only active if node ID dynamic
					(void)transportAssignNodeID(_msg.getByte());
#endif
					return; // no further processing required
				}
				if (type == I_FIND_PARENT_RESPONSE) {
#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_PARENT_NODE_IS_STATIC)
					if (_transportSM.findingParentNode) {	// only process if find parent active
						// Reply to a I_FIND_PARENT_REQUEST message. Check if the distance is shorter than we already have.
						uint8_t distance = _msg.getByte();
						if (isValidDistance(distance)) {
							distance++;	// Distance to gateway is one more for us w.r.t. parent
							// update settings if distance shorter or preferred parent found
							if (((isValidDistance(distance) && distance < _transportConfig.distanceGW) || (!_autoFindParent &&
							        sender == (uint8_t)MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) {
								// Found a neighbor closer to GW than previously found
								if (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID) {
									_transportSM.preferredParentFound = true;
									TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF\n"));	// find parent, preferred parent found
								}
								_transportConfig.distanceGW = distance;
								_transportConfig.parentNodeId = sender;
								TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR OK,ID=%d,D=%d\n"), _transportConfig.parentNodeId,
								                _transportConfig.distanceGW);
							}
						}
					} else {
						TRANSPORT_DEBUG(PSTR("!TSF:MSG:FPAR INACTIVE\n"));	// find parent response received, but inactive
					}
					return;
#endif
				}
#endif
				// general
				if (type == I_PING) {
					TRANSPORT_DEBUG(PSTR("TSF:MSG:PINGED,ID=%d,HP=%d\n"), sender, _msg.getByte()); // node pinged
#if defined(MY_GATEWAY_FEATURE) && (F_CPU>16000000)
					// delay for fast GW and slow nodes
					delay(5);
#endif
					(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
					                                  I_PONG).set((uint8_t)1));
					return; // no further processing required
				}
				if (type == I_PONG) {
					if (_transportSM.pingActive) {
						_transportSM.pingActive = false;
						_transportSM.pingResponse = _msg.getByte();
						TRANSPORT_DEBUG(PSTR("TSF:MSG:PONG RECV,HP=%d\n"), _transportSM.pingResponse); // pong received
					} else {
						TRANSPORT_DEBUG(PSTR("!TSF:MSG:PONG RECV,INACTIVE\n")); // pong received, but !pingActive
					}
					return; // no further processing required
				}
				if (_processInternalMessages()) {
					return; // no further processing required
				}
			} else if (command == C_STREAM) {
#if defined(MY_OTA_FIRMWARE_FEATURE)
				if(firmwareOTAUpdateProcess()) {
					return; // OTA FW update processing indicated no further action needed
				}
#endif
			}
		} else {
			TRANSPORT_DEBUG(
			    PSTR("TSF:MSG:ACK\n")); // received message is ACK, no internal processing, handover to msg callback
		}
#if defined(MY_GATEWAY_FEATURE)
		// Hand over message to controller
		(void)gatewayTransportSend(_msg);
#endif
		// Call incoming message callback if available
		if (receive) {
			receive(_msg);
		}
	} else if (destination == BROADCAST_ADDRESS) {
		TRANSPORT_DEBUG(PSTR("TSF:MSG:BC\n"));	// broadcast msg
		if (command == C_INTERNAL) {
			if (isTransportReady()) {
				// only reply if node is fully operational
				if (type == I_FIND_PARENT_REQUEST) {
#if defined(MY_REPEATER_FEATURE)
					if (sender != _transportConfig.parentNodeId) {	// no circular reference
						TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR REQ,ID=%d\n"), sender);	// FPAR: find parent request
						// check if uplink functional - node can only be parent node if link to GW functional
						// this also prevents circular references in case GW ooo
						if (transportCheckUplink()) {
							_transportSM.lastUplinkCheck = hwMillis();
							TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok
							// random delay minimizes collisions
							delay(hwMillis() & 0x3ff);
							(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
							                                  I_FIND_PARENT_RESPONSE).set(_transportConfig.distanceGW));
						} else {
							TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request
						}
					}
#endif
					return; // no further processing required, do not forward
				}
			} // isTransportReady
			if (type == I_FIND_PARENT_RESPONSE) {
				return;	// no further processing required, do not forward
			}
#if !defined(MY_GATEWAY_FEATURE)
			if (type == I_DISCOVER_REQUEST) {
				if (last == _transportConfig.parentNodeId) {
					// random wait to minimize collisions
					delay(hwMillis() & 0x3ff);
					(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
					                                  I_DISCOVER_RESPONSE).set(_transportConfig.parentNodeId));
					// no return here (for fwd if repeater)
				}
			}
#endif
		}
		// controlled BC relay
#if defined(MY_REPEATER_FEATURE)
		// controlled BC repeating: forward only if message received from parent and sender not self to prevent circular fwds
		if(last == _transportConfig.parentNodeId && sender != _transportConfig.nodeId &&
		        isTransportReady()) {
			TRANSPORT_DEBUG(PSTR("TSF:MSG:FWD BC MSG\n")); // controlled broadcast msg forwarding
			(void)transportRouteMessage(_msg);
		}
#endif

		// Callback for BC, only for non-internal messages
		if (command != C_INTERNAL) {
#if !defined(MY_GATEWAY_FEATURE)
			// only proceed if message received from parent
			if (last != _transportConfig.parentNodeId) {
				return;
			}
#endif
#if defined(MY_GATEWAY_FEATURE)
			// Hand over message to controller
			(void)gatewayTransportSend(_msg);
#endif
			if (receive) {
				receive(_msg);
			}
		}

	} else {
		// msg not to us and not BC, relay msg
#if defined(MY_REPEATER_FEATURE)
		if (isTransportReady()) {
			TRANSPORT_DEBUG(PSTR("TSF:MSG:REL MSG\n"));	// relay msg
			if (command == C_INTERNAL) {
				if (type == I_PING || type == I_PONG) {
					uint8_t hopsCnt = _msg.getByte();
					if (hopsCnt != MAX_HOPS) {
						TRANSPORT_DEBUG(PSTR("TSF:MSG:REL PxNG,HP=%d\n"), hopsCnt);
						hopsCnt++;
						_msg.set(hopsCnt);
					}
				}
			}
			// Relay this message to another node
			(void)transportRouteMessage(_msg);
		}
#else
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NREP\n"));	// message relaying request, but not a repeater
#endif
	}
}

void transportInvokeSanityCheck(void)
{
	if (!transportSanityCheck()) {
		TRANSPORT_DEBUG(PSTR("!TSF:SAN:FAIL\n"));	// sanity check fail
		transportSwitchSM(stFailure);
	} else {
		TRANSPORT_DEBUG(PSTR("TSF:SAN:OK\n"));		// sanity check ok
	}
}

void transportProcessFIFO(void)
{
	if (!_transportSM.transportActive) {
		// transport not active, no further processing required
		return;
	}

#if defined(MY_TRANSPORT_SANITY_CHECK)
	if (hwMillis() - _lastSanityCheck > MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS) {
		_lastSanityCheck = hwMillis();
		transportInvokeSanityCheck();
	}
#endif

	uint8_t _processedMessages = MAX_SUBSEQ_MSGS;
	// process all msgs in FIFO or counter exit
	while (transportAvailable() && _processedMessages--) {
		transportProcessMessage();
	}
#if defined(MY_OTA_FIRMWARE_FEATURE)
	if (isTransportReady()) {
		// only process if transport ok
		firmwareOTAUpdateRequest();
	}
#endif
}

bool transportSendWrite(const uint8_t to, MyMessage &message)
{
	message.last = _transportConfig.nodeId; // Update last
	// sign message if required
	if (!signerSignMsg(message)) {
		TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n"));
		setIndication(INDICATION_ERR_SIGN);
		return false;
	}

	// msg length changes if signed
	const uint8_t totalMsgLength = HEADER_SIZE + ( mGetSigned(message) ? MAX_PAYLOAD : mGetLength(
	                                   message) );

	// send
	setIndication(INDICATION_TX);
	bool result = transportSend(to, &message, min((uint8_t)MAX_MESSAGE_LENGTH, totalMsgLength));
	// broadcasting (workaround counterfeits)
	result |= (to == BROADCAST_ADDRESS);

	TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%d-%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s\n"),
	                (result ? "" : "!"), message.sender, message.last, to, message.destination, message.sensor,
	                mGetCommand(message), message.type,
	                mGetPayloadType(message), mGetLength(message), mGetSigned(message),
	                _transportSM.failedUplinkTransmissions, (result ? "OK" : "NACK"), message.getString(_convBuf));

	return result;
}
bool gatewayTransportInit() {
	gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
	return true;
}