Synchronous wireless star LoRa network, central device.

Dependencies:   SX127x sx12xx_hal

radio chip selection

Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.


Alternate to this project gateway running on raspberry pi can be used as gateway.

LoRaWAN on single radio channel

Synchronous Star Network

This project acts as central node for LoRaWAN-like network operating on single radio channel. Intended for use where network infrastructure would never exist due to cost and/or complexity of standard network. This project uses the class-B method of beacon generation to synchronize the end nodes with the gateway. OTA mode must be used. End-node will be allocated an uplink time slot upon joining. End node may transmit uplink at this assigned timeslot, if it desires to do so. This time slot is always referenced to the beacon sent by gateway.

LoRaWAN server is not necessary. All network control is implemented by this project. User can observe network activity on the mbed serial port. Downlinks can be scheduled using command on serial port.

This implementation must not be used on radio channels requiring duty-cycle transmit limiting.

alterations from LoRaWAN specification

This mode of operation uses a single datarate on single radio channel. ADR is not implemented, because datarate cannot be changed. OTA mode must be used. When join-accept is sent by gateway, it will have appended (instead of CFlist) the beacon timing answer to inform of when next beacon occurs, and two timing values: the time slot allocated to this end-node and the periodicity of the network. Periodicity means how often the end-node may again transmit. /media/uploads/dudmuck/class-b-single.png Beacon is sent for purpose of providing timing reference to end-nodes. The beacon payload may contain a broadcast command to end nodes. Time value is not sent in beacon payload. The time at which beacon is sent provides timing reference: every 128 seconds as standard.

Rx2 receive window is not implemented. Only Rx1 is used because a single radio channel is used. Rx1 delay is reduced to 100 milliseconds. Original LoRaWAN has 1000 millisecond Rx1 delay to accommodate internet latency.

LoRaWAN standard class-B beacon requires GPS timing reference. This implementation does not use GPS, instead a hardware timer peripheral generates interrupts to send beacons. Absolute accuracy is not required, only relative crystal drift between gateway and end-nodes is considered.

Timing values are always specified as 30ms per step as in LoRaWAN standard. Each beacon period has 4096 30ms steps per beacon period.

join OTA procedure

The join procedure has changed the join-accept delay to 100 milliseconds (standard is 5 seconds). This allows end-node to hunt for gateway on several channels during joining. When gateway starts, it will scan available channels for the optimal choice based on ambient noise on the channels. End node will retry join request until it finds the gateway. Gateway might change channel during operation if it deems current channel too busy.

configuration of network

End nodes must be provisioned by editing file Comissioning.h. The array motes lists every end node permitted on network. It contains appEui, devEUI and appKey just as specified in standard LoRaWAN. All provisioning is hard-coded; changing provisioning requires reprogramming gateway. When changing number of motes, N_MOTES definition must be changed in lorawan.h.

lorawan.h

#define N_MOTES     8
extern ota_mote_t motes[N_MOTES];   /* from Comissioning.h */

configuring number of end-nodes vs transmit rate

Trade-off can be selected between number of end-nodes on network vs. how often each end-node can transmit.
In this example, where DR_13 is SF7 at 500KHz:

lorawan.cpp

    #elif (LORAMAC_DEFAULT_DATARATE == DR_13)
        #define TX_SLOT_STEPPING        8  //approx 0.25 seconds
        #define PERIODICITY_SLOTS       (TX_SLOT_STEPPING * 6)
    #endif

Here, each end-node is given time of 240ms = 8 * 30ms; accommodating maximum payload length for both uplink and downlink.
6 in this code is the maximum count of end nodes using this gateway. Each end-node can transmit every 1.44 seconds, in this example.
If you wanted to change 6 to 20 end-nodes, each would be able to use network every 4.8 seconds.
Another example: If you wanted to use DR_12 = SF8, you would have approx 2.5 to 3dB more gain compared to SF7, but each end-node must be given double time, resulting in 20 nodes able to use network every 9.6 seconds at DR_12.

network capacity limitation

The number of end-nodes which can be supported is determined by number of SLOT_STEPPING's which can occur during BEACON_PERIOD. Beacon guard is implemented same as standard LoRaWAN, which is 3 seconds prior to beacon start and 2.12 seconds after beacon start, which gives 122.88 seconds for network traffic for each beacon period.

gateway configuration

spreading factor is declared at #define LORAMAC_DEFAULT_DATARATE in lorawan.h, valid rates are DR_8 to DR_13 (sf12 to sf7). In the end-node, the same definition must be configured in LoRaMac-definitions.h. This network operates at this constant datarate for all packets.
Band plan can be selected by defining USE_BAND_* in lorawan.h. 434MHz can be used on SX1276 shield. TypeABZ module and sx1272 only support 800/900MHz channels band.

end-node provisioning

Security permits only matching DevEUI / AppEui to join the network, due to unique AES root key for each end device; in this case the DevEUI must be programmed in advance into gateway. However, if the same AES root key could be used for each end device , then any number of end devices could be added at a later time, without checking DevEUI on the gateway when an end device joins the network. On the gateway, the end device join acceptance is performed in file lorawan.cpp LoRaWan::parse_receive() where MType == MTYPE_JOIN_REQ. A memcmp() is performed on both DevEUI and AppEUI.

If you wish to allow any DevEUI to join, define ANY_DEVEUI at top of lorawan.cpp . In this configuration, all end devices must use same AppEUI and AES key. N_MOTES must be defined to the maximum number of end devices expected. Test end device by commenting BoardGetUniqueId() in end node, and replace DevEui[] with 8 arbitrary bytes to verify gateway permits any DevEUI to join.

RAM usage

For gateway CPU, recommend to consider larger RAM size depending on number of end devices required. ota_motes_t has size of 123 bytes. Each end device has one of these, however if less RAM usage is required for more end-devices, the MAC command queue size may be reduced.

hardware support

The radio driver supports both SX1272 and SX1276, sx126x kit, sx126x radio-only shield, and sx128x 2.4GHz.. The selection of radio chip type is made by your choice of importing radio driver library.



Beacon generation requires low power ticker to be clocked from crystal, not internal RC oscillator.

Gateway Serial Interface

Gateway serial port operates at 115200bps.

commandargumentdescription
list-list joined end nodes
?-list available commands
dl<mote-hex-dev-addr> <hex-payload>send downlink, sent after uplink received
gpo<mote-hex-dev-addr> <0 or 1>set output PC6 pin level on end device
b32bit hex valueset beacon payload to be sent at next beacon
. (period)-print current status
opdBmconfigure TX power of gateway
sbcountskip sending beacons, for testing end node
fhex devAddrprinter filtering, show only single end node
hm-print filtering, hide MAC layer printing
hp-print filtering, hide APP layer printing
sa-print filtering, show all, undo hm and hp

Any received uplink will be printed with DevAddr and decrypted payload.

Committer:
dudmuck
Date:
Mon Jun 19 23:50:43 2017 +0000
Revision:
8:307f7faeb594
Parent:
5:ceacaa560cfd
Child:
9:a0ce66c18ec0
user button generates broadcast payload for beacon

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dudmuck 0:2ff18de8d48b 1 #include "lorawan.h"
dudmuck 0:2ff18de8d48b 2 #include "tim.h"
dudmuck 8:307f7faeb594 3 #include "commands.h"
dudmuck 0:2ff18de8d48b 4
dudmuck 2:9628d5e4b1bf 5
dudmuck 4:7e743e402681 6 RawSerial pc(USBTX, USBRX);
dudmuck 0:2ff18de8d48b 7 Timer timer;
dudmuck 0:2ff18de8d48b 8
dudmuck 5:ceacaa560cfd 9 #if defined(TARGET_DISCO_L072CZ_LRWAN1)
dudmuck 2:9628d5e4b1bf 10 SPI spi(PA_7, PA_6, PB_3); // mosi, miso, sclk
dudmuck 2:9628d5e4b1bf 11 // dio0, dio1, nss, spi, rst
dudmuck 2:9628d5e4b1bf 12 SX127x radio(PB_4, PB_1, PA_15, spi, PC_0); // sx1276 arduino shield
dudmuck 2:9628d5e4b1bf 13
dudmuck 2:9628d5e4b1bf 14 #define CRF1 PA_1
dudmuck 2:9628d5e4b1bf 15 #define CRF2 PC_2
dudmuck 2:9628d5e4b1bf 16 #define CRF3 PC_1
dudmuck 2:9628d5e4b1bf 17 DigitalOut Vctl1(CRF1);
dudmuck 2:9628d5e4b1bf 18 DigitalOut Vctl2(CRF2);
dudmuck 2:9628d5e4b1bf 19 DigitalOut Vctl3(CRF3);
dudmuck 2:9628d5e4b1bf 20
dudmuck 2:9628d5e4b1bf 21 void rfsw_callback()
dudmuck 2:9628d5e4b1bf 22 {
dudmuck 2:9628d5e4b1bf 23 if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER) {
dudmuck 2:9628d5e4b1bf 24 Vctl1 = 0;
dudmuck 2:9628d5e4b1bf 25 if (radio.RegPaConfig.bits.PaSelect) {
dudmuck 2:9628d5e4b1bf 26 Vctl2 = 0;
dudmuck 2:9628d5e4b1bf 27 Vctl3 = 1;
dudmuck 2:9628d5e4b1bf 28 } else {
dudmuck 2:9628d5e4b1bf 29 Vctl2 = 1;
dudmuck 2:9628d5e4b1bf 30 Vctl3 = 0;
dudmuck 2:9628d5e4b1bf 31 }
dudmuck 2:9628d5e4b1bf 32 } else {
dudmuck 2:9628d5e4b1bf 33 if (radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER || radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER_SINGLE)
dudmuck 2:9628d5e4b1bf 34 Vctl1 = 1;
dudmuck 2:9628d5e4b1bf 35 else
dudmuck 2:9628d5e4b1bf 36 Vctl1 = 0;
dudmuck 2:9628d5e4b1bf 37
dudmuck 2:9628d5e4b1bf 38 Vctl2 = 0;
dudmuck 2:9628d5e4b1bf 39 Vctl3 = 0;
dudmuck 2:9628d5e4b1bf 40 }
dudmuck 2:9628d5e4b1bf 41 }
dudmuck 2:9628d5e4b1bf 42 #else
dudmuck 2:9628d5e4b1bf 43 SPI spi(D11, D12, D13); // mosi, miso, sclk
dudmuck 2:9628d5e4b1bf 44 // dio0, dio1, nss, spi, rst
dudmuck 2:9628d5e4b1bf 45 SX127x radio( D2, D3, D10, spi, A0); // sx1276 arduino shield
dudmuck 2:9628d5e4b1bf 46
dudmuck 2:9628d5e4b1bf 47 DigitalInOut rfsw(A4);
dudmuck 2:9628d5e4b1bf 48
dudmuck 2:9628d5e4b1bf 49 void rfsw_callback()
dudmuck 2:9628d5e4b1bf 50 {
dudmuck 2:9628d5e4b1bf 51 if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER)
dudmuck 2:9628d5e4b1bf 52 rfsw = 1;
dudmuck 2:9628d5e4b1bf 53 else
dudmuck 2:9628d5e4b1bf 54 rfsw = 0;
dudmuck 2:9628d5e4b1bf 55 }
dudmuck 2:9628d5e4b1bf 56 #endif
dudmuck 0:2ff18de8d48b 57
dudmuck 0:2ff18de8d48b 58 char pcbuf[64];
dudmuck 0:2ff18de8d48b 59 int pcbuf_len;
dudmuck 0:2ff18de8d48b 60 unsigned int beacon_payload;
dudmuck 0:2ff18de8d48b 61
dudmuck 0:2ff18de8d48b 62 unsigned int skip_beacon_cnt;
dudmuck 0:2ff18de8d48b 63
dudmuck 8:307f7faeb594 64 /*Interrupt*/DigitalIn user_button(USER_BUTTON);
dudmuck 8:307f7faeb594 65 volatile bool but_state;
dudmuck 8:307f7faeb594 66 volatile uint8_t beacon_command_pending = BCAST_CMD_NONE;
dudmuck 8:307f7faeb594 67 volatile uint8_t prev_beacon_command = BCAST_CMD_NONE;
dudmuck 8:307f7faeb594 68 volatile int beacon_arg;
dudmuck 8:307f7faeb594 69 DigitalOut grove_led(D6);
dudmuck 0:2ff18de8d48b 70
dudmuck 0:2ff18de8d48b 71 SX127x_lora lora(radio);
dudmuck 0:2ff18de8d48b 72
dudmuck 0:2ff18de8d48b 73 #define LORAMAC_FIRST_CHANNEL ( (uint32_t)910.0e6 )
dudmuck 0:2ff18de8d48b 74 #define LORAMAC_STEPWIDTH_CHANNEL ( (uint32_t)800e3 )
dudmuck 0:2ff18de8d48b 75 #define LORA_MAX_NB_CHANNELS 8
dudmuck 0:2ff18de8d48b 76
dudmuck 0:2ff18de8d48b 77 #define N_SAMPLES 64
dudmuck 0:2ff18de8d48b 78 void channel_scan()
dudmuck 0:2ff18de8d48b 79 {
dudmuck 0:2ff18de8d48b 80 int min_ch, ch;
dudmuck 0:2ff18de8d48b 81 uint32_t hz = LORAMAC_FIRST_CHANNEL;
dudmuck 0:2ff18de8d48b 82 int acc[LORA_MAX_NB_CHANNELS];
dudmuck 0:2ff18de8d48b 83
dudmuck 0:2ff18de8d48b 84 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 85
dudmuck 0:2ff18de8d48b 86 for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
dudmuck 0:2ff18de8d48b 87 int i;
dudmuck 0:2ff18de8d48b 88 float MHz = (float)hz / 1e6;
dudmuck 0:2ff18de8d48b 89 radio.set_frf_MHz(MHz);
dudmuck 0:2ff18de8d48b 90 radio.set_opmode(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 91 acc[ch] = 0;
dudmuck 0:2ff18de8d48b 92 for (i = 0; i < N_SAMPLES; i++) {
dudmuck 0:2ff18de8d48b 93 int rssi = lora.get_current_rssi();
dudmuck 0:2ff18de8d48b 94 acc[ch] += rssi;
dudmuck 0:2ff18de8d48b 95 wait(0.01);
dudmuck 0:2ff18de8d48b 96 }
dudmuck 0:2ff18de8d48b 97 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 98 printf("ch%u: %f\r\n", ch, acc[ch] / (float)N_SAMPLES);
dudmuck 0:2ff18de8d48b 99 hz += LORAMAC_STEPWIDTH_CHANNEL;
dudmuck 0:2ff18de8d48b 100 radio.set_frf_MHz((float)hz/1e6);
dudmuck 0:2ff18de8d48b 101 }
dudmuck 0:2ff18de8d48b 102
dudmuck 0:2ff18de8d48b 103 int min = 0x7fffffff;
dudmuck 0:2ff18de8d48b 104 min_ch = 0;
dudmuck 0:2ff18de8d48b 105 for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
dudmuck 0:2ff18de8d48b 106 if (acc[ch] < min) {
dudmuck 0:2ff18de8d48b 107 min = acc[ch];
dudmuck 0:2ff18de8d48b 108 min_ch = ch;
dudmuck 0:2ff18de8d48b 109 }
dudmuck 0:2ff18de8d48b 110 }
dudmuck 0:2ff18de8d48b 111 hz = LORAMAC_FIRST_CHANNEL + (min_ch * LORAMAC_STEPWIDTH_CHANNEL);
dudmuck 0:2ff18de8d48b 112 printf("using ch%u, %luhz\r\n", min_ch, hz);
dudmuck 0:2ff18de8d48b 113 radio.set_frf_MHz((float)hz/1e6);
dudmuck 0:2ff18de8d48b 114 }
dudmuck 0:2ff18de8d48b 115
dudmuck 0:2ff18de8d48b 116 void init_radio()
dudmuck 0:2ff18de8d48b 117 {
dudmuck 0:2ff18de8d48b 118 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 119
dudmuck 0:2ff18de8d48b 120 radio.RegPaConfig.bits.OutputPower = 15;
dudmuck 0:2ff18de8d48b 121 radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
dudmuck 0:2ff18de8d48b 122 lora.enable();
dudmuck 0:2ff18de8d48b 123 lora.setBw_KHz(500);
dudmuck 0:2ff18de8d48b 124 lora.setSf(LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);
dudmuck 0:2ff18de8d48b 125 printf("using sf%u\r\n", LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);
dudmuck 0:2ff18de8d48b 126
dudmuck 0:2ff18de8d48b 127 channel_scan();
dudmuck 0:2ff18de8d48b 128
dudmuck 0:2ff18de8d48b 129 radio.write_reg(REG_LR_SYNC_BYTE, LORA_MAC_PUBLIC_SYNCWORD);
dudmuck 0:2ff18de8d48b 130 radio.write_reg(REG_LR_RX_MAX_PAYLOADLENGTH, 255);
dudmuck 0:2ff18de8d48b 131 }
dudmuck 0:2ff18de8d48b 132
dudmuck 0:2ff18de8d48b 133 void printLoraIrqs(bool clear)
dudmuck 0:2ff18de8d48b 134 {
dudmuck 0:2ff18de8d48b 135 printf("\r\nIrqFlags:");
dudmuck 0:2ff18de8d48b 136 if (lora.RegIrqFlags.bits.CadDetected)
dudmuck 0:2ff18de8d48b 137 printf("CadDetected ");
dudmuck 0:2ff18de8d48b 138 if (lora.RegIrqFlags.bits.FhssChangeChannel) {
dudmuck 0:2ff18de8d48b 139 printf("FhssChangeChannel:%d ", lora.RegHopChannel.bits.FhssPresentChannel);
dudmuck 0:2ff18de8d48b 140 }
dudmuck 0:2ff18de8d48b 141 if (lora.RegIrqFlags.bits.CadDone)
dudmuck 0:2ff18de8d48b 142 printf("CadDone ");
dudmuck 0:2ff18de8d48b 143 if (lora.RegIrqFlags.bits.TxDone)
dudmuck 0:2ff18de8d48b 144 printf("TxDone ");
dudmuck 0:2ff18de8d48b 145 if (lora.RegIrqFlags.bits.ValidHeader)
dudmuck 0:2ff18de8d48b 146 printf("ValidHeader ");
dudmuck 0:2ff18de8d48b 147 if (lora.RegIrqFlags.bits.PayloadCrcError)
dudmuck 0:2ff18de8d48b 148 printf("PayloadCrcError ");
dudmuck 0:2ff18de8d48b 149 if (lora.RegIrqFlags.bits.RxDone)
dudmuck 0:2ff18de8d48b 150 printf("RxDone ");
dudmuck 0:2ff18de8d48b 151 if (lora.RegIrqFlags.bits.RxTimeout)
dudmuck 0:2ff18de8d48b 152 printf("RxTimeout ");
dudmuck 0:2ff18de8d48b 153
dudmuck 0:2ff18de8d48b 154 printf("\r\n");
dudmuck 0:2ff18de8d48b 155
dudmuck 0:2ff18de8d48b 156 if (clear)
dudmuck 0:2ff18de8d48b 157 radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);
dudmuck 0:2ff18de8d48b 158 }
dudmuck 0:2ff18de8d48b 159
dudmuck 0:2ff18de8d48b 160 void printOpMode()
dudmuck 0:2ff18de8d48b 161 {
dudmuck 0:2ff18de8d48b 162 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 0:2ff18de8d48b 163 switch (radio.RegOpMode.bits.Mode) {
dudmuck 0:2ff18de8d48b 164 case RF_OPMODE_SLEEP: printf("sleep"); break;
dudmuck 0:2ff18de8d48b 165 case RF_OPMODE_STANDBY: printf("stby"); break;
dudmuck 0:2ff18de8d48b 166 case RF_OPMODE_SYNTHESIZER_TX: printf("fstx"); break;
dudmuck 0:2ff18de8d48b 167 case RF_OPMODE_TRANSMITTER: printf("tx"); break;
dudmuck 0:2ff18de8d48b 168 case RF_OPMODE_SYNTHESIZER_RX: printf("fsrx"); break;
dudmuck 0:2ff18de8d48b 169 case RF_OPMODE_RECEIVER: printf("rx"); break;
dudmuck 0:2ff18de8d48b 170 case 6:
dudmuck 0:2ff18de8d48b 171 if (radio.RegOpMode.bits.LongRangeMode)
dudmuck 0:2ff18de8d48b 172 printf("rxs");
dudmuck 0:2ff18de8d48b 173 else
dudmuck 0:2ff18de8d48b 174 printf("-6-");
dudmuck 0:2ff18de8d48b 175 break; // todo: different lora/fsk
dudmuck 0:2ff18de8d48b 176 case 7:
dudmuck 0:2ff18de8d48b 177 if (radio.RegOpMode.bits.LongRangeMode)
dudmuck 0:2ff18de8d48b 178 printf("cad");
dudmuck 0:2ff18de8d48b 179 else
dudmuck 0:2ff18de8d48b 180 printf("-7-");
dudmuck 0:2ff18de8d48b 181 break; // todo: different lora/fsk
dudmuck 0:2ff18de8d48b 182 }
dudmuck 0:2ff18de8d48b 183 }
dudmuck 0:2ff18de8d48b 184
dudmuck 0:2ff18de8d48b 185 EventQueue queue;
dudmuck 3:7c01b8978638 186
dudmuck 3:7c01b8978638 187 volatile bool get_tx_done;
dudmuck 3:7c01b8978638 188 volatile bool beacon_guard;
dudmuck 0:2ff18de8d48b 189 bool restore_tx_invert;
dudmuck 3:7c01b8978638 190 bool restore_header_mode;
dudmuck 0:2ff18de8d48b 191
dudmuck 0:2ff18de8d48b 192 void send_downlink()
dudmuck 0:2ff18de8d48b 193 {
dudmuck 0:2ff18de8d48b 194 if (LoRaWan::do_downlink) {
dudmuck 0:2ff18de8d48b 195 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 196 radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 197 lora.invert_tx(true);
dudmuck 0:2ff18de8d48b 198 restore_tx_invert = true;
dudmuck 0:2ff18de8d48b 199 lora.setRxPayloadCrcOn(false);
dudmuck 0:2ff18de8d48b 200 lora.start_tx(lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 201 LoRaWan::do_downlink = false;
dudmuck 3:7c01b8978638 202 get_tx_done = true;
dudmuck 0:2ff18de8d48b 203 }
dudmuck 0:2ff18de8d48b 204 }
dudmuck 0:2ff18de8d48b 205
dudmuck 8:307f7faeb594 206 void grove_led_off()
dudmuck 8:307f7faeb594 207 {
dudmuck 8:307f7faeb594 208 grove_led = 0;
dudmuck 8:307f7faeb594 209 }
dudmuck 8:307f7faeb594 210
dudmuck 0:2ff18de8d48b 211 void
dudmuck 0:2ff18de8d48b 212 service_radio()
dudmuck 0:2ff18de8d48b 213 {
dudmuck 0:2ff18de8d48b 214 service_action_e act = lora.service();
dudmuck 0:2ff18de8d48b 215
dudmuck 0:2ff18de8d48b 216 switch (act) {
dudmuck 0:2ff18de8d48b 217 case SERVICE_ERROR:
dudmuck 0:2ff18de8d48b 218 printf("SERVICE_ERROR\r\n");
dudmuck 0:2ff18de8d48b 219 case SERVICE_TX_DONE:
dudmuck 0:2ff18de8d48b 220 case SERVICE_NONE:
dudmuck 0:2ff18de8d48b 221 break;
dudmuck 0:2ff18de8d48b 222 case SERVICE_READ_FIFO:
dudmuck 3:7c01b8978638 223 LoRaWan::rx_slot = tim_get_current_slot();
dudmuck 3:7c01b8978638 224 LoRaWan::rx_ms = timer.read_ms();
dudmuck 8:307f7faeb594 225 grove_led = 1;
dudmuck 8:307f7faeb594 226 queue.call_in(100, grove_led_off);
dudmuck 8:307f7faeb594 227 printf("%.1fdB %ddBm ", lora.RegPktSnrValue / 4.0, lora.get_pkt_rssi());
dudmuck 3:7c01b8978638 228 if (LoRaWan::parse_receive()) {
dudmuck 3:7c01b8978638 229 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 3:7c01b8978638 230 wait(0.05);
dudmuck 3:7c01b8978638 231 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 232 }
dudmuck 0:2ff18de8d48b 233 break;
dudmuck 0:2ff18de8d48b 234 } // ..switch (act)
dudmuck 0:2ff18de8d48b 235 }
dudmuck 0:2ff18de8d48b 236
dudmuck 0:2ff18de8d48b 237 volatile float prev_beacon_send_at;
dudmuck 0:2ff18de8d48b 238 volatile float beacon_send_at;
dudmuck 0:2ff18de8d48b 239 volatile float beacon_loaded_at;
dudmuck 0:2ff18de8d48b 240
dudmuck 0:2ff18de8d48b 241 volatile bool beacon_loaded;
dudmuck 0:2ff18de8d48b 242 void
dudmuck 0:2ff18de8d48b 243 send_beacon()
dudmuck 0:2ff18de8d48b 244 {
dudmuck 0:2ff18de8d48b 245 prev_beacon_send_at = beacon_send_at;
dudmuck 0:2ff18de8d48b 246 beacon_send_at = timer.read();
dudmuck 0:2ff18de8d48b 247
dudmuck 0:2ff18de8d48b 248 if (!beacon_loaded)
dudmuck 0:2ff18de8d48b 249 return;
dudmuck 0:2ff18de8d48b 250
dudmuck 0:2ff18de8d48b 251 radio.set_opmode(RF_OPMODE_TRANSMITTER);
dudmuck 0:2ff18de8d48b 252 beacon_loaded = false;
dudmuck 3:7c01b8978638 253 get_tx_done = true;
dudmuck 3:7c01b8978638 254 beacon_guard = false;
dudmuck 0:2ff18de8d48b 255 }
dudmuck 0:2ff18de8d48b 256
dudmuck 0:2ff18de8d48b 257 static uint16_t beacon_crc( uint8_t *buffer, uint16_t length )
dudmuck 0:2ff18de8d48b 258 {
dudmuck 0:2ff18de8d48b 259 // The CRC calculation follows CCITT
dudmuck 0:2ff18de8d48b 260 const uint16_t polynom = 0x1021;
dudmuck 0:2ff18de8d48b 261 // CRC initial value
dudmuck 0:2ff18de8d48b 262 uint16_t crc = 0x0000;
dudmuck 0:2ff18de8d48b 263
dudmuck 0:2ff18de8d48b 264 if( buffer == NULL )
dudmuck 0:2ff18de8d48b 265 {
dudmuck 0:2ff18de8d48b 266 return 0;
dudmuck 0:2ff18de8d48b 267 }
dudmuck 0:2ff18de8d48b 268
dudmuck 0:2ff18de8d48b 269 for( uint16_t i = 0; i < length; ++i )
dudmuck 0:2ff18de8d48b 270 {
dudmuck 0:2ff18de8d48b 271 crc ^= ( uint16_t ) buffer[i] << 8;
dudmuck 0:2ff18de8d48b 272 for( uint16_t j = 0; j < 8; ++j )
dudmuck 0:2ff18de8d48b 273 {
dudmuck 0:2ff18de8d48b 274 crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
dudmuck 0:2ff18de8d48b 275 }
dudmuck 0:2ff18de8d48b 276 }
dudmuck 0:2ff18de8d48b 277
dudmuck 0:2ff18de8d48b 278 return crc;
dudmuck 0:2ff18de8d48b 279 }
dudmuck 0:2ff18de8d48b 280
dudmuck 0:2ff18de8d48b 281 void
dudmuck 3:7c01b8978638 282 _load_beacon()
dudmuck 0:2ff18de8d48b 283 {
dudmuck 0:2ff18de8d48b 284 uint16_t crc;
dudmuck 0:2ff18de8d48b 285 lora.RegPayloadLength = BEACON_SIZE;
dudmuck 0:2ff18de8d48b 286 radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 287 lora.setHeaderMode(true);
dudmuck 3:7c01b8978638 288 restore_header_mode = true;
dudmuck 0:2ff18de8d48b 289
dudmuck 0:2ff18de8d48b 290 if (skip_beacon_cnt > 0) {
dudmuck 1:107435401168 291 //printf("skip_beacon_cnt:%d\r\n", skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 292 lora.invert_tx(true);
dudmuck 0:2ff18de8d48b 293 restore_tx_invert = true;
dudmuck 0:2ff18de8d48b 294 skip_beacon_cnt--;
dudmuck 0:2ff18de8d48b 295 }
dudmuck 8:307f7faeb594 296
dudmuck 8:307f7faeb594 297 if (beacon_command_pending != BCAST_CMD_NONE) {
dudmuck 8:307f7faeb594 298 beacon_payload = beacon_command_pending;
dudmuck 8:307f7faeb594 299 beacon_payload <<= 24;
dudmuck 8:307f7faeb594 300 beacon_payload |= beacon_arg;
dudmuck 8:307f7faeb594 301 prev_beacon_command = beacon_command_pending;
dudmuck 8:307f7faeb594 302 beacon_command_pending = BCAST_CMD_NONE;
dudmuck 8:307f7faeb594 303
dudmuck 8:307f7faeb594 304 if (prev_beacon_command == BCAST_CMD_LED_OFF)
dudmuck 8:307f7faeb594 305 grove_led = 0;
dudmuck 8:307f7faeb594 306 else if (prev_beacon_command == BCAST_CMD_LED_ON)
dudmuck 8:307f7faeb594 307 grove_led = 1;
dudmuck 8:307f7faeb594 308 }
dudmuck 0:2ff18de8d48b 309
dudmuck 0:2ff18de8d48b 310 radio.tx_buf[0] = beacon_payload & 0xff;
dudmuck 0:2ff18de8d48b 311 radio.tx_buf[1] = (beacon_payload >> 8) & 0xff;
dudmuck 0:2ff18de8d48b 312 radio.tx_buf[2] = (beacon_payload >> 16) & 0xff;
dudmuck 0:2ff18de8d48b 313 radio.tx_buf[3] = (beacon_payload >> 24) & 0xff;
dudmuck 0:2ff18de8d48b 314 crc = beacon_crc(radio.tx_buf, 4);
dudmuck 0:2ff18de8d48b 315 radio.tx_buf[4] = crc & 0xff;
dudmuck 0:2ff18de8d48b 316 radio.tx_buf[5] = crc >> 8;
dudmuck 0:2ff18de8d48b 317
dudmuck 0:2ff18de8d48b 318 // DIO0 to TxDone
dudmuck 3:7c01b8978638 319 radio.RegDioMapping1.bits.Dio0Mapping = 1;
dudmuck 3:7c01b8978638 320 radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
dudmuck 0:2ff18de8d48b 321
dudmuck 0:2ff18de8d48b 322 // set FifoPtrAddr to FifoTxPtrBase
dudmuck 0:2ff18de8d48b 323 radio.write_reg(REG_LR_FIFOADDRPTR, radio.read_reg(REG_LR_FIFOTXBASEADDR));
dudmuck 0:2ff18de8d48b 324
dudmuck 0:2ff18de8d48b 325 // write PayloadLength bytes to fifo
dudmuck 0:2ff18de8d48b 326 lora.write_fifo(lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 327
dudmuck 0:2ff18de8d48b 328 // prepare for tx to occur in send_beacon()
dudmuck 0:2ff18de8d48b 329 radio.set_opmode(RF_OPMODE_SYNTHESIZER_TX);
dudmuck 0:2ff18de8d48b 330 beacon_loaded = true;
dudmuck 0:2ff18de8d48b 331
dudmuck 0:2ff18de8d48b 332 beacon_loaded_at = timer.read();
dudmuck 0:2ff18de8d48b 333
dudmuck 0:2ff18de8d48b 334 beacon_payload = 0; // sent once
dudmuck 0:2ff18de8d48b 335 }
dudmuck 0:2ff18de8d48b 336
dudmuck 3:7c01b8978638 337 void load_beacon()
dudmuck 3:7c01b8978638 338 {
dudmuck 3:7c01b8978638 339 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 3:7c01b8978638 340 beacon_guard = true;
dudmuck 3:7c01b8978638 341 queue.call_in(100, _load_beacon);
dudmuck 3:7c01b8978638 342 }
dudmuck 3:7c01b8978638 343
dudmuck 0:2ff18de8d48b 344 void get_time_till_beacon()
dudmuck 0:2ff18de8d48b 345 {
dudmuck 0:2ff18de8d48b 346 uint16_t slots = tim_get_current_slot();
dudmuck 0:2ff18de8d48b 347 printf("slots:%u\r\n", slots);
dudmuck 0:2ff18de8d48b 348 }
dudmuck 0:2ff18de8d48b 349
dudmuck 4:7e743e402681 350 void rx_isr()
dudmuck 0:2ff18de8d48b 351 {
dudmuck 0:2ff18de8d48b 352 static uint8_t pcbuf_idx = 0;
dudmuck 0:2ff18de8d48b 353 static uint8_t prev_len = 0;;
dudmuck 0:2ff18de8d48b 354 char c = pc.getc();
dudmuck 0:2ff18de8d48b 355
dudmuck 0:2ff18de8d48b 356 if (c == 8) {
dudmuck 0:2ff18de8d48b 357 if (pcbuf_idx > 0) {
dudmuck 0:2ff18de8d48b 358 pc.putc(8);
dudmuck 0:2ff18de8d48b 359 pc.putc(' ');
dudmuck 0:2ff18de8d48b 360 pc.putc(8);
dudmuck 0:2ff18de8d48b 361 pcbuf_idx--;
dudmuck 0:2ff18de8d48b 362 }
dudmuck 0:2ff18de8d48b 363 } else if (c == 3) { // ctrl-C
dudmuck 0:2ff18de8d48b 364 pcbuf_len = -1;
dudmuck 0:2ff18de8d48b 365 } else if (c == '\r') {
dudmuck 0:2ff18de8d48b 366 if (pcbuf_idx == 0) {
dudmuck 0:2ff18de8d48b 367 pcbuf_len = prev_len;
dudmuck 0:2ff18de8d48b 368 } else {
dudmuck 0:2ff18de8d48b 369 pcbuf[pcbuf_idx] = 0; // null terminate
dudmuck 0:2ff18de8d48b 370 prev_len = pcbuf_idx;
dudmuck 0:2ff18de8d48b 371 pcbuf_idx = 0;
dudmuck 0:2ff18de8d48b 372 pcbuf_len = prev_len;
dudmuck 0:2ff18de8d48b 373 }
dudmuck 0:2ff18de8d48b 374 } else if (pcbuf_idx < sizeof(pcbuf)) {
dudmuck 0:2ff18de8d48b 375 pcbuf[pcbuf_idx++] = c;
dudmuck 0:2ff18de8d48b 376 pc.putc(c);
dudmuck 0:2ff18de8d48b 377 }
dudmuck 0:2ff18de8d48b 378 }
dudmuck 0:2ff18de8d48b 379
dudmuck 0:2ff18de8d48b 380 void cmd_skip_beacon(uint8_t idx)
dudmuck 0:2ff18de8d48b 381 {
dudmuck 0:2ff18de8d48b 382 if (pcbuf[idx] >= '0' && pcbuf[idx] <= '9') {
dudmuck 0:2ff18de8d48b 383 sscanf(pcbuf+idx, "%u", &skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 384 }
dudmuck 0:2ff18de8d48b 385 printf("skip_beacon_cnt:%u\r\n", skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 386 }
dudmuck 0:2ff18de8d48b 387
dudmuck 0:2ff18de8d48b 388 void cmd_list_motes(uint8_t idx)
dudmuck 0:2ff18de8d48b 389 {
dudmuck 0:2ff18de8d48b 390 int i;
dudmuck 0:2ff18de8d48b 391 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 392 if (motes[i].dev_addr != DEVADDR_NONE) {
dudmuck 0:2ff18de8d48b 393 printf("%lx\r\n", motes[i].dev_addr);
dudmuck 0:2ff18de8d48b 394 }
dudmuck 0:2ff18de8d48b 395 }
dudmuck 0:2ff18de8d48b 396 }
dudmuck 0:2ff18de8d48b 397
dudmuck 0:2ff18de8d48b 398 void
dudmuck 0:2ff18de8d48b 399 cmd_beacon_payload(uint8_t idx)
dudmuck 0:2ff18de8d48b 400 {
dudmuck 0:2ff18de8d48b 401 sscanf(pcbuf+idx, "%x", &beacon_payload);
dudmuck 0:2ff18de8d48b 402 printf("beacon_payload:%08x\r\n", beacon_payload);
dudmuck 0:2ff18de8d48b 403 }
dudmuck 0:2ff18de8d48b 404
dudmuck 8:307f7faeb594 405 void cmd_beacon_led_blink(uint8_t idx)
dudmuck 8:307f7faeb594 406 {
dudmuck 8:307f7faeb594 407 printf("blink-");
dudmuck 8:307f7faeb594 408 if (beacon_command_pending == BCAST_CMD_NONE) {
dudmuck 8:307f7faeb594 409 beacon_command_pending = BCAST_CMD_LED_COUNT;
dudmuck 8:307f7faeb594 410 sscanf(pcbuf+idx, "%d", &beacon_arg);
dudmuck 8:307f7faeb594 411 printf("cmd-pending:%02x, arg:%u\r\n", beacon_command_pending, beacon_arg);
dudmuck 8:307f7faeb594 412 } else
dudmuck 8:307f7faeb594 413 printf("pending:%x\r\n", beacon_command_pending);
dudmuck 8:307f7faeb594 414 }
dudmuck 8:307f7faeb594 415
dudmuck 0:2ff18de8d48b 416 void
dudmuck 0:2ff18de8d48b 417 cmd_send_downlink(uint8_t idx)
dudmuck 0:2ff18de8d48b 418 {
dudmuck 0:2ff18de8d48b 419 ota_mote_t* mote = NULL;
dudmuck 0:2ff18de8d48b 420 int i;
dudmuck 0:2ff18de8d48b 421 unsigned int dev_addr;
dudmuck 0:2ff18de8d48b 422 sscanf(pcbuf+idx, "%x", &dev_addr);
dudmuck 0:2ff18de8d48b 423 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 424 if (motes[i].dev_addr == dev_addr) {
dudmuck 0:2ff18de8d48b 425 break;
dudmuck 0:2ff18de8d48b 426 }
dudmuck 0:2ff18de8d48b 427 }
dudmuck 0:2ff18de8d48b 428 if (i == N_MOTES) {
dudmuck 0:2ff18de8d48b 429 printf("mote %x not found\r\n", dev_addr);
dudmuck 0:2ff18de8d48b 430 return;
dudmuck 0:2ff18de8d48b 431 }
dudmuck 0:2ff18de8d48b 432 mote = &motes[i];
dudmuck 0:2ff18de8d48b 433
dudmuck 0:2ff18de8d48b 434 while (pcbuf[idx] != ' ') {
dudmuck 0:2ff18de8d48b 435 if (pcbuf[++idx] == 0) {
dudmuck 0:2ff18de8d48b 436 printf("hit end\r\n");
dudmuck 0:2ff18de8d48b 437 return;
dudmuck 0:2ff18de8d48b 438 }
dudmuck 0:2ff18de8d48b 439 }
dudmuck 0:2ff18de8d48b 440 idx++; // step past space
dudmuck 0:2ff18de8d48b 441
dudmuck 0:2ff18de8d48b 442 mote->user_downlink_length = 0;
dudmuck 0:2ff18de8d48b 443 while (pcbuf[idx] > ' ') {
dudmuck 0:2ff18de8d48b 444 int o;
dudmuck 0:2ff18de8d48b 445 sscanf(pcbuf+idx, "%02x", &o);
dudmuck 0:2ff18de8d48b 446 LoRaWan::user_downlink[mote->user_downlink_length++] = o;
dudmuck 0:2ff18de8d48b 447 idx += 2;
dudmuck 0:2ff18de8d48b 448 }
dudmuck 0:2ff18de8d48b 449
dudmuck 0:2ff18de8d48b 450 printf("%u bytes scheduled for %lx\r\n", mote->user_downlink_length, mote->dev_addr);
dudmuck 0:2ff18de8d48b 451 }
dudmuck 0:2ff18de8d48b 452
dudmuck 0:2ff18de8d48b 453 void cmd_status(uint8_t idx)
dudmuck 0:2ff18de8d48b 454 {
dudmuck 0:2ff18de8d48b 455 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 2:9628d5e4b1bf 456 printf("%.3fMHz sf%ubw%u ", radio.get_frf_MHz(), lora.getSf(), lora.getBw());
dudmuck 0:2ff18de8d48b 457 printOpMode();
dudmuck 0:2ff18de8d48b 458 if (!radio.RegOpMode.bits.LongRangeMode) {
dudmuck 0:2ff18de8d48b 459 printf("FSK\r\n");
dudmuck 0:2ff18de8d48b 460 return;
dudmuck 0:2ff18de8d48b 461 }
dudmuck 0:2ff18de8d48b 462
dudmuck 0:2ff18de8d48b 463 lora.RegIrqFlags.octet = radio.read_reg(REG_LR_IRQFLAGS);
dudmuck 0:2ff18de8d48b 464 printLoraIrqs(false);
dudmuck 0:2ff18de8d48b 465
dudmuck 3:7c01b8978638 466 printf("get_tx_done:%u, ", get_tx_done);
dudmuck 2:9628d5e4b1bf 467 printf("modemstat:%02x\r\n", radio.read_reg(REG_LR_MODEMSTAT));
dudmuck 2:9628d5e4b1bf 468 radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
dudmuck 2:9628d5e4b1bf 469 printf("\r\nskip_beacon_cnt:%u, currently:%u dio0map:%u\r\n", skip_beacon_cnt, tim_get_current_slot(), radio.RegDioMapping1.bits.Dio0Mapping);
dudmuck 2:9628d5e4b1bf 470
dudmuck 0:2ff18de8d48b 471 }
dudmuck 0:2ff18de8d48b 472
dudmuck 0:2ff18de8d48b 473 void cmd_help(uint8_t);
dudmuck 0:2ff18de8d48b 474
dudmuck 0:2ff18de8d48b 475 typedef struct {
dudmuck 0:2ff18de8d48b 476 const char* const cmd;
dudmuck 0:2ff18de8d48b 477 void (*handler)(uint8_t args_at);
dudmuck 0:2ff18de8d48b 478 const char* const arg_descr;
dudmuck 0:2ff18de8d48b 479 const char* const description;
dudmuck 0:2ff18de8d48b 480 } menu_item_t;
dudmuck 0:2ff18de8d48b 481
dudmuck 0:2ff18de8d48b 482 const menu_item_t menu_items[] =
dudmuck 0:2ff18de8d48b 483 { /* after first character, command names must be [A-Za-z] */
dudmuck 0:2ff18de8d48b 484 { "?", cmd_help, "","show available commands"},
dudmuck 0:2ff18de8d48b 485 { ".", cmd_status, "","read status"},
dudmuck 0:2ff18de8d48b 486 { "b", cmd_beacon_payload, "<%x>","set beacon payload"},
dudmuck 8:307f7faeb594 487 { "bl", cmd_beacon_led_blink, "<%u>","set beacon command for LED blink"},
dudmuck 0:2ff18de8d48b 488 { "sb", cmd_skip_beacon, "<%d>","skip beacons"},
dudmuck 0:2ff18de8d48b 489 { "list", cmd_list_motes, "","list active motes"},
dudmuck 0:2ff18de8d48b 490 { "dl", cmd_send_downlink, "[%x %s]","send downlink <mote-hex-dev-addr> <hex-payload>"},
dudmuck 0:2ff18de8d48b 491 { NULL, NULL, NULL, NULL }
dudmuck 0:2ff18de8d48b 492 };
dudmuck 0:2ff18de8d48b 493
dudmuck 0:2ff18de8d48b 494 void cmd_help(uint8_t args_at)
dudmuck 0:2ff18de8d48b 495 {
dudmuck 0:2ff18de8d48b 496 int i;
dudmuck 0:2ff18de8d48b 497
dudmuck 0:2ff18de8d48b 498 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:2ff18de8d48b 499 printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description);
dudmuck 0:2ff18de8d48b 500 }
dudmuck 0:2ff18de8d48b 501
dudmuck 0:2ff18de8d48b 502 }
dudmuck 0:2ff18de8d48b 503
dudmuck 0:2ff18de8d48b 504 void
dudmuck 0:2ff18de8d48b 505 console()
dudmuck 0:2ff18de8d48b 506 {
dudmuck 0:2ff18de8d48b 507 int i;
dudmuck 0:2ff18de8d48b 508 uint8_t user_cmd_len;
dudmuck 0:2ff18de8d48b 509
dudmuck 0:2ff18de8d48b 510 if (pcbuf_len < 0) { // ctrl-C
dudmuck 0:2ff18de8d48b 511 //printf("abort\r\n");
dudmuck 0:2ff18de8d48b 512 return;
dudmuck 0:2ff18de8d48b 513 }
dudmuck 0:2ff18de8d48b 514 if (pcbuf_len == 0)
dudmuck 0:2ff18de8d48b 515 return;
dudmuck 0:2ff18de8d48b 516
dudmuck 0:2ff18de8d48b 517 printf("\r\n");
dudmuck 0:2ff18de8d48b 518
dudmuck 0:2ff18de8d48b 519 /* get end of user-entered command */
dudmuck 0:2ff18de8d48b 520 user_cmd_len = 1; // first character can be any character
dudmuck 0:2ff18de8d48b 521 for (i = 1; i <= pcbuf_len; i++) {
dudmuck 0:2ff18de8d48b 522 if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') {
dudmuck 0:2ff18de8d48b 523 user_cmd_len = i;
dudmuck 0:2ff18de8d48b 524 break;
dudmuck 0:2ff18de8d48b 525 }
dudmuck 0:2ff18de8d48b 526 }
dudmuck 0:2ff18de8d48b 527
dudmuck 0:2ff18de8d48b 528 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:2ff18de8d48b 529 int mi_len = strlen(menu_items[i].cmd);
dudmuck 0:2ff18de8d48b 530
dudmuck 0:2ff18de8d48b 531 if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) {
dudmuck 0:2ff18de8d48b 532 while (pcbuf[mi_len] == ' ') // skip past spaces
dudmuck 0:2ff18de8d48b 533 mi_len++;
dudmuck 0:2ff18de8d48b 534 menu_items[i].handler(mi_len);
dudmuck 0:2ff18de8d48b 535 break;
dudmuck 0:2ff18de8d48b 536 }
dudmuck 0:2ff18de8d48b 537 }
dudmuck 0:2ff18de8d48b 538
dudmuck 0:2ff18de8d48b 539 pcbuf_len = 0;
dudmuck 0:2ff18de8d48b 540 printf("> ");
dudmuck 0:2ff18de8d48b 541 fflush(stdout);
dudmuck 0:2ff18de8d48b 542 }
dudmuck 0:2ff18de8d48b 543
dudmuck 0:2ff18de8d48b 544 int main()
dudmuck 0:2ff18de8d48b 545 {
dudmuck 0:2ff18de8d48b 546 Thread eventThread;
dudmuck 3:7c01b8978638 547 pc.baud(38400);
dudmuck 1:107435401168 548 printf("\r\nreset\r\n");
dudmuck 0:2ff18de8d48b 549
dudmuck 0:2ff18de8d48b 550 radio.hw_reset();
dudmuck 0:2ff18de8d48b 551
dudmuck 5:ceacaa560cfd 552 #ifndef TARGET_DISCO_L072CZ_LRWAN1
dudmuck 0:2ff18de8d48b 553 rfsw.input();
dudmuck 0:2ff18de8d48b 554 if (rfsw.read()) {
dudmuck 0:2ff18de8d48b 555 printf("LAS\r\n");
dudmuck 0:2ff18de8d48b 556 /* LAS HF=PA_BOOST LF=RFO */
dudmuck 0:2ff18de8d48b 557 radio.RegPaConfig.bits.PaSelect = 1;
dudmuck 0:2ff18de8d48b 558 } else {
dudmuck 0:2ff18de8d48b 559 printf("MAS\r\n");
dudmuck 0:2ff18de8d48b 560 radio.RegPaConfig.bits.PaSelect = 0;
dudmuck 0:2ff18de8d48b 561 }
dudmuck 0:2ff18de8d48b 562 rfsw.output();
dudmuck 5:ceacaa560cfd 563 #endif /* !TARGET_DISCO_L072CZ_LRWAN1 */
dudmuck 0:2ff18de8d48b 564
dudmuck 0:2ff18de8d48b 565 radio.rf_switch = rfsw_callback;
dudmuck 0:2ff18de8d48b 566
dudmuck 0:2ff18de8d48b 567 init_radio();
dudmuck 0:2ff18de8d48b 568
dudmuck 0:2ff18de8d48b 569 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 570
dudmuck 0:2ff18de8d48b 571 eventThread.start(callback(&queue, &EventQueue::dispatch_forever));
dudmuck 0:2ff18de8d48b 572 LoRaWan::init();
dudmuck 0:2ff18de8d48b 573
dudmuck 0:2ff18de8d48b 574 timer.start();
dudmuck 0:2ff18de8d48b 575 tim_init();
dudmuck 4:7e743e402681 576
dudmuck 4:7e743e402681 577 pc.attach(&rx_isr);
dudmuck 8:307f7faeb594 578
dudmuck 8:307f7faeb594 579 if (user_button)
dudmuck 8:307f7faeb594 580 but_state = true;
dudmuck 8:307f7faeb594 581 else
dudmuck 8:307f7faeb594 582 but_state = false;
dudmuck 0:2ff18de8d48b 583
dudmuck 0:2ff18de8d48b 584 for (;;) {
dudmuck 0:2ff18de8d48b 585 console();
dudmuck 1:107435401168 586
dudmuck 1:107435401168 587 if (radio.dio0) {
dudmuck 3:7c01b8978638 588 if (get_tx_done) {
dudmuck 3:7c01b8978638 589 get_tx_done = false;
dudmuck 3:7c01b8978638 590 lora.RegIrqFlags.octet = 0;
dudmuck 3:7c01b8978638 591 lora.RegIrqFlags.bits.TxDone = 1;
dudmuck 3:7c01b8978638 592 radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);
dudmuck 3:7c01b8978638 593
dudmuck 3:7c01b8978638 594 if (restore_tx_invert) {
dudmuck 3:7c01b8978638 595 lora.invert_tx(false);
dudmuck 3:7c01b8978638 596 restore_tx_invert = false;
dudmuck 3:7c01b8978638 597 }
dudmuck 3:7c01b8978638 598
dudmuck 3:7c01b8978638 599 if (restore_header_mode) {
dudmuck 3:7c01b8978638 600 lora.setHeaderMode(false);
dudmuck 3:7c01b8978638 601 restore_header_mode = false;
dudmuck 3:7c01b8978638 602 }
dudmuck 3:7c01b8978638 603 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 3:7c01b8978638 604 } else {
dudmuck 3:7c01b8978638 605 if (!beacon_guard)
dudmuck 3:7c01b8978638 606 service_radio();
dudmuck 3:7c01b8978638 607 }
dudmuck 1:107435401168 608 }
dudmuck 8:307f7faeb594 609
dudmuck 8:307f7faeb594 610 if (but_state) {
dudmuck 8:307f7faeb594 611 if (!user_button)
dudmuck 8:307f7faeb594 612 but_state = false;
dudmuck 8:307f7faeb594 613 } else {
dudmuck 8:307f7faeb594 614 if (user_button) {
dudmuck 8:307f7faeb594 615 but_state = true;
dudmuck 8:307f7faeb594 616 printf("beacon-command ");
dudmuck 8:307f7faeb594 617 if (beacon_command_pending == BCAST_CMD_NONE) {
dudmuck 8:307f7faeb594 618 switch (prev_beacon_command) {
dudmuck 8:307f7faeb594 619 case BCAST_CMD_LED_OFF:
dudmuck 8:307f7faeb594 620 case BCAST_CMD_NONE: beacon_command_pending = BCAST_CMD_LED_ON; printf("LED_ON"); break;
dudmuck 8:307f7faeb594 621 case BCAST_CMD_LED_ON: beacon_command_pending = BCAST_CMD_LED_OFF; printf("LED_OFF"); break;
dudmuck 8:307f7faeb594 622 default: break;
dudmuck 8:307f7faeb594 623 }
dudmuck 8:307f7faeb594 624 } else
dudmuck 8:307f7faeb594 625 printf("already-pending:%x", beacon_command_pending);
dudmuck 8:307f7faeb594 626
dudmuck 8:307f7faeb594 627 printf("\r\n");
dudmuck 8:307f7faeb594 628 } // ..if (user_button)
dudmuck 8:307f7faeb594 629 }
dudmuck 0:2ff18de8d48b 630 } // ..for(;;)
dudmuck 0:2ff18de8d48b 631 }