예제 #1
0
// stParentUpdate
void stParentUpdate(void)
{
#if defined(MY_PARENT_NODE_IS_STATIC)
	// skipping find parent
	setIndication(INDICATION_GOT_PARENT);
	transportSwitchSM(stID);
#else
	if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS || _transportSM.preferredParentFound) {
		// timeout or preferred parent found
		if (_transportConfig.parentNodeId != AUTO) {
			// parent assigned
			TRANSPORT_DEBUG(PSTR("TSM:FPAR:OK\n"));	// find parent ok
			_transportSM.findingParentNode = false;
			setIndication(INDICATION_GOT_PARENT);
			// go to next state
			transportSwitchSM(stID);
		} else {
			// timeout w/o reply or valid parent
			if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) {
				// retries left
				TRANSPORT_DEBUG(PSTR("!TSM:FPAR:NO REPLY\n"));		// find parent, no reply
				// reenter state
				transportSwitchSM(stParent);
			} else {
				// no retries left, finding parent failed
				TRANSPORT_DEBUG(PSTR("!TSM:FPAR:FAIL\n"));
				setIndication(INDICATION_ERR_FIND_PARENT);
				transportSwitchSM(stFailure);
			}
		}
	}
#endif
}
예제 #2
0
bool transportRouteMessage(MyMessage &message)
{
	const uint8_t destination = message.destination;
	uint8_t route = _transportConfig.parentNodeId;	// by default, all traffic is routed via parent node

	if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) {
		TRANSPORT_DEBUG(PSTR("!TSF:RTE:FPAR ACTIVE\n")); // find parent active, message not sent
		// request to send a non-BC message while finding parent active, abort
		return false;
	}

	if (destination == GATEWAY_ADDRESS) {
		route = _transportConfig.parentNodeId;		// message to GW always routes via parent
	} else if (destination == BROADCAST_ADDRESS) {
		route = BROADCAST_ADDRESS;		// message to BC does not require routing
	} else {
#if defined(MY_REPEATER_FEATURE)
		// destination not GW & not BC, get route
		route = transportGetRoute(destination);
		if (route == AUTO) {
			TRANSPORT_DEBUG(PSTR("!TSF:RTE:%d UNKNOWN\n"), destination);	// route unknown
#if !defined(MY_GATEWAY_FEATURE)
			if (message.last != _transportConfig.parentNodeId) {
				// message not from parent, i.e. child node - route it to parent
				route = _transportConfig.parentNodeId;
			} else {
				// route unknown and msg received from parent, send it to destination assuming in rx radius
				route = destination;
			}
#else
			// if GW, all unknown destinations are directly addressed
			route = destination;
#endif
		}
#else
		route = _transportConfig.parentNodeId;	// not a repeater, all traffic routed via parent
#endif
	}
	// send message
	const bool result = transportSendWrite(route, message);
#if !defined(MY_GATEWAY_FEATURE)
	// update counter
	if (route == _transportConfig.parentNodeId) {
		if (!result) {
			setIndication(INDICATION_ERR_TX);
			_transportSM.failedUplinkTransmissions++;
		} else {
			_transportSM.failedUplinkTransmissions = 0u;
		}
	}
#else
	if(!result) {
		setIndication(INDICATION_ERR_TX);
	}
#endif

	return result;
}
예제 #3
0
void stUplinkUpdate(void)
{
#if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED)
	if (_transportSM.pingResponse != INVALID_HOPS) {
		_transportSM.lastUplinkCheck = hwMillis();
		// uplink ok, i.e. GW replied
		TRANSPORT_DEBUG(PSTR("TSM:UPL:OK\n"));	// uplink ok
		if (_transportSM.pingResponse != _transportConfig.distanceGW) {
			TRANSPORT_DEBUG(PSTR("TSM:UPL:DGWC,O=%d,N=%d\n"), _transportConfig.distanceGW,
			                _transportSM.pingResponse);	// distance to GW changed
			_transportConfig.distanceGW = _transportSM.pingResponse;
		}
		transportSwitchSM(stReady);		// proceed to next state
	} else if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS) {
		// timeout
		if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) {
			// retries left: reenter state
			transportSwitchSM(stUplink);
		} else {
			// no retries left
			TRANSPORT_DEBUG(PSTR("!TSM:UPL:FAIL\n"));	// uplink check failed
			_transportSM.pingActive = false;
			setIndication(INDICATION_ERR_CHECK_UPLINK);
			transportSwitchSM(stParent);	// go back to stParent
		}
	}
#else
	TRANSPORT_DEBUG(PSTR("TSM:UPL:DISABLED\n"));	// uplink check disabled
	transportSwitchSM(stReady);
#endif
}
예제 #4
0
int setValueIfLess(int x, int y, int val) {
	int existing = getIndication(x, y);
	if (val < existing) {
		setIndication(x, y, val);
		return 1;
	}
	return 0;
}
예제 #5
0
void stUplinkTransition(void)
{
#if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED)
	TRANSPORT_DEBUG(PSTR("TSM:UPL\n"));
	setIndication(INDICATION_CHECK_UPLINK);
	_transportSM.pingResponse = INVALID_HOPS;
	_transportSM.pingActive = true;
	(void)transportRouteMessage(build(_msgTmp,GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
	                                  I_PING).set((uint8_t)0x01));
#endif
}
예제 #6
0
// stID: verify and request ID if necessary
void stIDTransition(void)
{
	TRANSPORT_DEBUG(PSTR("TSM:ID\n"));	// verify/request node ID
	if (_transportConfig.nodeId == AUTO) {
		// send ID request
		setIndication(INDICATION_REQ_NODEID);
		TRANSPORT_DEBUG(PSTR("TSM:ID:REQ\n"));	// request node ID
		(void)transportRouteMessage(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
		                                  I_ID_REQUEST).set(""));
	}
}
예제 #7
0
void stIDUpdate(void)
{
	if (_transportConfig.nodeId != AUTO) {
		// current node ID is valid
		TRANSPORT_DEBUG(PSTR("TSM:ID:OK\n"));
		setIndication(INDICATION_GOT_NODEID);
		// proceed to next state
		transportSwitchSM(stUplink);
	} else if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS) {
		// timeout
		if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) {
			// retries left: reenter state
			transportSwitchSM(stID);
		} else {
			// no retries left
			TRANSPORT_DEBUG(PSTR("!TSM:ID:FAIL\n"));
			setIndication(INDICATION_ERR_GET_NODEID);
			transportSwitchSM(stFailure);
		}
	}
}
예제 #8
0
// stFailure: entered upon HW init failure or max retries exceeded
void stFailureTransition(void)
{
	if (_transportSM.failureCounter < MY_TRANSPORT_MAX_TSM_FAILURES) {
		_transportSM.failureCounter++;		// increment consecutive TSM failure counter
	}
	TRANSPORT_DEBUG(PSTR("TSM:FAIL:CNT=%d\n"),_transportSM.failureCounter);
	_transportSM.uplinkOk = false;			// uplink nok
	_transportSM.transportActive = false;	// transport inactive
	setIndication(INDICATION_ERR_INIT_TRANSPORT);
#if defined(MY_SENSOR_NETWORK)
	TRANSPORT_DEBUG(PSTR("TSM:FAIL:PDT\n"));	// power down transport, no need until re-init
	transportPowerDown();
#endif
}
예제 #9
0
bool transportAssignNodeID(const uint8_t newNodeId)
{
	// verify if ID valid
	if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) {
		_transportConfig.nodeId = newNodeId;
		transportSetAddress(newNodeId);
		// Write ID to EEPROM
		hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId);
		TRANSPORT_DEBUG(PSTR("TSF:SID:OK,ID=%d\n"),newNodeId);	// Node ID assigned
		return true;
	} else {
		TRANSPORT_DEBUG(PSTR("!TSF:SID:FAIL,ID=%d\n"),newNodeId);	// ID is invalid, cannot assign ID
		setIndication(INDICATION_ERR_NET_FULL);
		_transportConfig.nodeId = AUTO;
		return false;
	}
}
예제 #10
0
// stParent: find parent
void stParentTransition(void)
{
	TRANSPORT_DEBUG(PSTR("TSM:FPAR\n"));	// find parent
	setIndication(INDICATION_FIND_PARENT);
	_transportSM.uplinkOk = false;
	_transportSM.preferredParentFound = false;
#if defined(MY_PARENT_NODE_IS_STATIC)
	TRANSPORT_DEBUG(PSTR("TSM:FPAR:STATP=%d\n"), (uint8_t)MY_PARENT_NODE_ID);	// static parent
	_transportSM.findingParentNode = false;
	_transportConfig.distanceGW = 1u;	// assumption, CHKUPL:GWDC will update this variable
	_transportConfig.parentNodeId = (uint8_t)MY_PARENT_NODE_ID;
	// save parent ID to eeprom (for bootloader)
	hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, (uint8_t)MY_PARENT_NODE_ID);
#else
	_transportSM.findingParentNode = true;
	_transportConfig.distanceGW = DISTANCE_INVALID;	// Set distance to max and invalidate parent node ID
	_transportConfig.parentNodeId = AUTO;
	// Broadcast find parent request
	(void)transportRouteMessage(build(_msgTmp, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
	                                  I_FIND_PARENT_REQUEST).set(""));
#endif
}
예제 #11
0
void stInitUpdate(void)
{
	// initialise radio
	if (!transportInit()) {
		TRANSPORT_DEBUG(PSTR("!TSM:INIT:TSP FAIL\n"));
		setIndication(INDICATION_ERR_INIT_TRANSPORT);
		transportSwitchSM(stFailure);
	} else {
		TRANSPORT_DEBUG(PSTR("TSM:INIT:TSP OK\n"));
		_transportSM.transportActive = true;
#if defined(MY_GATEWAY_FEATURE)
		// Set configuration for gateway
		TRANSPORT_DEBUG(PSTR("TSM:INIT:GW MODE\n"));
		_transportConfig.parentNodeId = GATEWAY_ADDRESS;
		_transportConfig.distanceGW = 0u;
		_transportConfig.nodeId = GATEWAY_ADDRESS;
		transportSetAddress(GATEWAY_ADDRESS);
		// GW mode: skip FPAR,ID,UPL states
		transportSwitchSM(stReady);
#else
		if (MY_NODE_ID != AUTO) {
			TRANSPORT_DEBUG(PSTR("TSM:INIT:STATID=%d\n"),(uint8_t)MY_NODE_ID);
			// Set static ID
			_transportConfig.nodeId = (uint8_t)MY_NODE_ID;
			// Save static ID to eeprom (for bootloader)
			hwWriteConfig(EEPROM_NODE_ID_ADDRESS, (uint8_t)MY_NODE_ID);
		}
		// assign ID if set
		if (_transportConfig.nodeId == AUTO || transportAssignNodeID(_transportConfig.nodeId)) {
			// if node ID valid (>0 and <255), proceed to next state
			transportSwitchSM(stParent);
		} else {
			// ID invalid (0 or 255)
			transportSwitchSM(stFailure);
		}
#endif
	}
}
예제 #12
0
void evaluatePaths() {
	// looking for labyrinth exit

	int x,y;
	for (x = 0; x < getWorldWidth(); x++){
		for (y = 0; y < getWorldHeight(); y++){
			if (hasBaggle(x,y)){
				setIndication(x, y, 0);
			}
		}
	}

	int changed = 1;
	while (changed) {
		changed = 0;
		for (x = 0; x < getWorldWidth(); x++) {
			for (y = 0; y < getWorldHeight(); y++) {
				int indication = getIndication(x, y);
				if (indication != 9999) {
					if (! hasBottomWall(x,y))
						changed |= setValueIfLess(x, (y + 1) % getWorldHeight(), indication + 1);

					if (! hasRightWall(x,y))
						changed |= setValueIfLess((x + 1) % getWorldWidth(), y, indication + 1);

					if (! hasTopWall(x,y))
						changed |= setValueIfLess(x, (y+getWorldHeight() - 1) % getWorldHeight(), indication + 1);

					if (! hasLeftWall(x,y))
						changed |= setValueIfLess((x +getWorldWidth() - 1) % getWorldWidth(), y, indication + 1);

				}
			}
		}
	}
}
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;
}
예제 #14
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;
}
예제 #15
0
bool gatewayTransportSend(MyMessage &message) {
    setIndication(INDICATION_GW_TX);
	MY_SERIALDEVICE.print(protocolFormat(message));
	// Serial print is always successful
	return true;
}