star-mesh LoRa network

Dependencies:   sx12xx_hal

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "main.h"
00002 
00003 #ifdef TARGET_STM32L0
00004 #include "stm32l0xx_ll_utils.h"
00005 #elif defined(TARGET_STM32L1)
00006 #include "stm32l1xx_ll_utils.h"
00007 #elif defined(TARGET_STM32L4)
00008 #include "stm32l4xx_ll_utils.h"
00009 #endif
00010 
00011 #ifdef TARGET_DISCO_L072CZ_LRWAN1
00012 DigitalOut rxdbg(PB_8);
00013 #elif defined(TARGET_FF_ARDUINO)
00014 DigitalOut rxdbg(PC_3);
00015 #endif
00016 
00017 RawSerial pc(USBTX, USBRX); 
00018 
00019 EventQueue queue(32 * EVENTS_EVENT_SIZE);
00020 
00021 char pcbuf[64]; /* local user terminal */
00022 int pcbuf_len;
00023 
00024 unsigned discovery_ans_time_step_us;
00025 unsigned discovery_ans_time_total_us;
00026 
00027 uint32_t my_id;
00028 
00029 #ifdef GATEWAY
00030 const uint8_t hops_from_gateway = 0;
00031 #else
00032 uint8_t hops_from_gateway;
00033 #endif
00034 
00035 volatile flags_t flags;
00036 
00037 static uint8_t attemptCnt;
00038 static int req_timeout_id;
00039 reqflags_t reqFlags;
00040 volatile unsigned channelVacantCount;
00041 
00042 uint8_t txBuf[255];
00043 uint8_t txBuf_idx;
00044 uint32_t tx_dest_id;
00045 
00046 bool remove_directlyAttached_device(uint32_t id)
00047 {
00048     bool removed = false;
00049     lid_list_t* L;
00050     for (L = attachedDevices; L != NULL; L = L->next) {
00051         if (L->id == id) {
00052             /* remove/clear all child devices also */
00053             cid_list_t* children;
00054             for (children = L->attachedList;  children != NULL; children = children->next)
00055                 children->id = ID_NONE;
00056 
00057             removed = true;
00058             L->id = ID_NONE;
00059         }
00060     }
00061     return removed;
00062 }
00063 
00064 void remove_childDevice(uint32_t id, uint32_t* attachedTo)
00065 {
00066     lid_list_t* L;
00067     *attachedTo = ID_NONE;
00068     for (L = attachedDevices; L != NULL; L = L->next) {
00069         cid_list_t* children;
00070         for (children = L->attachedList;  children != NULL; children = children->next) {
00071             if (children->id == id) {
00072                 *attachedTo = L->id;
00073                 children->id = ID_NONE;
00074             }
00075         }
00076     }
00077 }
00078 
00079 void setPreambleSize(bool wakesize, uint8_t by)
00080 {
00081     if (wakesize) {
00082         Radio::LoRaPacketConfig(N_PRE_SYMB, false, true, false);  // preambleLen, fixLen, crcOn, invIQ
00083         if (flags.discoverAnswering) {
00084             Mdbg_printf("\e[41mLPDA%02x\e[0m ", by);
00085         }
00086     } else {
00087         Radio::LoRaPacketConfig(8, false, true, false);  // preambleLen, fixLen, crcOn, invIQ
00088     }
00089 }
00090 
00091 static void rxSingle()
00092 {
00093     if (flags.CallTXRequest) {
00094         txBuf_send(true);
00095         flags.CallTXRequest = 0;
00096     } else if (reqFlags.octet == 0 || flags.deferred_send) {
00097         flags.vacantCheck = 1;
00098         /* rx single: auto-timeout */
00099         Radio::Rx(999);
00100     } 
00101 }
00102 
00103 void start_periodic_rxing(uint8_t by)   // definition
00104 {
00105     setPreambleSize(true, by | 3);
00106     Radio::SetLoRaSymbolTimeout(8);
00107     queue.call_in(WAKEUP_INTERVAL_MS, rxSingle);
00108 }
00109 
00110 const char* const cmdStrs[] = {
00111     "unused", /*  0  CMD_UNUSED */
00112     "Ans", /*  1  CMD_ANS, */
00113     "discoverReq", /*  2  CMD_DISCOVERY_REQ, */
00114     "discoverAns", /*  3  CMD_DISCOVERY_ANS, */
00115     "attachReq", /*  4  CMD_ATTACH_REQ, */
00116     "userPayReqUp", /*  5  CMD_USER_PAYLOAD_UP_REQ, */
00117     "userPayReqDn", /*  6  CMD_USER_PAYLOAD_DN_REQ, */
00118     "newDev", /*  7  CMD_NEW_DEVICE_ATTACHED_REQ, */
00119     "removeDev", /*  8  CMD_REMOVE_DEVICE_REQ, */
00120     "downstreamNotResponding", /*  9  CMD_DOWNSTREAM_NOT_RESPONDING, */
00121 };
00122 
00123 
00124 uint8_t tx_len;
00125 
00126 static void _send_(void)
00127 {
00128     if (flags.sending_req)
00129         setPreambleSize(true, 2); // sending request
00130 
00131     Radio::Send(tx_len, 0, 0, 0);   /* begin transmission */
00132     if (flags.discoverAnswering) {
00133         Mdbg_printf("\e[36m%u->txing%u_to:%lx_%s\e[0m", txBuf_idx, tx_len, tx_dest_id, cmdStrs[Radio::radio.tx_buf[9]]);
00134     } else {
00135         Mdbg_printf("%u->\e[31mtxing\e[0m%u_to:%lx_\e[7m%s\e[0m", txBuf_idx, tx_len, tx_dest_id, cmdStrs[Radio::radio.tx_buf[9]]);
00136     }
00137     Mdbg_printf(":");
00138 #ifdef MESH_DEBUG
00139     /*{
00140         unsigned n;
00141         for (n = 0; n < tx_len; n++)
00142             pc.printf("%02x ", Radio::radio.tx_buf[n]);
00143     }*/
00144 #endif /* MESH_DEBUG */
00145 
00146     if (flags.sending_req) {
00147         attemptCnt++;
00148         flags.getAns = 1;
00149     } else
00150         flags.getAns = 0;
00151 
00152     channelVacantCount = 0;
00153 } // .._send_()
00154 
00155 void txBuf_send(bool sendingReq)
00156 {
00157     uint16_t crc;
00158 
00159     if (txBuf_idx == 0) {
00160         return;
00161     }
00162 
00163     Radio::Standby();
00164 
00165     if (sendingReq) {
00166         if (attemptCnt > RETRY_LIMIT) {
00167             /* give up trying */
00168             txBuf_idx = 0;
00169             attemptCnt = 0;
00170 #ifdef GATEWAY
00171             reqFlags.octet = 0; // TODO dropping request
00172             start_periodic_rxing(0x80);  // retry give-up
00173             return;
00174 #else
00175             if (tx_dest_id == attUp.id) {
00176                 /* find new upstream device */
00177                 queue.call_in(1000, upstream_init);
00178                 if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ)
00179                     app_uplink_complete();  // TODO report failure to application layer
00180                 return;
00181             } else {
00182                 txBuf[txBuf_idx++] = CMD_DOWNSTREAM_NOT_RESPONDING;
00183                 putu32ToBuf(&txBuf[txBuf_idx], my_id);   // ID of reporting device
00184                 txBuf_idx += 4;
00185                 putu32ToBuf(&txBuf[txBuf_idx], tx_dest_id);   // ID of failed downstream device
00186                 txBuf_idx += 4;
00187                 tx_dest_id = attUp.id;
00188                 reqFlags.bits.currentOp = CMD_DOWNSTREAM_NOT_RESPONDING; 
00189             }
00190 #endif /* !GATEWAY */
00191         }
00192     } // ..if (sendingReq)
00193 
00194     Radio::radio.tx_buf[0] = hops_from_gateway;
00195     putu32ToBuf(&Radio::radio.tx_buf[1], my_id);
00196     putu32ToBuf(&Radio::radio.tx_buf[5], tx_dest_id);
00197 
00198     tx_len = txBuf_idx;
00199     memcpy(&Radio::radio.tx_buf[9], txBuf, tx_len);
00200     tx_len += 9;
00201     crc = crc16(Radio::radio.tx_buf, tx_len);
00202     putu16ToBuf(&Radio::radio.tx_buf[tx_len], crc);
00203     tx_len += 2;
00204 
00205     flags.sending_req = sendingReq;
00206     if (sendingReq) {
00207         if (channelVacantCount > CHANNEL_VACANT_REQUIRED_COUNT)
00208             _send_();
00209         else {
00210             flags.deferred_send = 1;
00211             start_periodic_rxing(0x70);  // deferred send
00212         }
00213     } else
00214         _send_();
00215 
00216 } // ..txBuf_send()
00217 
00218 #ifdef MESH_DEBUG
00219 bool rx_log_disable;
00220 char rx_log[768];
00221 volatile unsigned rx_log_buf_idx;
00222 int _rx_log_printf(const char *format, ...)
00223     {
00224     va_list aptr;
00225     int ret = -1;
00226 
00227     va_start(aptr, format);
00228     if (!rx_log_disable) {
00229         ret = vsprintf(rx_log + rx_log_buf_idx , format, aptr);
00230         rx_log_buf_idx += ret;
00231         if (rx_log_buf_idx >= sizeof(rx_log)-1) {
00232             pc.printf("\e[31mrx_log_overrun\e[0m ");
00233             rx_log_disable = true;
00234         }
00235     }
00236     va_end(aptr);
00237 
00238 
00239     return ret;
00240 }
00241 
00242 void rx_log_print()
00243 {
00244     rx_log[rx_log_buf_idx+1] = 0;
00245     pc.printf(rx_log);
00246     rx_log_buf_idx = 0;
00247 }
00248 #endif /* MESH_DEBUG */
00249 
00250 static void txAns(unsigned sending_id)
00251 {
00252     unsigned elapsedAlready;
00253     int pad_us;
00254     txBuf_idx = 0; // previously sent request no longer needed
00255     txBuf[txBuf_idx++] = CMD_ANS;
00256     txBuf[txBuf_idx++] = reqFlags.bits.txAns;
00257     setPreambleSize(false, 6); // sending answer
00258     tx_dest_id = sending_id;
00259     elapsedAlready = Radio::lpt.read_us() - Radio::irqAt;
00260     pad_us = (ANS_PAD_MS * 1000) - elapsedAlready;
00261     if (pad_us > 100) {
00262         wait_us(pad_us);    // short wait time more accurate in busy-loop
00263     }
00264     txBuf_send(false);
00265     if (pad_us <= 0) {
00266         pc.printf("\e[41mLATE:%d ", pad_us);
00267         pc.printf("\e[0m ");
00268     }
00269 
00270 #ifdef MESH_DEBUG
00271     // printing of rx_log was deferred until this answer sent
00272     rx_log_print();
00273 #endif /* MESH_DEBUG */
00274 }
00275 
00276 uint16_t getu16FromBuf(const uint8_t* in)
00277 {
00278     uint16_t ret;
00279 
00280     ret = in[1];
00281     ret <<= 8;
00282     ret |= in[0];
00283 
00284     return ret;
00285 }
00286 
00287 void putu16ToBuf(uint8_t* out, uint16_t v)
00288 {
00289     *out++ = v & 0xff;
00290     v >>= 8;
00291     *out = v & 0xff;
00292 }
00293 
00294 void putu32ToBuf(uint8_t* out, uint32_t v)
00295 {
00296     /* most significant last */
00297     /* least significant first */
00298     *out++ = v & 0xff;
00299     v >>= 8;
00300     *out++ = v & 0xff;
00301     v >>= 8;
00302     *out++ = v & 0xff;
00303     v >>= 8;
00304     *out = v & 0xff;
00305 }
00306 
00307 uint32_t getu32FromBuf(const uint8_t* in)
00308 {
00309     uint32_t ret;
00310 
00311     ret = in[3];
00312     ret <<= 8;
00313     ret |= in[2];
00314     ret <<= 8;
00315     ret |= in[1];
00316     ret <<= 8;
00317     ret |= in[0];
00318 
00319     return ret;
00320 }
00321 
00322 
00323 
00324 struct _fwd_ fwd;
00325 struct _nr_ notResponding;
00326 
00327 void txDoneCB()
00328 {
00329     if (flags.getAns) {
00330         unsigned toms;
00331         setPreambleSize(false, 5);    // getting answer
00332         Radio::Rx(0);
00333 #ifndef GATEWAY
00334         if (reqFlags.bits.currentOp == CMD_DISCOVERY_REQ) {
00335             /* discovering: listen for answers from any upstream devices */
00336             toms = discovery_ans_time_total_us / 1000;
00337             queue.call_in(toms, discovery_rx_end);
00338         } else
00339 #endif /* !GATEWAY */
00340         {
00341             unsigned target_us;
00342             target_us = Radio::lora_toa_us(ANS_SIZE_BYTE) * 4;  // four packet length's worth
00343             toms = (ANS_PAD_MS + ANS_PAD_MS) + (target_us / 1000); // microseconds to milliseconds
00344             req_timeout_id = queue.call_in(toms, txBuf_send, true);
00345         }
00346     } else if (reqFlags.bits.txAns != NOT_ANSWERING) { // txDone callback answer-tx-complete
00347         /* we just sent answer: restore to idle condition waiting for wakeup packet */
00348         reqFlags.bits.txAns = NOT_ANSWERING;
00349         txBuf_idx = 0;
00350 
00351 #ifndef GATEWAY
00352         if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ || reqFlags.bits.currentOp == CMD_USER_PAYLOAD_DN_REQ) {
00353             int n;
00354             if (fwd.len >= 0) {
00355                 tx_dest_id = fwd.tx_dest_id;
00356                 if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ) {
00357                     /* forward upstream */
00358                     txBuf[txBuf_idx++] = CMD_USER_PAYLOAD_UP_REQ;
00359                     putu32ToBuf(&txBuf[txBuf_idx], fwd.B_id);   // originating_src_id
00360                     txBuf_idx += 4;
00361                 } else if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_DN_REQ) {
00362                     /* forward downstream */
00363                     txBuf[txBuf_idx++] = CMD_USER_PAYLOAD_DN_REQ;
00364                     putu32ToBuf(&txBuf[txBuf_idx], fwd.A_id);    // final_dest_id
00365                     txBuf_idx += 4;
00366                 }
00367                 txBuf[txBuf_idx++] = fwd.len;
00368                 for (n = 0; n < fwd.len; n++)
00369                     txBuf[txBuf_idx++] = fwd.buf[n];
00370 
00371                 queue.call_in(500, txBuf_send, true);
00372                 fwd.len = -1;
00373             } // ..if (fwd.len >= 0)
00374             else {
00375                 /* uplink/downlink not forwarding */
00376                 if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ)
00377                     app_uplink_complete();
00378                 reqFlags.bits.currentOp = CMD_UNUSED;
00379             }
00380         }
00381 
00382         if (attUp.id == ID_NONE) {
00383             /* disconnected from upstream, rediscover */
00384             queue.call_in(1000, upstream_init);
00385         } else if (id_newDeviceNotification != ID_NONE) {
00386             upstream_new_device_notify();
00387         } else if (reqFlags.bits.currentOp == CMD_DOWNSTREAM_NOT_RESPONDING) {
00388             tx_dest_id = attUp.id;
00389             txBuf[txBuf_idx++] = CMD_DOWNSTREAM_NOT_RESPONDING;
00390             putu32ToBuf(&txBuf[txBuf_idx], notResponding.reporting_id);
00391             txBuf_idx += 4;
00392             putu32ToBuf(&txBuf[txBuf_idx], notResponding.device_not_respoding_id);
00393             txBuf_idx += 4;
00394             queue.call_in(500, txBuf_send, true);
00395         } else
00396 #endif /* !GATEWAY */
00397         if (downRemove.destID != ID_NONE) {
00398             request_remove_device();
00399         }
00400         start_periodic_rxing(0x60); // reqFlags.bits.txAns != NOT_ANSWERING
00401     } else if (reqFlags.bits.currentOp == CMD_DISCOVERY_ANS) {
00402         if (flags.firstDiscoverAns) {
00403             unsigned rnd = Radio::Random() % N_HALF_DISCOVERY_ANS;
00404             queue.call_in((discovery_ans_time_step_us * rnd) / 1000, txBuf_send, false);
00405             flags.firstDiscoverAns = 0;
00406         } else {
00407             reqFlags.bits.currentOp = CMD_UNUSED;
00408             txBuf_idx = 0;
00409             //start_periodic_rxing(0x50); // 2nd discoverAns
00410         }
00411     } else {
00412         /* ? wtf did we just transmit ? */
00413         pc.printf("\e[31mnoTxAns_or_STATE_GET_ANS\e[0m ");
00414     }
00415 } // ..txDoneCB()
00416 
00417 
00418 uint16_t crc16( uint8_t *buffer, uint16_t length )
00419 {
00420     uint16_t i;
00421     // The CRC calculation follows CCITT
00422     const uint16_t polynom = 0x1021;
00423     // CRC initial value
00424     uint16_t crc = 0x0000;
00425  
00426     if( buffer == NULL )
00427     {
00428         return 0;
00429     }
00430  
00431     for( i = 0; i < length; ++i )
00432     {
00433         uint16_t j;
00434         crc ^= ( uint16_t ) buffer[i] << 8;
00435         for( j = 0; j < 8; ++j )
00436         {
00437             crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
00438         }
00439     }
00440  
00441     return crc;
00442 }
00443 
00444 
00445 void rxDoneCB(uint8_t size, float rssi, float snr)
00446 {
00447     uint8_t rx_buf_idx;
00448     uint16_t calc, rxCrc;
00449     uint8_t rx_hfg = Radio::radio.rx_buf[0];
00450     uint32_t sending_id = getu32FromBuf(&Radio::radio.rx_buf[1]);
00451     uint32_t dest_id = getu32FromBuf(&Radio::radio.rx_buf[5]);
00452 #ifdef GATEWAY
00453     upInfo_t up_info;
00454     up_info.originating_src_id = ID_NONE;
00455 #endif /* GATEWAY */
00456 
00457 #ifdef MESH_DEBUG
00458     bool print_log_here = true;
00459     rx_log_buf_idx = 0;
00460     rx_log[0] = 0;
00461     rx_log_disable = false;
00462 #endif /* MESH_DEBUG */
00463 
00464     if (flags.vacantCheck) {
00465         channelVacantCount = 0;
00466         flags.vacantCheck = 0;
00467     }
00468 
00469     Rx_log_printf("\e[32mrxDone %ubytes %.1fdBm %.1fdB\e[0m ", size, rssi, snr);
00470     Rx_log_printf("from:%lx_to_%lx ", sending_id, dest_id);
00471 
00472     if (dest_id != my_id && dest_id != ANY_ID) {
00473 #ifndef GATEWAY
00474         /* check if upstream device were attached to is re-attaching */
00475         upstream_attached_check(sending_id);
00476 #endif /* !GATEWAY */
00477         if (!flags.discoverAnswering)
00478             start_periodic_rxing(0x40); // rxDone notForMe
00479         goto done;
00480     }
00481 
00482     calc = crc16(Radio::radio.rx_buf, size-2);
00483     rxCrc = getu16FromBuf(&Radio::radio.rx_buf[size-2]);
00484     if (calc != rxCrc) {
00485         Rx_log_printf("%04x != %04x\r\n", calc, rxCrc);
00486         if (!flags.discoverAnswering)
00487             start_periodic_rxing(0x30); // rxDone crcfail
00488         goto done;
00489     }
00490 
00491     size -= 2;  // take off trailing crc
00492     for (rx_buf_idx = 9; rx_buf_idx < size; ) {
00493         cmd_e cmd = (cmd_e)Radio::radio.rx_buf[rx_buf_idx++];
00494 
00495         Rx_log_printf(" curOp:%u_", reqFlags.bits.currentOp);
00496         Rx_log_printf(" \e[7mRxCmd:%s\e[0m", cmdStrs[cmd]);
00497         Rx_log_printf(" ");
00498             
00499         switch (cmd) {
00500             ans_e ans;
00501             case CMD_ANS:
00502                 ans = (ans_e)Radio::radio.rx_buf[rx_buf_idx++];
00503                 /* request as been answered successfully */
00504                 Rx_log_printf("\e[35mrxAns\e[0m ");
00505                 if (flags.getAns) {
00506                     if (ans == ANSWER_OK) {
00507                         txBuf_idx = 0;
00508                         queue.cancel(req_timeout_id);
00509                         attemptCnt = 0;
00510                         downstream_ans_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd);
00511 #ifndef GATEWAY
00512                         upstream_ans_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd);
00513 #endif /* !GATEWAY */
00514 
00515                         if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_UP_REQ) {
00516                             fwd.len = -1;
00517                             reqFlags.bits.currentOp = CMD_UNUSED;
00518                         }
00519 
00520                         if (reqFlags.bits.currentOp == CMD_USER_PAYLOAD_DN_REQ) {
00521                             fwd.len = -1;
00522                             reqFlags.bits.currentOp = CMD_UNUSED;
00523                         }
00524                     }
00525                 }
00526 
00527                 break;
00528             case CMD_UNUSED:
00529                 break;
00530             case CMD_DISCOVERY_REQ:
00531             case CMD_ATTACH_REQ:
00532             case CMD_NEW_DEVICE_ATTACHED_REQ:
00533             case CMD_USER_PAYLOAD_UP_REQ:
00534             case CMD_DOWNSTREAM_NOT_RESPONDING:
00535 #ifdef GATEWAY
00536                 downstream_req_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd, &up_info);
00537 #else
00538                 downstream_req_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd);
00539 #endif
00540                 break;
00541             case CMD_REMOVE_DEVICE_REQ:
00542             case CMD_DISCOVERY_ANS:
00543             case CMD_USER_PAYLOAD_DN_REQ:
00544                 upstream_req_rxDoneCB(rssi, snr, &rx_buf_idx, sending_id, cmd);
00545                 break;
00546         } // ..switch (cmd)
00547 
00548     } // ..for (rx_buf_idx = 9; rx_buf_idx < size; )
00549 
00550     if (reqFlags.bits.currentOp != CMD_DISCOVERY_ANS && reqFlags.bits.currentOp != CMD_DISCOVERY_REQ) {
00551         if (reqFlags.bits.txAns != NOT_ANSWERING) {
00552             queue.call(txAns, sending_id);  // must return from this function prior to transmitting
00553 #ifdef MESH_DEBUG
00554             print_log_here = false;
00555 #endif /* MESH_DEBUG */
00556         } else {
00557             Radio::Sleep();
00558             start_periodic_rxing(0x20); // rxDone
00559         }
00560     }
00561 
00562 done:
00563 #ifdef GATEWAY
00564     if (rx_hfg == 0) {
00565         Rx_log_printf("\e[31mrx_hfg:%u\e[0m ", rx_hfg);   /* another gateway */
00566     } else {
00567         Rx_log_printf("rx_hfg:%u ", rx_hfg);
00568     }
00569 #else
00570     Rx_log_printf("rx_hfg:%u ", rx_hfg);
00571     /* compare against attached upstream */
00572     if (hops_from_gateway != HFG_UNATTACHED)
00573         upstream_signal_check(rssi, snr, rx_hfg, sending_id);
00574 #endif
00575 
00576 #ifdef MESH_DEBUG
00577     if (print_log_here)
00578         queue.call_in(10, rx_log_print);
00579     // else txAns must be completed first, will be called from txAns
00580 #endif /* MESH_DEBUG */
00581 
00582 #ifdef GATEWAY
00583     if (up_info.originating_src_id != ID_NONE) {
00584         /* txAns takes priority over application layer */
00585         queue.call(gateway_uplink, up_info.len, up_info.originating_src_id, &Radio::radio.rx_buf[up_info.rxBufIdx]);
00586     }
00587 #endif /* GATEWAY */
00588 } // ..rxDoneCB()
00589 
00590 void txTimeoutCB()
00591 {
00592     pc.printf("\e[41mTxTimeout\e[0m\r\n");
00593 }
00594 
00595 void rxTimeoutCB()
00596 {
00597     Radio::Sleep();
00598     queue.call_in(WAKEUP_INTERVAL_MS, rxSingle);
00599 
00600     if (flags.vacantCheck) {
00601         channelVacantCount++;
00602         if (flags.deferred_send) {
00603             uint8_t vc_thresh = CHANNEL_VACANT_REQUIRED_COUNT;
00604             if (attemptCnt > 1) {
00605                 /* retry backoff */
00606                 vc_thresh += Radio::Random() % CHANNEL_VACANT_REQUIRED_COUNT;
00607             }
00608             if (channelVacantCount > vc_thresh) {
00609                 _send_();
00610                 flags.deferred_send = 0;
00611             }
00612         }
00613         flags.vacantCheck = 0;
00614     }
00615 }
00616 
00617 uint32_t find_dest_id(uint32_t reqid)
00618 {
00619     lid_list_t* L;
00620     for (L = attachedDevices; L != NULL; L = L->next) {
00621         if (L->id == reqid)
00622             return L->id;   // is locally attached
00623     }
00624 
00625     for (L = attachedDevices; L != NULL; L = L->next) {
00626         if (L->attachedList != NULL) {
00627             cid_list_t* children;
00628             for (children = L->attachedList;  children != NULL; children = children->next) {
00629                 if (children->id == reqid)
00630                     return L->id;
00631             }
00632         }
00633     }
00634     return ID_NONE;
00635 }
00636 
00637 void cmd_tx(uint8_t argsAt)
00638 {
00639     unsigned symbs;
00640     if (sscanf(pcbuf+argsAt, "%u", &symbs) == 1) {
00641         Radio::LoRaPacketConfig(symbs, false, true, false);  // preambleLen, fixLen, crcOn, invIQ
00642         pc.printf("txing %u symbols\r\n", symbs);
00643     }
00644 
00645     txBuf[txBuf_idx++] = CMD_UNUSED;
00646     tx_dest_id = ANY_ID;
00647     txBuf_send(false);
00648 }
00649 
00650 void cmd_list_devices(uint8_t argsAt)
00651 {
00652     lid_list_t* L;
00653     pc.printf("my_id:%lx  hops_from_gateway:%u\r\n", my_id, hops_from_gateway);
00654     for (L = attachedDevices; L != NULL; L = L->next) {
00655         pc.printf("%lx", L->id);
00656         if (L->attachedList != NULL) {
00657             cid_list_t* children;
00658             pc.printf(": ");
00659             for (children = L->attachedList;  children != NULL; children = children->next)
00660                 pc.printf("%lx ", children->id);
00661         }
00662         pc.printf("\r\n");
00663     }
00664 }
00665 
00666 void cmd_print_status(uint8_t idx)
00667 {
00668     radio_print_status();
00669 
00670     pc.printf("my_id:%lx  hops_from_gateway:%u\r\n", my_id, hops_from_gateway);
00671     pc.printf("ClearChan%u reqFlags:%02x ", channelVacantCount, reqFlags.octet);
00672 #ifndef GATEWAY
00673     upstream_print_status();
00674 #endif /* GATEWAY */
00675     pc.printf("\r\n");
00676 }
00677 
00678 typedef struct {
00679     const char* const cmd;
00680     void (*handler)(uint8_t args_at);
00681     const char* const arg_descr;
00682     const char* const description;
00683 } menu_item_t;
00684 
00685 void cmd_help(uint8_t);
00686 
00687 const menu_item_t menu_items[] =   {
00688     { ".", cmd_print_status, "","print status"},
00689     { "op", cmd_op, "%u","get/set tx power"},
00690 #ifdef GATEWAY
00691     { "dl", cmd_downlink, "%x %x...","send downlink <destIDhex> <payload bytes hex>"},
00692 #endif /* GATEWAY */
00693     { "ls", cmd_list_devices, "%u","list seen downstream devices"},
00694     { "tx", cmd_tx, "%u","tx test preamble length"},
00695     { "?", cmd_help, "","this list of commands"},
00696     { NULL, NULL, NULL, NULL }
00697 };
00698 
00699 void cmd_help(uint8_t args_at)
00700 {
00701     int i;
00702     
00703     for (i = 0; menu_items[i].cmd != NULL ; i++) {
00704         printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description);
00705     }
00706 }
00707 
00708 void console()
00709 {
00710     uint8_t i, user_cmd_len;
00711  
00712     if (pcbuf_len == 0)
00713         return;
00714         
00715     printf("\r\n");
00716         
00717     /* get end of user-entered command */
00718     user_cmd_len = 1;   // first character can be any character
00719     for (i = 1; i <= pcbuf_len; i++) {
00720         if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') {
00721             user_cmd_len = i;
00722             break;
00723         }
00724     }
00725  
00726     for (i = 0; menu_items[i].cmd != NULL ; i++) {
00727         int mi_len = strlen(menu_items[i].cmd);
00728         if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) {
00729             while (pcbuf[mi_len] == ' ')   // skip past spaces
00730                 mi_len++;
00731             menu_items[i].handler(mi_len);
00732             break;
00733         }
00734     }
00735    
00736     pcbuf_len = 0;
00737     printf("> ");
00738     fflush(stdout); 
00739 }
00740 
00741 void radio_irq_topHalf()
00742 {
00743     /* isr context -> main loop context */
00744     queue.call(Radio::service);
00745 }
00746 
00747 const RadioEvents_t rev = {
00748     /* DioPin_top_half */     radio_irq_topHalf,
00749     /* TxDone_topHalf */    NULL,
00750     /* TxDone_botHalf */    txDoneCB,
00751     /* TxTimeout  */        txTimeoutCB,
00752     /* RxDone  */           rxDoneCB,
00753     /* RxTimeout  */        rxTimeoutCB,
00754     /* RxError  */          NULL,
00755     /* FhssChangeChannel  */NULL,
00756     /* CadDone  */          NULL
00757 };
00758 
00759 void rx_callback()
00760 {
00761     static uint8_t pcbuf_idx = 0;
00762     static uint8_t prev_len = 0;
00763     char c = pc.getc();
00764     if (c == 8) {
00765         if (pcbuf_idx > 0) {
00766             pc.putc(8);
00767             pc.putc(' ');
00768             pc.putc(8);
00769             pcbuf_idx--;
00770         }
00771     } else if (c == 3) {    // ctrl-C
00772         pcbuf_len = -1;
00773     } else if (c == '\r') {
00774         if (pcbuf_idx == 0) {
00775             pcbuf_len = prev_len;
00776         } else {
00777             pcbuf[pcbuf_idx] = 0;   // null terminate
00778             prev_len = pcbuf_idx;
00779             pcbuf_idx = 0;
00780             pcbuf_len = prev_len;
00781         }
00782         queue.call(console);
00783     } else if (pcbuf_idx < sizeof(pcbuf)) {
00784         pcbuf[pcbuf_idx++] = c;
00785         pc.putc(c);
00786     }
00787 }
00788 
00789 int main()
00790 {
00791     pc.baud(115200);
00792     pc.printf("\r\nreset\r\n");
00793     pc.attach(rx_callback);
00794 
00795     {
00796         uint32_t u32;
00797 #ifdef TARGET_FAMILY_STM32 
00798         u32 = LL_GetUID_Word0();
00799         u32 <<= 2;
00800         u32 ^= LL_GetUID_Word1();
00801         u32 ^= LL_GetUID_Word2();
00802 #else
00803 #error TODO_nSTM32
00804 #endif
00805         my_id = u32;
00806         pc.printf("my_id %lx\r\n", my_id);
00807     }
00808 
00809     wait_ms(200);    // power stabilization from cold-reset
00810     Radio::Init(&rev);
00811 
00812     rxdbg = 0;
00813     Radio::Standby();
00814     Radio::LoRaModemConfig(BW_KHZ, SPREADING_FACTOR, 1);
00815     Radio::SetChannel(CF_MHZ * 1000000);
00816     Radio::set_tx_dbm(TX_DBM);
00817 #ifdef SX126x_H 
00818     {
00819         status_t status;
00820         uint8_t stopOnPreamble = 1;
00821 
00822         Radio::radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet);
00823         wait_ms(20);
00824         Radio::radio.xfer(OPCODE_STOP_TIMER_ON_PREAMBLE, 1, 0, &stopOnPreamble);
00825     }
00826 #endif /* SX126x_H  */
00827 
00828     setPreambleSize(false, 4); //init
00829     {
00830         unsigned daDur = Radio::lora_toa_us(DISCOVERY_ANS_LENGTH);
00831         discovery_ans_time_step_us = daDur + (ANS_PAD_MS * 1000); // + padding for receiver handling
00832         discovery_ans_time_total_us = discovery_ans_time_step_us * N_DISCOVERY_ANS;
00833     }
00834 
00835 #ifdef SX128x_H 
00836     /* C preprocess doesnt do floating point */
00837     if (N_PRE_SYMB > 255) {
00838         pc.printf("\e[41mlong preamble oversized %.1f\e[0m\r\n", N_PRE_SYMB);
00839     }
00840 #endif /* ..SX128x_H */
00841 
00842 #ifdef GATEWAY
00843     start_periodic_rxing(0x10);  // gateway startup
00844 #else
00845     init_attached_upstream();
00846     hops_from_gateway = HFG_UNATTACHED;
00847     upstream_init();
00848 #endif
00849 
00850     app_init();
00851     fwd.len = -1;
00852 
00853 /*    if (!sleep_manager_can_deep_sleep()) {
00854         sleep_manager_unlock_deep_sleep();
00855         pc.printf("unLockDeepSleep\r\n");
00856     }*/
00857 
00858     queue.dispatch();
00859 } // ..main()
00860