Smart-Home am Beispiel der Präsenzerkennung im Raum Projektarbeit Lennart Heimbs, Johannes Krug, Sebastian Dohle und Kevin Holzschuh bei Prof. Oliver Hofmann SS2019
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MyTransport.cpp 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. /*
  2. * The MySensors Arduino library handles the wireless radio link and protocol
  3. * between your home built sensors/actuators and HA controller of choice.
  4. * The sensors forms a self healing radio network with optional repeaters. Each
  5. * repeater and gateway builds a routing tables in EEPROM which keeps track of the
  6. * network topology allowing messages to be routed to nodes.
  7. *
  8. * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
  9. * Copyright (C) 2013-2018 Sensnology AB
  10. * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
  11. *
  12. * Documentation: http://www.mysensors.org
  13. * Support Forum: http://forum.mysensors.org
  14. *
  15. * This program is free software; you can redistribute it and/or
  16. * modify it under the terms of the GNU General Public License
  17. * version 2 as published by the Free Software Foundation.
  18. */
  19. #include "MyTransport.h"
  20. // debug
  21. #if defined(MY_DEBUG_VERBOSE_TRANSPORT)
  22. #define TRANSPORT_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< debug
  23. extern char _convBuf[MAX_PAYLOAD * 2 + 1];
  24. #else
  25. #define TRANSPORT_DEBUG(x,...) //!< debug NULL
  26. #endif
  27. // SM: transitions and update states
  28. static transportState_t stInit = { stInitTransition, stInitUpdate };
  29. static transportState_t stParent = { stParentTransition, stParentUpdate };
  30. static transportState_t stID = { stIDTransition, stIDUpdate };
  31. static transportState_t stUplink = { stUplinkTransition, stUplinkUpdate };
  32. static transportState_t stReady = { stReadyTransition, stReadyUpdate };
  33. static transportState_t stFailure = { stFailureTransition, stFailureUpdate };
  34. // transport SM variables
  35. static transportSM_t _transportSM;
  36. // transport configuration
  37. static transportConfig_t _transportConfig;
  38. // callback transportOk
  39. transportCallback_t _transportReady_cb = NULL;
  40. // enhanced ID assignment
  41. #if !defined(MY_GATEWAY_FEATURE) && (MY_NODE_ID == AUTO)
  42. static uint8_t _transportToken = AUTO;
  43. #endif
  44. // global variables
  45. extern MyMessage _msg; // incoming message
  46. extern MyMessage _msgTmp; // outgoing message
  47. #if defined(MY_RAM_ROUTING_TABLE_ENABLED)
  48. static routingTable_t _transportRoutingTable; //!< routing table
  49. static uint32_t _lastRoutingTableSave; //!< last routing table dump
  50. #endif
  51. // regular sanity check, activated by default on GW and repeater nodes
  52. #if defined(MY_TRANSPORT_SANITY_CHECK)
  53. static uint32_t _lastSanityCheck; //!< last sanity check
  54. #endif
  55. // regular network discovery, sends I_DISCOVER_REQUESTS to update routing table
  56. // sufficient to have GW triggering requests to also update repeater nodes
  57. #if defined(MY_GATEWAY_FEATURE)
  58. static uint32_t _lastNetworkDiscovery; //!< last network discovery
  59. #endif
  60. // stInit: initialise transport HW
  61. void stInitTransition(void)
  62. {
  63. TRANSPORT_DEBUG(PSTR("TSM:INIT\n"));
  64. // initialise status variables
  65. _transportSM.pingActive = false;
  66. _transportSM.transportActive = false;
  67. _transportSM.lastUplinkCheck = 0;
  68. #if defined(MY_TRANSPORT_SANITY_CHECK)
  69. _lastSanityCheck = hwMillis();
  70. #endif
  71. #if defined(MY_GATEWAY_FEATURE)
  72. _lastNetworkDiscovery = 0;
  73. #endif
  74. #if defined(MY_RAM_ROUTING_TABLE_ENABLED)
  75. _lastRoutingTableSave = hwMillis();
  76. #endif
  77. // Read node settings (ID, parent ID, GW distance) from EEPROM
  78. hwReadConfigBlock((void *)&_transportConfig, (void *)EEPROM_NODE_ID_ADDRESS,
  79. sizeof(transportConfig_t));
  80. }
  81. void stInitUpdate(void)
  82. {
  83. // initialise radio
  84. if (!transportInit()) {
  85. TRANSPORT_DEBUG(PSTR("!TSM:INIT:TSP FAIL\n"));
  86. setIndication(INDICATION_ERR_INIT_TRANSPORT);
  87. transportSwitchSM(stFailure);
  88. } else {
  89. TRANSPORT_DEBUG(PSTR("TSM:INIT:TSP OK\n"));
  90. _transportSM.transportActive = true;
  91. #if defined (MY_PASSIVE_NODE)
  92. _transportConfig.passiveMode = true;
  93. TRANSPORT_DEBUG(PSTR("TSM:INIT:TSP PSM\n")); // transport passive mode
  94. #else
  95. _transportConfig.passiveMode = false;
  96. #endif
  97. #if defined(MY_GATEWAY_FEATURE)
  98. // Set configuration for gateway
  99. TRANSPORT_DEBUG(PSTR("TSM:INIT:GW MODE\n"));
  100. _transportConfig.parentNodeId = GATEWAY_ADDRESS;
  101. _transportConfig.distanceGW = 0u;
  102. _transportConfig.nodeId = GATEWAY_ADDRESS;
  103. transportSetAddress(GATEWAY_ADDRESS);
  104. // GW mode: skip FPAR,ID,UPL states
  105. transportSwitchSM(stReady);
  106. #else
  107. if (MY_NODE_ID != AUTO) {
  108. TRANSPORT_DEBUG(PSTR("TSM:INIT:STATID=%" PRIu8 "\n"),(uint8_t)MY_NODE_ID);
  109. // Set static ID
  110. _transportConfig.nodeId = (uint8_t)MY_NODE_ID;
  111. // Save static ID to eeprom (for bootloader)
  112. hwWriteConfig(EEPROM_NODE_ID_ADDRESS, (uint8_t)MY_NODE_ID);
  113. }
  114. // assign ID if set
  115. if (_transportConfig.nodeId == AUTO || transportAssignNodeID(_transportConfig.nodeId)) {
  116. // if node ID valid (>0 and <255), proceed to next state
  117. transportSwitchSM(stParent);
  118. } else {
  119. // ID invalid (0 or 255)
  120. transportSwitchSM(stFailure);
  121. }
  122. #endif
  123. }
  124. }
  125. // stParent: find parent
  126. void stParentTransition(void)
  127. {
  128. TRANSPORT_DEBUG(PSTR("TSM:FPAR\n")); // find parent
  129. setIndication(INDICATION_FIND_PARENT);
  130. _transportSM.uplinkOk = false;
  131. _transportSM.preferredParentFound = false;
  132. #if defined(MY_PARENT_NODE_IS_STATIC) || defined(MY_PASSIVE_NODE)
  133. TRANSPORT_DEBUG(PSTR("TSM:FPAR:STATP=%" PRIu8 "\n"), (uint8_t)MY_PARENT_NODE_ID); // static parent
  134. _transportSM.findingParentNode = false;
  135. _transportConfig.distanceGW = 1u; // assumption, CHKUPL:GWDC will update this variable
  136. _transportConfig.parentNodeId = (uint8_t)MY_PARENT_NODE_ID;
  137. // save parent ID to eeprom (for bootloader)
  138. hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, (uint8_t)MY_PARENT_NODE_ID);
  139. #else
  140. _transportSM.findingParentNode = true;
  141. _transportConfig.distanceGW = DISTANCE_INVALID; // Set distance to max and invalidate parent node ID
  142. _transportConfig.parentNodeId = AUTO;
  143. // Broadcast find parent request
  144. (void)transportRouteMessage(build(_msgTmp, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
  145. I_FIND_PARENT_REQUEST).set(""));
  146. #endif
  147. }
  148. // stParentUpdate
  149. void stParentUpdate(void)
  150. {
  151. #if defined(MY_PARENT_NODE_IS_STATIC) || defined(MY_PASSIVE_NODE)
  152. // skipping find parent
  153. setIndication(INDICATION_GOT_PARENT);
  154. transportSwitchSM(stID);
  155. #else
  156. if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS || _transportSM.preferredParentFound) {
  157. // timeout or preferred parent found
  158. if (_transportConfig.parentNodeId != AUTO) {
  159. // parent assigned
  160. TRANSPORT_DEBUG(PSTR("TSM:FPAR:OK\n")); // find parent ok
  161. _transportSM.findingParentNode = false;
  162. setIndication(INDICATION_GOT_PARENT);
  163. // go to next state
  164. transportSwitchSM(stID);
  165. } else {
  166. // timeout w/o reply or valid parent
  167. if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) {
  168. // retries left
  169. TRANSPORT_DEBUG(PSTR("!TSM:FPAR:NO REPLY\n")); // find parent, no reply
  170. // reenter state
  171. transportSwitchSM(stParent);
  172. } else {
  173. // no retries left, finding parent failed
  174. TRANSPORT_DEBUG(PSTR("!TSM:FPAR:FAIL\n"));
  175. setIndication(INDICATION_ERR_FIND_PARENT);
  176. transportSwitchSM(stFailure);
  177. }
  178. }
  179. }
  180. #endif
  181. }
  182. // stID: verify and request ID if necessary
  183. void stIDTransition(void)
  184. {
  185. TRANSPORT_DEBUG(PSTR("TSM:ID\n")); // verify/request node ID
  186. if (_transportConfig.nodeId == AUTO) {
  187. // send ID request
  188. setIndication(INDICATION_REQ_NODEID);
  189. #if !defined(MY_GATEWAY_FEATURE) && (MY_NODE_ID == AUTO)
  190. _transportToken = (uint8_t)(hwMillis() & 0xFF);
  191. if (_transportToken == AUTO) {
  192. _transportToken++; // AUTO as token not allowed
  193. }
  194. const uint8_t sensorID = _transportToken;
  195. #else
  196. const uint8_t sensorID = NODE_SENSOR_ID;
  197. #endif
  198. TRANSPORT_DEBUG(PSTR("TSM:ID:REQ\n")); // request node ID
  199. (void)transportRouteMessage(build(_msgTmp, GATEWAY_ADDRESS, sensorID, C_INTERNAL,
  200. I_ID_REQUEST).set(""));
  201. }
  202. }
  203. void stIDUpdate(void)
  204. {
  205. if (_transportConfig.nodeId != AUTO) {
  206. // current node ID is valid
  207. TRANSPORT_DEBUG(PSTR("TSM:ID:OK\n"));
  208. setIndication(INDICATION_GOT_NODEID);
  209. // proceed to next state
  210. transportSwitchSM(stUplink);
  211. } else if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS) {
  212. // timeout
  213. if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) {
  214. // retries left: reenter state
  215. transportSwitchSM(stID);
  216. } else {
  217. // no retries left
  218. TRANSPORT_DEBUG(PSTR("!TSM:ID:FAIL\n"));
  219. setIndication(INDICATION_ERR_GET_NODEID);
  220. transportSwitchSM(stFailure);
  221. }
  222. }
  223. }
  224. void stUplinkTransition(void)
  225. {
  226. #if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED)
  227. TRANSPORT_DEBUG(PSTR("TSM:UPL\n"));
  228. setIndication(INDICATION_CHECK_UPLINK);
  229. _transportSM.pingResponse = INVALID_HOPS;
  230. _transportSM.pingActive = true;
  231. (void)transportRouteMessage(build(_msgTmp,GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
  232. I_PING).set((uint8_t)0x01));
  233. #endif
  234. }
  235. void stUplinkUpdate(void)
  236. {
  237. #if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED)
  238. if (_transportSM.pingResponse != INVALID_HOPS) {
  239. _transportSM.lastUplinkCheck = hwMillis();
  240. // uplink ok, i.e. GW replied
  241. TRANSPORT_DEBUG(PSTR("TSM:UPL:OK\n")); // uplink ok
  242. if (_transportSM.pingResponse != _transportConfig.distanceGW) {
  243. TRANSPORT_DEBUG(PSTR("TSM:UPL:DGWC,O=%" PRIu8 ",N=%" PRIu8 "\n"), _transportConfig.distanceGW,
  244. _transportSM.pingResponse); // distance to GW changed
  245. _transportConfig.distanceGW = _transportSM.pingResponse;
  246. }
  247. transportSwitchSM(stReady); // proceed to next state
  248. } else if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS) {
  249. // timeout
  250. if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) {
  251. // retries left: reenter state
  252. transportSwitchSM(stUplink);
  253. } else {
  254. // no retries left
  255. TRANSPORT_DEBUG(PSTR("!TSM:UPL:FAIL\n")); // uplink check failed
  256. _transportSM.pingActive = false;
  257. setIndication(INDICATION_ERR_CHECK_UPLINK);
  258. transportSwitchSM(stParent); // go back to stParent
  259. }
  260. }
  261. #else
  262. TRANSPORT_DEBUG(PSTR("TSM:UPL:DISABLED\n")); // uplink check disabled
  263. transportSwitchSM(stReady);
  264. #endif
  265. }
  266. void stReadyTransition(void)
  267. {
  268. // transport is ready and fully operational
  269. TRANSPORT_DEBUG(PSTR("TSM:READY:ID=%" PRIu8 ",PAR=%" PRIu8 ",DIS=%" PRIu8 "\n"),
  270. _transportConfig.nodeId,
  271. _transportConfig.parentNodeId, _transportConfig.distanceGW);
  272. _transportSM.uplinkOk = true;
  273. _transportSM.failureCounter = 0u; // reset failure counter
  274. _transportSM.failedUplinkTransmissions = 0u; // reset failed uplink TX counter
  275. // callback
  276. if (_transportReady_cb) {
  277. _transportReady_cb();
  278. }
  279. }
  280. // stReadyUpdate: monitors link
  281. void stReadyUpdate(void)
  282. {
  283. #if defined(MY_GATEWAY_FEATURE)
  284. if (!_lastNetworkDiscovery ||
  285. (hwMillis() - _lastNetworkDiscovery > MY_TRANSPORT_DISCOVERY_INTERVAL_MS)) {
  286. _lastNetworkDiscovery = hwMillis();
  287. TRANSPORT_DEBUG(PSTR("TSM:READY:NWD REQ\n")); // send transport network discovery
  288. (void)transportRouteMessage(build(_msgTmp, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
  289. I_DISCOVER_REQUEST).set(""));
  290. }
  291. #else
  292. if (_transportSM.failedUplinkTransmissions > MY_TRANSPORT_MAX_TX_FAILURES) {
  293. // too many uplink transmissions failed, find new parent (if non-static)
  294. #if !defined(MY_PARENT_NODE_IS_STATIC)
  295. TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,SNP\n")); // uplink failed, search new parent
  296. transportSwitchSM(stParent);
  297. #else
  298. TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,STATP\n")); // uplink failed, static parent
  299. // reset counter
  300. _transportSM.failedUplinkTransmissions = 0u;
  301. #endif
  302. }
  303. #endif
  304. #if defined(MY_RAM_ROUTING_TABLE_ENABLED)
  305. if (hwMillis() - _lastRoutingTableSave > MY_ROUTING_TABLE_SAVE_INTERVAL_MS) {
  306. _lastRoutingTableSave = hwMillis();
  307. transportSaveRoutingTable();
  308. }
  309. #endif
  310. }
  311. // stFailure: entered upon HW init failure or max retries exceeded
  312. void stFailureTransition(void)
  313. {
  314. if (_transportSM.failureCounter < MY_TRANSPORT_MAX_TSM_FAILURES) {
  315. _transportSM.failureCounter++; // increment consecutive TSM failure counter
  316. }
  317. TRANSPORT_DEBUG(PSTR("TSM:FAIL:CNT=%" PRIu8 "\n"),_transportSM.failureCounter);
  318. _transportSM.uplinkOk = false; // uplink nok
  319. _transportSM.transportActive = false; // transport inactive
  320. setIndication(INDICATION_ERR_INIT_TRANSPORT);
  321. #if defined(MY_SENSOR_NETWORK)
  322. TRANSPORT_DEBUG(PSTR("TSM:FAIL:DIS\n")); // disable transport, no need until re-init
  323. transportDisable(); // sleep
  324. #endif
  325. }
  326. void stFailureUpdate(void)
  327. {
  328. if (transportTimeInState() > ( isTransportExtendedFailure()?
  329. MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE_MS:
  330. MY_TRANSPORT_TIMEOUT_FAILURE_STATE_MS) ) {
  331. TRANSPORT_DEBUG(PSTR("TSM:FAIL:RE-INIT\n")); // attempt to re-initialise transport
  332. transportSwitchSM(stInit);
  333. }
  334. }
  335. void transportSwitchSM(transportState_t &newState)
  336. {
  337. if (_transportSM.currentState != &newState) {
  338. _transportSM.stateRetries = 0u; // state change, reset retry counter
  339. _transportSM.currentState = &newState; // change state
  340. } else {
  341. _transportSM.stateRetries++; // increment retries
  342. }
  343. if (_transportSM.currentState) {
  344. _transportSM.currentState->Transition(); // State transition
  345. }
  346. _transportSM.stateEnter = hwMillis(); // save time
  347. }
  348. uint32_t transportTimeInState(void)
  349. {
  350. return hwMillis() - _transportSM.stateEnter;
  351. }
  352. void transportUpdateSM(void)
  353. {
  354. if (_transportSM.currentState) {
  355. _transportSM.currentState->Update();
  356. }
  357. }
  358. bool isTransportReady(void)
  359. {
  360. return _transportSM.uplinkOk;
  361. }
  362. bool isTransportExtendedFailure(void)
  363. {
  364. return _transportSM.failureCounter == MY_TRANSPORT_MAX_TSM_FAILURES;
  365. }
  366. bool isTransportSearchingParent(void)
  367. {
  368. return _transportSM.findingParentNode;
  369. }
  370. bool isMessageReceived(void)
  371. {
  372. return _transportSM.msgReceived;
  373. }
  374. void resetMessageReceived(void)
  375. {
  376. _transportSM.msgReceived = false;
  377. }
  378. void transportInitialise(void)
  379. {
  380. _transportSM.failureCounter = 0u; // reset failure counter
  381. transportLoadRoutingTable(); // load routing table to RAM (if feature enabled)
  382. // initial state
  383. _transportSM.currentState = NULL;
  384. transportSwitchSM(stInit);
  385. }
  386. void transportDisable(void)
  387. {
  388. if (RADIO_CAN_POWER_OFF == true) {
  389. TRANSPORT_DEBUG(PSTR("TSF:TDI:TPD\n")); // power down transport
  390. transportPowerDown();
  391. } else {
  392. TRANSPORT_DEBUG(PSTR("TSF:TDI:TSL\n")); // send transport to sleep
  393. transportSleep();
  394. }
  395. }
  396. void transportReInitialise(void)
  397. {
  398. if (RADIO_CAN_POWER_OFF == true) {
  399. TRANSPORT_DEBUG(PSTR("TSF:TRI:TPU\n")); // transport power up
  400. transportPowerUp();
  401. transportSetAddress(_transportConfig.nodeId);
  402. } else {
  403. TRANSPORT_DEBUG(PSTR("TSF:TRI:TSB\n")); // transport standby
  404. transportStandBy();
  405. }
  406. }
  407. bool transportWaitUntilReady(const uint32_t waitingMS)
  408. {
  409. // check if transport ready
  410. TRANSPORT_DEBUG(PSTR("TSF:WUR:MS=%" PRIu32 "\n"), waitingMS); // timeout
  411. uint32_t enterMS = hwMillis();
  412. bool result = false;
  413. while (!result && ( hwMillis() - enterMS < waitingMS || !waitingMS)) {
  414. transportProcess();
  415. result = isTransportReady();
  416. doYield();
  417. }
  418. return result;
  419. }
  420. // update TSM and process incoming messages
  421. void transportProcess(void)
  422. {
  423. // update state machine
  424. transportUpdateSM();
  425. // process transport FIFO
  426. transportProcessFIFO();
  427. }
  428. bool transportCheckUplink(const bool force)
  429. {
  430. if (!force && (hwMillis() - _transportSM.lastUplinkCheck) < MY_TRANSPORT_CHKUPL_INTERVAL_MS) {
  431. TRANSPORT_DEBUG(PSTR("TSF:CKU:OK,FCTRL\n")); // flood control
  432. return true;
  433. }
  434. // ping GW
  435. const uint8_t hopsCount = transportPingNode(GATEWAY_ADDRESS);
  436. // verify hops
  437. if (hopsCount != INVALID_HOPS) {
  438. // update
  439. _transportSM.lastUplinkCheck = hwMillis();
  440. TRANSPORT_DEBUG(PSTR("TSF:CKU:OK\n"));
  441. // did distance to GW change upstream, eg. re-routing of uplink nodes
  442. if (hopsCount != _transportConfig.distanceGW) {
  443. TRANSPORT_DEBUG(PSTR("TSF:CKU:DGWC,O=%" PRIu8 ",N=%" PRIu8 "\n"), _transportConfig.distanceGW,
  444. hopsCount); // distance to GW changed
  445. _transportConfig.distanceGW = hopsCount;
  446. }
  447. return true;
  448. } else {
  449. TRANSPORT_DEBUG(PSTR("TSF:CKU:FAIL\n"));
  450. return false;
  451. }
  452. }
  453. bool transportAssignNodeID(const uint8_t newNodeId)
  454. {
  455. // verify if ID valid
  456. if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) {
  457. _transportConfig.nodeId = newNodeId;
  458. transportSetAddress(newNodeId);
  459. // Write ID to EEPROM
  460. hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId);
  461. TRANSPORT_DEBUG(PSTR("TSF:SID:OK,ID=%" PRIu8 "\n"),newNodeId); // Node ID assigned
  462. return true;
  463. } else {
  464. TRANSPORT_DEBUG(PSTR("!TSF:SID:FAIL,ID=%" PRIu8 "\n"),newNodeId); // ID is invalid, cannot assign ID
  465. setIndication(INDICATION_ERR_NET_FULL);
  466. _transportConfig.nodeId = AUTO;
  467. return false;
  468. }
  469. }
  470. bool transportRouteMessage(MyMessage &message)
  471. {
  472. const uint8_t destination = message.destination;
  473. uint8_t route = _transportConfig.parentNodeId; // by default, all traffic is routed via parent node
  474. if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) {
  475. TRANSPORT_DEBUG(PSTR("!TSF:RTE:FPAR ACTIVE\n")); // find parent active, message not sent
  476. // request to send a non-BC message while finding parent active, abort
  477. return false;
  478. }
  479. if (destination == GATEWAY_ADDRESS) {
  480. route = _transportConfig.parentNodeId; // message to GW always routes via parent
  481. } else if (destination == BROADCAST_ADDRESS) {
  482. route = BROADCAST_ADDRESS; // message to BC does not require routing
  483. } else {
  484. #if defined(MY_REPEATER_FEATURE)
  485. // destination not GW & not BC, get route
  486. route = transportGetRoute(destination);
  487. if (route == AUTO) {
  488. TRANSPORT_DEBUG(PSTR("!TSF:RTE:%" PRIu8 " UNKNOWN\n"), destination); // route unknown
  489. #if !defined(MY_GATEWAY_FEATURE)
  490. if (message.last != _transportConfig.parentNodeId) {
  491. // message not from parent, i.e. child node - route it to parent
  492. route = _transportConfig.parentNodeId;
  493. } else {
  494. // route unknown and msg received from parent, send it to destination assuming in rx radius
  495. route = destination;
  496. }
  497. #else
  498. // if GW, all unknown destinations are directly addressed
  499. route = destination;
  500. #endif
  501. }
  502. #else
  503. if (destination > GATEWAY_ADDRESS && destination < BROADCAST_ADDRESS) {
  504. // node2node traffic: assume node is in vincinity. If transmission fails, hand over to parent
  505. if (transportSendWrite(destination, message)) {
  506. TRANSPORT_DEBUG(PSTR("TSF:RTE:N2N OK\n"));
  507. return true;
  508. }
  509. TRANSPORT_DEBUG(PSTR("!TSF:RTE:N2N FAIL\n"));
  510. }
  511. route = _transportConfig.parentNodeId; // not a repeater, all traffic routed via parent
  512. #endif
  513. }
  514. // send message
  515. const bool result = transportSendWrite(route, message);
  516. #if !defined(MY_GATEWAY_FEATURE)
  517. // update counter
  518. if (route == _transportConfig.parentNodeId) {
  519. if (!result) {
  520. setIndication(INDICATION_ERR_TX);
  521. _transportSM.failedUplinkTransmissions++;
  522. } else {
  523. _transportSM.failedUplinkTransmissions = 0u;
  524. #if defined(MY_SIGNAL_REPORT_ENABLED)
  525. // update uplink quality monitor
  526. const int16_t signalStrengthRSSI = transportGetSignalReport(SR_TX_RSSI);
  527. _transportSM.uplinkQualityRSSI = static_cast<transportRSSI_t>((1 - UPLINK_QUALITY_WEIGHT) *
  528. _transportSM.uplinkQualityRSSI
  529. + (UPLINK_QUALITY_WEIGHT * transportRSSItoInternal(signalStrengthRSSI)));
  530. #endif
  531. }
  532. }
  533. #else
  534. if(!result) {
  535. setIndication(INDICATION_ERR_TX);
  536. }
  537. #endif
  538. return result;
  539. }
  540. bool transportSendRoute(MyMessage &message)
  541. {
  542. bool result = false;
  543. if (isTransportReady()) {
  544. result = transportRouteMessage(message);
  545. } else {
  546. // TNR: transport not ready
  547. TRANSPORT_DEBUG(PSTR("!TSF:SND:TNR\n"));
  548. }
  549. return result;
  550. }
  551. // only be used inside transport
  552. bool transportWait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType)
  553. {
  554. const uint32_t enterMS = hwMillis();
  555. // invalidate msg type
  556. _msg.type = !msgType;
  557. bool expectedResponse = false;
  558. while ((hwMillis() - enterMS < waitingMS) && !expectedResponse) {
  559. // process incoming messages
  560. transportProcessFIFO();
  561. doYield();
  562. expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgType);
  563. }
  564. return expectedResponse;
  565. }
  566. uint8_t transportPingNode(const uint8_t targetId)
  567. {
  568. if(!_transportSM.pingActive) {
  569. TRANSPORT_DEBUG(PSTR("TSF:PNG:SEND,TO=%" PRIu8 "\n"), targetId);
  570. if(targetId == _transportConfig.nodeId) {
  571. // pinging self
  572. _transportSM.pingResponse = 0u;
  573. } else {
  574. _transportSM.pingActive = true;
  575. _transportSM.pingResponse = INVALID_HOPS;
  576. (void)transportRouteMessage(build(_msgTmp, targetId, NODE_SENSOR_ID, C_INTERNAL,
  577. I_PING).set((uint8_t)0x01));
  578. // Wait for ping reply or timeout
  579. (void)transportWait(2000, C_INTERNAL, I_PONG);
  580. }
  581. // make sure missing I_PONG msg does not block pinging function by leaving pingActive=true
  582. _transportSM.pingActive = false;
  583. return _transportSM.pingResponse;
  584. } else {
  585. TRANSPORT_DEBUG(PSTR("!TSF:PNG:ACTIVE\n")); // ping active, cannot start new ping
  586. return INVALID_HOPS;
  587. }
  588. }
  589. uint32_t transportGetHeartbeat(void)
  590. {
  591. return transportTimeInState();
  592. }
  593. void transportProcessMessage(void)
  594. {
  595. // Manage signing timeout
  596. (void)signerCheckTimer();
  597. // receive message
  598. setIndication(INDICATION_RX);
  599. uint8_t payloadLength = transportReceive((uint8_t *)
  600. &_msg.last); // last is the first byte of the payload buffer
  601. // get message length and limit size
  602. const uint8_t msgLength = min(mGetLength(_msg), (uint8_t)MAX_PAYLOAD);
  603. // calculate expected length
  604. const uint8_t expectedMessageLength = HEADER_SIZE + (mGetSigned(_msg) ? MAX_PAYLOAD : msgLength);
  605. #if defined(MY_RF24_ENABLE_ENCRYPTION) || defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) || defined(MY_RFM95_ENABLE_ENCRYPTION)
  606. // payload length = a multiple of blocksize length for decrypted messages, i.e. cannot be used for payload length check
  607. payloadLength = expectedMessageLength;
  608. #endif
  609. const uint8_t command = mGetCommand(_msg);
  610. const uint8_t type = _msg.type;
  611. const uint8_t sender = _msg.sender;
  612. const uint8_t last = _msg.last;
  613. const uint8_t destination = _msg.destination;
  614. TRANSPORT_DEBUG(PSTR("TSF:MSG:READ,%" PRIu8 "-%" PRIu8 "-%" PRIu8 ",s=%" PRIu8 ",c=%" PRIu8 ",t=%"
  615. PRIu8 ",pt=%" PRIu8 ",l=%" PRIu8 ",sg=%" PRIu8 ":%s\n"),
  616. sender, last, destination, _msg.sensor, command, type, mGetPayloadType(_msg), msgLength,
  617. mGetSigned(_msg), ((command == C_INTERNAL &&
  618. type == I_NONCE_RESPONSE) ? "<NONCE>" : _msg.getString(_convBuf)));
  619. // Reject payloads with incorrect length
  620. if (payloadLength != expectedMessageLength) {
  621. setIndication(INDICATION_ERR_LENGTH);
  622. TRANSPORT_DEBUG(PSTR("!TSF:MSG:LEN=%" PRIu8 ",EXP=%" PRIu8 "\n"), payloadLength,
  623. expectedMessageLength); // invalid payload length
  624. return;
  625. }
  626. // Reject messages with incorrect protocol version
  627. if (mGetVersion(_msg) != PROTOCOL_VERSION) {
  628. setIndication(INDICATION_ERR_VERSION);
  629. TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%" PRIu8 "!=%" PRIu8 "\n"), mGetVersion(_msg),
  630. PROTOCOL_VERSION); // protocol version mismatch
  631. return;
  632. }
  633. // Reject messages that do not pass verification
  634. if (!signerVerifyMsg(_msg)) {
  635. setIndication(INDICATION_ERR_SIGN);
  636. TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN VERIFY FAIL\n"));
  637. return;
  638. }
  639. // update routing table if msg not from parent
  640. #if defined(MY_REPEATER_FEATURE)
  641. #if !defined(MY_GATEWAY_FEATURE)
  642. if (last != _transportConfig.parentNodeId) {
  643. #else
  644. // GW doesn't have parent
  645. {
  646. #endif
  647. // Message is from one of the child nodes and not sent from this node. Add it to routing table.
  648. if (sender != _transportConfig.nodeId)
  649. {
  650. transportSetRoute(sender, last);
  651. }
  652. }
  653. #endif // MY_REPEATER_FEATURE
  654. // set message received flag
  655. _transportSM.msgReceived = true;
  656. // Is message addressed to this node?
  657. if (destination == _transportConfig.nodeId) {
  658. // prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes)
  659. mSetLength(_msg, min(mGetLength(_msg), (uint8_t)MAX_PAYLOAD));
  660. // null terminate data
  661. _msg.data[msgLength] = 0u;
  662. // Check if sender requests an ack back.
  663. if (mGetRequestAck(_msg)) {
  664. TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n")); // ACK requested
  665. _msgTmp = _msg; // Copy message
  666. mSetRequestAck(_msgTmp,
  667. false); // Reply without ack flag (otherwise we would end up in an eternal loop)
  668. mSetAck(_msgTmp, true); // set ACK flag
  669. _msgTmp.sender = _transportConfig.nodeId;
  670. _msgTmp.destination = sender;
  671. // send ACK, use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply
  672. (void)transportSendRoute(_msgTmp);
  673. }
  674. if(!mGetAck(_msg)) {
  675. // only process if not ACK
  676. if (command == C_INTERNAL) {
  677. // Process signing related internal messages
  678. if (signerProcessInternal(_msg)) {
  679. return; // Signer processing indicated no further action needed
  680. }
  681. #if !defined(MY_GATEWAY_FEATURE)
  682. if (type == I_ID_RESPONSE) {
  683. #if (MY_NODE_ID == AUTO)
  684. // only active if node ID dynamic
  685. if ((_msg.sensor == _transportToken) || (_msg.sensor == AUTO)) {
  686. (void)transportAssignNodeID(_msg.getByte());
  687. } else {
  688. TRANSPORT_DEBUG(PSTR("!TSF:MSG:ID TK INVALID\n"));
  689. }
  690. #endif
  691. return; // no further processing required
  692. }
  693. if (type == I_FIND_PARENT_RESPONSE) {
  694. #if !defined(MY_GATEWAY_FEATURE) && !defined(MY_PARENT_NODE_IS_STATIC)
  695. if (_transportSM.findingParentNode) { // only process if find parent active
  696. // Reply to a I_FIND_PARENT_REQUEST message. Check if the distance is shorter than we already have.
  697. uint8_t distance = _msg.getByte();
  698. if (isValidDistance(distance)) {
  699. distance++; // Distance to gateway is one more for us w.r.t. parent
  700. // update settings if distance shorter or preferred parent found
  701. if (((isValidDistance(distance) && distance < _transportConfig.distanceGW) || (!_autoFindParent &&
  702. sender == (uint8_t)MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) {
  703. // Found a neighbor closer to GW than previously found
  704. if (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID) {
  705. _transportSM.preferredParentFound = true;
  706. TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF\n")); // find parent, preferred parent found
  707. }
  708. _transportConfig.distanceGW = distance;
  709. _transportConfig.parentNodeId = sender;
  710. TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR OK,ID=%" PRIu8 ",D=%" PRIu8 "\n"), _transportConfig.parentNodeId,
  711. _transportConfig.distanceGW);
  712. }
  713. }
  714. } else {
  715. TRANSPORT_DEBUG(PSTR("!TSF:MSG:FPAR INACTIVE\n")); // find parent response received, but inactive
  716. }
  717. return; // no further processing required
  718. #endif
  719. }
  720. #endif // !defined(MY_GATEWAY_FEATURE)
  721. // general
  722. if (type == I_PING) {
  723. TRANSPORT_DEBUG(PSTR("TSF:MSG:PINGED,ID=%" PRIu8 ",HP=%" PRIu8 "\n"), sender,
  724. _msg.getByte()); // node pinged
  725. #if defined(MY_GATEWAY_FEATURE) && (F_CPU>16000000)
  726. // delay for fast GW and slow nodes
  727. delay(5);
  728. #endif
  729. (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
  730. I_PONG).set((uint8_t)1));
  731. return; // no further processing required
  732. }
  733. if (type == I_PONG) {
  734. if (_transportSM.pingActive) {
  735. _transportSM.pingActive = false;
  736. _transportSM.pingResponse = _msg.getByte();
  737. TRANSPORT_DEBUG(PSTR("TSF:MSG:PONG RECV,HP=%" PRIu8 "\n"),
  738. _transportSM.pingResponse); // pong received
  739. } else {
  740. TRANSPORT_DEBUG(PSTR("!TSF:MSG:PONG RECV,INACTIVE\n")); // pong received, but !pingActive
  741. }
  742. return; // no further processing required
  743. }
  744. if (type == I_SIGNAL_REPORT_REVERSE) {
  745. return; // no further processing required
  746. }
  747. if (type == I_SIGNAL_REPORT_REQUEST) {
  748. int16_t value = INVALID_RSSI;
  749. #if defined(MY_SIGNAL_REPORT_ENABLED)
  750. const char command = _msg.data[0];
  751. if (_msg.data[1] != '!') {
  752. value = transportSignalReport(command);
  753. } else {
  754. // send request
  755. if (transportRouteMessage(build(_msgTmp, _msg.last, NODE_SENSOR_ID, C_INTERNAL,
  756. I_SIGNAL_REPORT_REVERSE).set((uint8_t)255))) {
  757. // S>s, R>r, ascii delta = 32
  758. value = transportSignalReport(command + 32); // reverse
  759. };
  760. }
  761. #endif
  762. (void)transportRouteMessage(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
  763. I_SIGNAL_REPORT_RESPONSE).set(value));
  764. return; // no further processing required
  765. }
  766. if (_processInternalCoreMessage()) {
  767. return; // no further processing required
  768. }
  769. } else if (command == C_STREAM) {
  770. #if defined(MY_OTA_FIRMWARE_FEATURE)
  771. if(firmwareOTAUpdateProcess()) {
  772. return; // OTA FW update processing indicated no further action needed
  773. }
  774. #endif
  775. }
  776. } else {
  777. TRANSPORT_DEBUG(
  778. PSTR("TSF:MSG:ACK\n")); // received message is ACK, no internal processing, handover to msg callback
  779. }
  780. #if defined(MY_OTA_LOG_RECEIVER_FEATURE)
  781. if ((type == I_LOG_MESSAGE) && (command == C_INTERNAL)) {
  782. OTALogPrint(_msg);
  783. return; // no further processing required
  784. }
  785. #endif //defined(MY_OTA_LOG_RECEIVER_FEATURE)
  786. #if defined(MY_GATEWAY_FEATURE)
  787. // Hand over message to controller
  788. (void)gatewayTransportSend(_msg);
  789. #endif
  790. // Call incoming message callback if available
  791. if (receive) {
  792. receive(_msg);
  793. }
  794. } else if (destination == BROADCAST_ADDRESS) {
  795. TRANSPORT_DEBUG(PSTR("TSF:MSG:BC\n")); // broadcast msg
  796. if (command == C_INTERNAL) {
  797. if (isTransportReady()) {
  798. // only reply if node is fully operational
  799. if (type == I_FIND_PARENT_REQUEST) {
  800. #if defined(MY_REPEATER_FEATURE)
  801. if (sender != _transportConfig.parentNodeId) { // no circular reference
  802. TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR REQ,ID=%" PRIu8 "\n"), sender); // FPAR: find parent request
  803. // check if uplink functional - node can only be parent node if link to GW functional
  804. // this also prevents circular references in case GW ooo
  805. if (transportCheckUplink()) {
  806. _transportSM.lastUplinkCheck = hwMillis();
  807. TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok
  808. // random delay minimizes collisions
  809. delay(hwMillis() & 0x3ff);
  810. (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
  811. I_FIND_PARENT_RESPONSE).set(_transportConfig.distanceGW));
  812. } else {
  813. TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request
  814. }
  815. }
  816. #endif
  817. return; // no further processing required, do not forward
  818. }
  819. } // isTransportReady
  820. if (type == I_FIND_PARENT_RESPONSE) {
  821. return; // no further processing required, do not forward
  822. }
  823. #if !defined(MY_GATEWAY_FEATURE)
  824. if (type == I_DISCOVER_REQUEST) {
  825. if (last == _transportConfig.parentNodeId) {
  826. // random wait to minimize collisions
  827. delay(hwMillis() & 0x3ff);
  828. (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL,
  829. I_DISCOVER_RESPONSE).set(_transportConfig.parentNodeId));
  830. // no return here (for fwd if repeater)
  831. }
  832. }
  833. #endif
  834. }
  835. // controlled BC relay
  836. #if defined(MY_REPEATER_FEATURE)
  837. // controlled BC repeating: forward only if message received from parent and sender not self to prevent circular fwds
  838. if(last == _transportConfig.parentNodeId && sender != _transportConfig.nodeId &&
  839. isTransportReady()) {
  840. TRANSPORT_DEBUG(PSTR("TSF:MSG:FWD BC MSG\n")); // controlled broadcast msg forwarding
  841. (void)transportRouteMessage(_msg);
  842. }
  843. #endif
  844. // Callback for BC, only for non-internal messages
  845. if (command != C_INTERNAL) {
  846. #if !defined(MY_GATEWAY_FEATURE)
  847. // only proceed if message received from parent
  848. if (last != _transportConfig.parentNodeId) {
  849. return;
  850. }
  851. #endif
  852. #if defined(MY_GATEWAY_FEATURE)
  853. // Hand over message to controller
  854. (void)gatewayTransportSend(_msg);
  855. #endif
  856. if (receive) {
  857. TRANSPORT_DEBUG(PSTR("TSF:MSG:RCV CB\n")); // hand over message to receive callback function
  858. receive(_msg);
  859. }
  860. }
  861. } else {
  862. // msg not to us and not BC, relay msg
  863. #if defined(MY_REPEATER_FEATURE)
  864. if (isTransportReady()) {
  865. if (command == C_INTERNAL) {
  866. if (type == I_PING || type == I_PONG) {
  867. uint8_t hopsCnt = _msg.getByte();
  868. TRANSPORT_DEBUG(PSTR("TSF:MSG:REL PxNG,HP=%" PRIu8 "\n"), hopsCnt);
  869. if (hopsCnt != MAX_HOPS) {
  870. hopsCnt++;
  871. _msg.set(hopsCnt);
  872. }
  873. }
  874. }
  875. // Relay this message to another node
  876. TRANSPORT_DEBUG(PSTR("TSF:MSG:REL MSG\n")); // relay msg
  877. (void)transportRouteMessage(_msg);
  878. }
  879. #else
  880. TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NREP\n")); // message relaying request, but not a repeater
  881. #endif
  882. }
  883. (void)last; //avoid cppcheck warning
  884. }
  885. void transportInvokeSanityCheck(void)
  886. {
  887. // Suppress this because the function may return a variable value in some configurations
  888. // cppcheck-suppress knownConditionTrueFalse
  889. if (!transportSanityCheck()) {
  890. TRANSPORT_DEBUG(PSTR("!TSF:SAN:FAIL\n")); // sanity check fail
  891. transportSwitchSM(stFailure);
  892. } else {
  893. TRANSPORT_DEBUG(PSTR("TSF:SAN:OK\n")); // sanity check ok
  894. }
  895. }
  896. void transportProcessFIFO(void)
  897. {
  898. if (!_transportSM.transportActive) {
  899. // transport not active, no further processing required
  900. return;
  901. }
  902. #if defined(MY_TRANSPORT_SANITY_CHECK)
  903. if (hwMillis() - _lastSanityCheck > MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS) {
  904. _lastSanityCheck = hwMillis();
  905. transportInvokeSanityCheck();
  906. }
  907. #endif
  908. uint8_t _processedMessages = MAX_SUBSEQ_MSGS;
  909. // process all msgs in FIFO or counter exit
  910. while (transportAvailable() && _processedMessages--) {
  911. transportProcessMessage();
  912. }
  913. #if defined(MY_OTA_FIRMWARE_FEATURE)
  914. if (isTransportReady()) {
  915. // only process if transport ok
  916. firmwareOTAUpdateRequest();
  917. }
  918. #endif
  919. }
  920. bool transportSendWrite(const uint8_t to, MyMessage &message)
  921. {
  922. message.last = _transportConfig.nodeId; // Update last
  923. // sign message if required
  924. if (!signerSignMsg(message)) {
  925. TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n"));
  926. setIndication(INDICATION_ERR_SIGN);
  927. return false;
  928. }
  929. // msg length changes if signed
  930. const uint8_t totalMsgLength = HEADER_SIZE + ( mGetSigned(message) ? MAX_PAYLOAD : mGetLength(
  931. message) );
  932. // send
  933. setIndication(INDICATION_TX);
  934. bool result = transportSend(to, &message, min((uint8_t)MAX_MESSAGE_LENGTH, totalMsgLength),
  935. _transportConfig.passiveMode);
  936. // broadcasting (workaround counterfeits)
  937. result |= (to == BROADCAST_ADDRESS);
  938. TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%" PRIu8 "-%" PRIu8 "-%" PRIu8 "-%" PRIu8 ",s=%" PRIu8 ",c=%"
  939. PRIu8 ",t=%" PRIu8 ",pt=%" PRIu8 ",l=%" PRIu8 ",sg=%" PRIu8 ",ft=%" PRIu8 ",st=%s:%s\n"),
  940. (_transportConfig.passiveMode ? "?" : result ? "" : "!"), message.sender, message.last, to,
  941. message.destination,
  942. message.sensor,
  943. mGetCommand(message), message.type,
  944. mGetPayloadType(message), mGetLength(message), mGetSigned(message),
  945. _transportSM.failedUplinkTransmissions,
  946. (result ? "OK" : "NACK"),
  947. ((mGetCommand(message) == C_INTERNAL &&
  948. message.type == I_NONCE_RESPONSE) ? "<NONCE>" : message.getString(_convBuf)));
  949. return result;
  950. }
  951. void transportRegisterReadyCallback(transportCallback_t cb)
  952. {
  953. _transportReady_cb = cb;
  954. }
  955. uint8_t transportGetNodeId(void)
  956. {
  957. return _transportConfig.nodeId;
  958. }
  959. uint8_t transportGetParentNodeId(void)
  960. {
  961. return _transportConfig.parentNodeId;
  962. }
  963. uint8_t transportGetDistanceGW(void)
  964. {
  965. return _transportConfig.distanceGW;
  966. }
  967. void transportClearRoutingTable(void)
  968. {
  969. for (uint16_t i = 0; i < SIZE_ROUTES; i++) {
  970. transportSetRoute((uint8_t)i, BROADCAST_ADDRESS);
  971. }
  972. transportSaveRoutingTable(); // save cleared routing table to EEPROM (if feature enabled)
  973. TRANSPORT_DEBUG(PSTR("TSF:CRT:OK\n")); // clear routing table
  974. }
  975. void transportLoadRoutingTable(void)
  976. {
  977. #if defined(MY_RAM_ROUTING_TABLE_ENABLED)
  978. hwReadConfigBlock((void*)&_transportRoutingTable.route, (void*)EEPROM_ROUTES_ADDRESS, SIZE_ROUTES);
  979. TRANSPORT_DEBUG(PSTR("TSF:LRT:OK\n")); // load routing table
  980. #endif
  981. }
  982. void transportSaveRoutingTable(void)
  983. {
  984. #if defined(MY_RAM_ROUTING_TABLE_ENABLED)
  985. hwWriteConfigBlock((void*)&_transportRoutingTable.route, (void*)EEPROM_ROUTES_ADDRESS, SIZE_ROUTES);
  986. TRANSPORT_DEBUG(PSTR("TSF:SRT:OK\n")); // save routing table
  987. #endif
  988. }
  989. void transportSetRoute(const uint8_t node, const uint8_t route)
  990. {
  991. #if defined(MY_RAM_ROUTING_TABLE_ENABLED)
  992. _transportRoutingTable.route[node] = route;
  993. #else
  994. hwWriteConfig(EEPROM_ROUTES_ADDRESS + node, route);
  995. #endif
  996. }
  997. uint8_t transportGetRoute(const uint8_t node)
  998. {
  999. uint8_t result;
  1000. #if defined(MY_RAM_ROUTING_TABLE_ENABLED)
  1001. result = _transportRoutingTable.route[node];
  1002. #else
  1003. result = hwReadConfig(EEPROM_ROUTES_ADDRESS + node);
  1004. #endif
  1005. return result;
  1006. }
  1007. void transportReportRoutingTable(void)
  1008. {
  1009. #if defined(MY_REPEATER_FEATURE)
  1010. for (uint16_t cnt = 0; cnt < SIZE_ROUTES; cnt++) {
  1011. const uint8_t route = transportGetRoute(cnt);
  1012. if (route != BROADCAST_ADDRESS) {
  1013. TRANSPORT_DEBUG(PSTR("TSF:RRT:ROUTE N=%" PRIu8 ",R=%" PRIu8 "\n"), cnt, route);
  1014. uint8_t outBuf[2] = { (uint8_t)cnt,route };
  1015. (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG).set(outBuf,
  1016. 2));
  1017. wait(200);
  1018. }
  1019. }
  1020. #endif
  1021. }
  1022. void transportTogglePassiveMode(const bool OnOff)
  1023. {
  1024. #if !defined (MY_PASSIVE_NODE)
  1025. _transportConfig.passiveMode = OnOff;
  1026. #else
  1027. (void)OnOff;
  1028. #endif
  1029. }
  1030. int16_t transportGetSignalReport(const signalReport_t signalReport)
  1031. {
  1032. int16_t result;
  1033. switch (signalReport) {
  1034. case SR_RX_RSSI:
  1035. result = transportGetReceivingRSSI();
  1036. break;
  1037. case SR_TX_RSSI:
  1038. result = transportGetSendingRSSI();
  1039. break;
  1040. case SR_RX_SNR:
  1041. result = transportGetReceivingSNR();
  1042. break;
  1043. case SR_TX_SNR:
  1044. result = transportGetSendingSNR();
  1045. break;
  1046. case SR_TX_POWER_LEVEL:
  1047. result = transportGetTxPowerLevel();
  1048. break;
  1049. case SR_TX_POWER_PERCENT:
  1050. result = transportGetTxPowerPercent();
  1051. break;
  1052. case SR_UPLINK_QUALITY:
  1053. result = transportInternalToRSSI(_transportSM.uplinkQualityRSSI);
  1054. break;
  1055. default:
  1056. result = 0;
  1057. break;
  1058. }
  1059. return result;
  1060. }
  1061. int16_t transportSignalReport(const char command)
  1062. {
  1063. signalReport_t reportCommand;
  1064. switch (command) {
  1065. case 'S':
  1066. // SNR (if available) of incoming message
  1067. reportCommand = SR_RX_SNR;
  1068. break;
  1069. case 's':
  1070. // SNR (if available) of incoming ACK message
  1071. reportCommand = SR_TX_SNR;
  1072. break;
  1073. case 'R':
  1074. // RSSI (if available) of incoming message
  1075. reportCommand = SR_RX_RSSI;
  1076. break;
  1077. case 'r':
  1078. // RSSI (if available) of incoming ACK message
  1079. reportCommand = SR_TX_RSSI;
  1080. break;
  1081. case 'P':
  1082. // TX powerlevel in %
  1083. reportCommand = SR_TX_POWER_PERCENT;
  1084. break;
  1085. case 'T':
  1086. // TX powerlevel in dBm
  1087. reportCommand = SR_TX_POWER_LEVEL;
  1088. break;
  1089. case 'U':
  1090. // Uplink quality
  1091. reportCommand = SR_UPLINK_QUALITY;
  1092. break;
  1093. default:
  1094. reportCommand = SR_NOT_DEFINED;
  1095. break;
  1096. }
  1097. const uint16_t result = transportGetSignalReport(reportCommand);
  1098. TRANSPORT_DEBUG(PSTR("TSF:SIR:CMD=%" PRIu8 ",VAL=%" PRIu16 "\n"), reportCommand, result);
  1099. return result;
  1100. }