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:
Thu Jul 27 21:58:41 2017 +0000
Revision:
12:9a8c13c4298b
Parent:
11:50d0558a4e37
Child:
13:fa2095be01c4
prevented ISR SPI access from colliding with mainloop SPI access

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 12:9a8c13c4298b 5 #define TYPE_ABZ
dudmuck 4:7e743e402681 6 RawSerial pc(USBTX, USBRX);
dudmuck 0:2ff18de8d48b 7 Timer timer;
dudmuck 0:2ff18de8d48b 8
dudmuck 10:6783623cc886 9 //#if defined(TARGET_DISCO_L072CZ_LRWAN1)
dudmuck 10:6783623cc886 10 #if defined(TARGET_NUCLEO_L073RZ) && defined(TYPE_ABZ)
dudmuck 2:9628d5e4b1bf 11 SPI spi(PA_7, PA_6, PB_3); // mosi, miso, sclk
dudmuck 2:9628d5e4b1bf 12 // dio0, dio1, nss, spi, rst
dudmuck 2:9628d5e4b1bf 13 SX127x radio(PB_4, PB_1, PA_15, spi, PC_0); // sx1276 arduino shield
dudmuck 2:9628d5e4b1bf 14
dudmuck 2:9628d5e4b1bf 15 #define CRF1 PA_1
dudmuck 2:9628d5e4b1bf 16 #define CRF2 PC_2
dudmuck 2:9628d5e4b1bf 17 #define CRF3 PC_1
dudmuck 2:9628d5e4b1bf 18 DigitalOut Vctl1(CRF1);
dudmuck 2:9628d5e4b1bf 19 DigitalOut Vctl2(CRF2);
dudmuck 2:9628d5e4b1bf 20 DigitalOut Vctl3(CRF3);
dudmuck 2:9628d5e4b1bf 21
dudmuck 2:9628d5e4b1bf 22 void rfsw_callback()
dudmuck 2:9628d5e4b1bf 23 {
dudmuck 2:9628d5e4b1bf 24 if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER) {
dudmuck 2:9628d5e4b1bf 25 Vctl1 = 0;
dudmuck 2:9628d5e4b1bf 26 if (radio.RegPaConfig.bits.PaSelect) {
dudmuck 2:9628d5e4b1bf 27 Vctl2 = 0;
dudmuck 2:9628d5e4b1bf 28 Vctl3 = 1;
dudmuck 2:9628d5e4b1bf 29 } else {
dudmuck 2:9628d5e4b1bf 30 Vctl2 = 1;
dudmuck 2:9628d5e4b1bf 31 Vctl3 = 0;
dudmuck 2:9628d5e4b1bf 32 }
dudmuck 2:9628d5e4b1bf 33 } else {
dudmuck 2:9628d5e4b1bf 34 if (radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER || radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER_SINGLE)
dudmuck 2:9628d5e4b1bf 35 Vctl1 = 1;
dudmuck 2:9628d5e4b1bf 36 else
dudmuck 2:9628d5e4b1bf 37 Vctl1 = 0;
dudmuck 2:9628d5e4b1bf 38
dudmuck 2:9628d5e4b1bf 39 Vctl2 = 0;
dudmuck 2:9628d5e4b1bf 40 Vctl3 = 0;
dudmuck 2:9628d5e4b1bf 41 }
dudmuck 2:9628d5e4b1bf 42 }
dudmuck 2:9628d5e4b1bf 43 #else
dudmuck 2:9628d5e4b1bf 44 SPI spi(D11, D12, D13); // mosi, miso, sclk
dudmuck 2:9628d5e4b1bf 45 // dio0, dio1, nss, spi, rst
dudmuck 2:9628d5e4b1bf 46 SX127x radio( D2, D3, D10, spi, A0); // sx1276 arduino shield
dudmuck 2:9628d5e4b1bf 47
dudmuck 2:9628d5e4b1bf 48 DigitalInOut rfsw(A4);
dudmuck 2:9628d5e4b1bf 49
dudmuck 2:9628d5e4b1bf 50 void rfsw_callback()
dudmuck 2:9628d5e4b1bf 51 {
dudmuck 2:9628d5e4b1bf 52 if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER)
dudmuck 2:9628d5e4b1bf 53 rfsw = 1;
dudmuck 2:9628d5e4b1bf 54 else
dudmuck 2:9628d5e4b1bf 55 rfsw = 0;
dudmuck 2:9628d5e4b1bf 56 }
dudmuck 2:9628d5e4b1bf 57 #endif
dudmuck 0:2ff18de8d48b 58
dudmuck 0:2ff18de8d48b 59 char pcbuf[64];
dudmuck 0:2ff18de8d48b 60 int pcbuf_len;
dudmuck 9:a0ce66c18ec0 61 uint8_t beacon_payload[4];
dudmuck 12:9a8c13c4298b 62 uint32_t tx_ms;
dudmuck 0:2ff18de8d48b 63
dudmuck 0:2ff18de8d48b 64 unsigned int skip_beacon_cnt;
dudmuck 0:2ff18de8d48b 65
dudmuck 0:2ff18de8d48b 66 SX127x_lora lora(radio);
dudmuck 0:2ff18de8d48b 67
dudmuck 0:2ff18de8d48b 68 #define LORAMAC_FIRST_CHANNEL ( (uint32_t)910.0e6 )
dudmuck 0:2ff18de8d48b 69 #define LORAMAC_STEPWIDTH_CHANNEL ( (uint32_t)800e3 )
dudmuck 0:2ff18de8d48b 70 #define LORA_MAX_NB_CHANNELS 8
dudmuck 12:9a8c13c4298b 71 float starting_bg_rssi;
dudmuck 0:2ff18de8d48b 72
dudmuck 0:2ff18de8d48b 73 #define N_SAMPLES 64
dudmuck 0:2ff18de8d48b 74 void channel_scan()
dudmuck 0:2ff18de8d48b 75 {
dudmuck 0:2ff18de8d48b 76 int min_ch, ch;
dudmuck 0:2ff18de8d48b 77 uint32_t hz = LORAMAC_FIRST_CHANNEL;
dudmuck 0:2ff18de8d48b 78 int acc[LORA_MAX_NB_CHANNELS];
dudmuck 0:2ff18de8d48b 79
dudmuck 0:2ff18de8d48b 80 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 81
dudmuck 0:2ff18de8d48b 82 for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
dudmuck 0:2ff18de8d48b 83 int i;
dudmuck 0:2ff18de8d48b 84 float MHz = (float)hz / 1e6;
dudmuck 0:2ff18de8d48b 85 radio.set_frf_MHz(MHz);
dudmuck 0:2ff18de8d48b 86 radio.set_opmode(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 87 acc[ch] = 0;
dudmuck 0:2ff18de8d48b 88 for (i = 0; i < N_SAMPLES; i++) {
dudmuck 0:2ff18de8d48b 89 int rssi = lora.get_current_rssi();
dudmuck 0:2ff18de8d48b 90 acc[ch] += rssi;
dudmuck 0:2ff18de8d48b 91 wait(0.01);
dudmuck 0:2ff18de8d48b 92 }
dudmuck 0:2ff18de8d48b 93 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 94 printf("ch%u: %f\r\n", ch, acc[ch] / (float)N_SAMPLES);
dudmuck 0:2ff18de8d48b 95 hz += LORAMAC_STEPWIDTH_CHANNEL;
dudmuck 0:2ff18de8d48b 96 radio.set_frf_MHz((float)hz/1e6);
dudmuck 0:2ff18de8d48b 97 }
dudmuck 0:2ff18de8d48b 98
dudmuck 0:2ff18de8d48b 99 int min = 0x7fffffff;
dudmuck 0:2ff18de8d48b 100 min_ch = 0;
dudmuck 0:2ff18de8d48b 101 for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
dudmuck 0:2ff18de8d48b 102 if (acc[ch] < min) {
dudmuck 0:2ff18de8d48b 103 min = acc[ch];
dudmuck 0:2ff18de8d48b 104 min_ch = ch;
dudmuck 0:2ff18de8d48b 105 }
dudmuck 0:2ff18de8d48b 106 }
dudmuck 0:2ff18de8d48b 107 hz = LORAMAC_FIRST_CHANNEL + (min_ch * LORAMAC_STEPWIDTH_CHANNEL);
dudmuck 0:2ff18de8d48b 108 printf("using ch%u, %luhz\r\n", min_ch, hz);
dudmuck 0:2ff18de8d48b 109 radio.set_frf_MHz((float)hz/1e6);
dudmuck 12:9a8c13c4298b 110
dudmuck 12:9a8c13c4298b 111 starting_bg_rssi = acc[min_ch] / (float)N_SAMPLES;
dudmuck 12:9a8c13c4298b 112 }
dudmuck 12:9a8c13c4298b 113
dudmuck 12:9a8c13c4298b 114 void measure_ambient()
dudmuck 12:9a8c13c4298b 115 {
dudmuck 12:9a8c13c4298b 116 int i, acc = 0;
dudmuck 12:9a8c13c4298b 117 float bg_rssi;
dudmuck 12:9a8c13c4298b 118 float diff;
dudmuck 12:9a8c13c4298b 119 static unsigned cnt = 0;
dudmuck 12:9a8c13c4298b 120
dudmuck 12:9a8c13c4298b 121 for (i = 0; i < N_SAMPLES; i++) {
dudmuck 12:9a8c13c4298b 122 int rssi = lora.get_current_rssi();
dudmuck 12:9a8c13c4298b 123 acc += rssi;
dudmuck 12:9a8c13c4298b 124 wait(0.01);
dudmuck 12:9a8c13c4298b 125 }
dudmuck 12:9a8c13c4298b 126 bg_rssi = acc / (float)N_SAMPLES;
dudmuck 12:9a8c13c4298b 127 diff = bg_rssi - starting_bg_rssi;
dudmuck 12:9a8c13c4298b 128 printf("bg_rssi:%.1fdBm vs %1.fdBm, diff:%.1f, %d\r\n", bg_rssi, starting_bg_rssi, diff, cnt);
dudmuck 12:9a8c13c4298b 129 if (diff > 10) {
dudmuck 12:9a8c13c4298b 130 if (++cnt > 3) {
dudmuck 12:9a8c13c4298b 131 /* find better channel */
dudmuck 12:9a8c13c4298b 132 channel_scan();
dudmuck 12:9a8c13c4298b 133 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 12:9a8c13c4298b 134 cnt = 0;
dudmuck 12:9a8c13c4298b 135 }
dudmuck 12:9a8c13c4298b 136 } else
dudmuck 12:9a8c13c4298b 137 cnt = 0;
dudmuck 0:2ff18de8d48b 138 }
dudmuck 0:2ff18de8d48b 139
dudmuck 0:2ff18de8d48b 140 void init_radio()
dudmuck 0:2ff18de8d48b 141 {
dudmuck 0:2ff18de8d48b 142 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 143
dudmuck 0:2ff18de8d48b 144 radio.RegPaConfig.bits.OutputPower = 15;
dudmuck 0:2ff18de8d48b 145 radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
dudmuck 0:2ff18de8d48b 146 lora.enable();
dudmuck 0:2ff18de8d48b 147 lora.setBw_KHz(500);
dudmuck 0:2ff18de8d48b 148 lora.setSf(LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);
dudmuck 0:2ff18de8d48b 149 printf("using sf%u\r\n", LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);
dudmuck 0:2ff18de8d48b 150
dudmuck 0:2ff18de8d48b 151 channel_scan();
dudmuck 0:2ff18de8d48b 152
dudmuck 0:2ff18de8d48b 153 radio.write_reg(REG_LR_SYNC_BYTE, LORA_MAC_PUBLIC_SYNCWORD);
dudmuck 0:2ff18de8d48b 154 radio.write_reg(REG_LR_RX_MAX_PAYLOADLENGTH, 255);
dudmuck 0:2ff18de8d48b 155 }
dudmuck 0:2ff18de8d48b 156
dudmuck 0:2ff18de8d48b 157 void printLoraIrqs(bool clear)
dudmuck 0:2ff18de8d48b 158 {
dudmuck 0:2ff18de8d48b 159 printf("\r\nIrqFlags:");
dudmuck 0:2ff18de8d48b 160 if (lora.RegIrqFlags.bits.CadDetected)
dudmuck 0:2ff18de8d48b 161 printf("CadDetected ");
dudmuck 0:2ff18de8d48b 162 if (lora.RegIrqFlags.bits.FhssChangeChannel) {
dudmuck 0:2ff18de8d48b 163 printf("FhssChangeChannel:%d ", lora.RegHopChannel.bits.FhssPresentChannel);
dudmuck 0:2ff18de8d48b 164 }
dudmuck 0:2ff18de8d48b 165 if (lora.RegIrqFlags.bits.CadDone)
dudmuck 0:2ff18de8d48b 166 printf("CadDone ");
dudmuck 0:2ff18de8d48b 167 if (lora.RegIrqFlags.bits.TxDone)
dudmuck 12:9a8c13c4298b 168 printf("TxDone-dio0:%d ", radio.dio0.read());
dudmuck 0:2ff18de8d48b 169 if (lora.RegIrqFlags.bits.ValidHeader)
dudmuck 0:2ff18de8d48b 170 printf("ValidHeader ");
dudmuck 0:2ff18de8d48b 171 if (lora.RegIrqFlags.bits.PayloadCrcError)
dudmuck 0:2ff18de8d48b 172 printf("PayloadCrcError ");
dudmuck 0:2ff18de8d48b 173 if (lora.RegIrqFlags.bits.RxDone)
dudmuck 0:2ff18de8d48b 174 printf("RxDone ");
dudmuck 0:2ff18de8d48b 175 if (lora.RegIrqFlags.bits.RxTimeout)
dudmuck 0:2ff18de8d48b 176 printf("RxTimeout ");
dudmuck 0:2ff18de8d48b 177
dudmuck 0:2ff18de8d48b 178 printf("\r\n");
dudmuck 0:2ff18de8d48b 179
dudmuck 0:2ff18de8d48b 180 if (clear)
dudmuck 0:2ff18de8d48b 181 radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);
dudmuck 0:2ff18de8d48b 182 }
dudmuck 0:2ff18de8d48b 183
dudmuck 0:2ff18de8d48b 184 void printOpMode()
dudmuck 0:2ff18de8d48b 185 {
dudmuck 0:2ff18de8d48b 186 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 0:2ff18de8d48b 187 switch (radio.RegOpMode.bits.Mode) {
dudmuck 0:2ff18de8d48b 188 case RF_OPMODE_SLEEP: printf("sleep"); break;
dudmuck 0:2ff18de8d48b 189 case RF_OPMODE_STANDBY: printf("stby"); break;
dudmuck 0:2ff18de8d48b 190 case RF_OPMODE_SYNTHESIZER_TX: printf("fstx"); break;
dudmuck 0:2ff18de8d48b 191 case RF_OPMODE_TRANSMITTER: printf("tx"); break;
dudmuck 0:2ff18de8d48b 192 case RF_OPMODE_SYNTHESIZER_RX: printf("fsrx"); break;
dudmuck 0:2ff18de8d48b 193 case RF_OPMODE_RECEIVER: printf("rx"); break;
dudmuck 0:2ff18de8d48b 194 case 6:
dudmuck 0:2ff18de8d48b 195 if (radio.RegOpMode.bits.LongRangeMode)
dudmuck 0:2ff18de8d48b 196 printf("rxs");
dudmuck 0:2ff18de8d48b 197 else
dudmuck 0:2ff18de8d48b 198 printf("-6-");
dudmuck 0:2ff18de8d48b 199 break; // todo: different lora/fsk
dudmuck 0:2ff18de8d48b 200 case 7:
dudmuck 0:2ff18de8d48b 201 if (radio.RegOpMode.bits.LongRangeMode)
dudmuck 0:2ff18de8d48b 202 printf("cad");
dudmuck 0:2ff18de8d48b 203 else
dudmuck 0:2ff18de8d48b 204 printf("-7-");
dudmuck 0:2ff18de8d48b 205 break; // todo: different lora/fsk
dudmuck 0:2ff18de8d48b 206 }
dudmuck 0:2ff18de8d48b 207 }
dudmuck 0:2ff18de8d48b 208
dudmuck 9:a0ce66c18ec0 209 #define GPO_IDX 6
dudmuck 9:a0ce66c18ec0 210 void decrypted_uplink(uint8_t* buf, uint8_t buflen, uint8_t port)
dudmuck 9:a0ce66c18ec0 211 {
dudmuck 9:a0ce66c18ec0 212 if (port == SENSOR_PORT) {
dudmuck 9:a0ce66c18ec0 213 uint16_t a_1, a_3, lum;
dudmuck 9:a0ce66c18ec0 214 a_1 = buf[0] << 8;
dudmuck 9:a0ce66c18ec0 215 a_1 += buf[1];
dudmuck 9:a0ce66c18ec0 216 a_3 = buf[2] << 8;
dudmuck 9:a0ce66c18ec0 217 a_3 += buf[3];
dudmuck 9:a0ce66c18ec0 218 lum = buf[4] << 8;
dudmuck 9:a0ce66c18ec0 219 lum += buf[5];
dudmuck 10:6783623cc886 220 printf("SENSOR gpi:%u, gpo:%u, a1:%u, a3:%u, lum:%u\r\n", (buf[GPO_IDX] & 2) >> 1, buf[GPO_IDX] & 1, a_1, a_3, lum);
dudmuck 9:a0ce66c18ec0 221 } else if (port == TEXT_PORT) {
dudmuck 9:a0ce66c18ec0 222 buf[buflen] = 0;
dudmuck 9:a0ce66c18ec0 223 printf("TEXT %s\r\n", buf);
dudmuck 9:a0ce66c18ec0 224 } else {
dudmuck 9:a0ce66c18ec0 225 int i;
dudmuck 9:a0ce66c18ec0 226 printf("port%u: ", port);
dudmuck 9:a0ce66c18ec0 227 for (i = 0; i < buflen; i++)
dudmuck 9:a0ce66c18ec0 228 printf("%02x ", buf[i]);
dudmuck 9:a0ce66c18ec0 229 printf("\r\n");
dudmuck 9:a0ce66c18ec0 230 }
dudmuck 9:a0ce66c18ec0 231 }
dudmuck 9:a0ce66c18ec0 232
dudmuck 0:2ff18de8d48b 233 EventQueue queue;
dudmuck 3:7c01b8978638 234
dudmuck 3:7c01b8978638 235 volatile bool get_tx_done;
dudmuck 3:7c01b8978638 236 volatile bool beacon_guard;
dudmuck 0:2ff18de8d48b 237 bool restore_tx_invert;
dudmuck 3:7c01b8978638 238 bool restore_header_mode;
dudmuck 0:2ff18de8d48b 239
dudmuck 0:2ff18de8d48b 240 void send_downlink()
dudmuck 0:2ff18de8d48b 241 {
dudmuck 12:9a8c13c4298b 242 if (!beacon_guard && LoRaWan::do_downlink) {
dudmuck 0:2ff18de8d48b 243 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 244 radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 245 lora.invert_tx(true);
dudmuck 0:2ff18de8d48b 246 restore_tx_invert = true;
dudmuck 0:2ff18de8d48b 247 lora.setRxPayloadCrcOn(false);
dudmuck 0:2ff18de8d48b 248 lora.start_tx(lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 249 LoRaWan::do_downlink = false;
dudmuck 3:7c01b8978638 250 get_tx_done = true;
dudmuck 12:9a8c13c4298b 251 tx_ms = timer.read_ms();
dudmuck 0:2ff18de8d48b 252 }
dudmuck 0:2ff18de8d48b 253 }
dudmuck 0:2ff18de8d48b 254
dudmuck 12:9a8c13c4298b 255
dudmuck 0:2ff18de8d48b 256 void
dudmuck 0:2ff18de8d48b 257 service_radio()
dudmuck 0:2ff18de8d48b 258 {
dudmuck 0:2ff18de8d48b 259 service_action_e act = lora.service();
dudmuck 0:2ff18de8d48b 260
dudmuck 0:2ff18de8d48b 261 switch (act) {
dudmuck 0:2ff18de8d48b 262 case SERVICE_ERROR:
dudmuck 0:2ff18de8d48b 263 printf("SERVICE_ERROR\r\n");
dudmuck 0:2ff18de8d48b 264 case SERVICE_TX_DONE:
dudmuck 0:2ff18de8d48b 265 case SERVICE_NONE:
dudmuck 0:2ff18de8d48b 266 break;
dudmuck 0:2ff18de8d48b 267 case SERVICE_READ_FIFO:
dudmuck 3:7c01b8978638 268 LoRaWan::rx_slot = tim_get_current_slot();
dudmuck 3:7c01b8978638 269 LoRaWan::rx_ms = timer.read_ms();
dudmuck 11:50d0558a4e37 270 printf("%u, %.1fdB, %ddBm, ", time(NULL), lora.RegPktSnrValue / 4.0, lora.get_pkt_rssi());
dudmuck 12:9a8c13c4298b 271 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 12:9a8c13c4298b 272 LoRaWan::parse_receive();
dudmuck 12:9a8c13c4298b 273 if (!LoRaWan::do_downlink) {
dudmuck 12:9a8c13c4298b 274 /* if not sending downlink, start receiver now */
dudmuck 3:7c01b8978638 275 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 276 }
dudmuck 12:9a8c13c4298b 277
dudmuck 0:2ff18de8d48b 278 break;
dudmuck 0:2ff18de8d48b 279 } // ..switch (act)
dudmuck 0:2ff18de8d48b 280 }
dudmuck 0:2ff18de8d48b 281
dudmuck 0:2ff18de8d48b 282 volatile float prev_beacon_send_at;
dudmuck 0:2ff18de8d48b 283 volatile float beacon_send_at;
dudmuck 0:2ff18de8d48b 284 volatile float beacon_loaded_at;
dudmuck 0:2ff18de8d48b 285
dudmuck 0:2ff18de8d48b 286 volatile bool beacon_loaded;
dudmuck 0:2ff18de8d48b 287 void
dudmuck 0:2ff18de8d48b 288 send_beacon()
dudmuck 0:2ff18de8d48b 289 {
dudmuck 0:2ff18de8d48b 290 prev_beacon_send_at = beacon_send_at;
dudmuck 0:2ff18de8d48b 291 beacon_send_at = timer.read();
dudmuck 0:2ff18de8d48b 292
dudmuck 0:2ff18de8d48b 293 if (!beacon_loaded)
dudmuck 0:2ff18de8d48b 294 return;
dudmuck 0:2ff18de8d48b 295
dudmuck 0:2ff18de8d48b 296 radio.set_opmode(RF_OPMODE_TRANSMITTER);
dudmuck 0:2ff18de8d48b 297 beacon_loaded = false;
dudmuck 3:7c01b8978638 298 get_tx_done = true;
dudmuck 12:9a8c13c4298b 299 tx_ms = timer.read_ms();
dudmuck 0:2ff18de8d48b 300 }
dudmuck 0:2ff18de8d48b 301
dudmuck 0:2ff18de8d48b 302 static uint16_t beacon_crc( uint8_t *buffer, uint16_t length )
dudmuck 0:2ff18de8d48b 303 {
dudmuck 0:2ff18de8d48b 304 // The CRC calculation follows CCITT
dudmuck 0:2ff18de8d48b 305 const uint16_t polynom = 0x1021;
dudmuck 0:2ff18de8d48b 306 // CRC initial value
dudmuck 0:2ff18de8d48b 307 uint16_t crc = 0x0000;
dudmuck 0:2ff18de8d48b 308
dudmuck 0:2ff18de8d48b 309 if( buffer == NULL )
dudmuck 0:2ff18de8d48b 310 {
dudmuck 0:2ff18de8d48b 311 return 0;
dudmuck 0:2ff18de8d48b 312 }
dudmuck 0:2ff18de8d48b 313
dudmuck 0:2ff18de8d48b 314 for( uint16_t i = 0; i < length; ++i )
dudmuck 0:2ff18de8d48b 315 {
dudmuck 0:2ff18de8d48b 316 crc ^= ( uint16_t ) buffer[i] << 8;
dudmuck 0:2ff18de8d48b 317 for( uint16_t j = 0; j < 8; ++j )
dudmuck 0:2ff18de8d48b 318 {
dudmuck 0:2ff18de8d48b 319 crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
dudmuck 0:2ff18de8d48b 320 }
dudmuck 0:2ff18de8d48b 321 }
dudmuck 0:2ff18de8d48b 322
dudmuck 0:2ff18de8d48b 323 return crc;
dudmuck 0:2ff18de8d48b 324 }
dudmuck 0:2ff18de8d48b 325
dudmuck 0:2ff18de8d48b 326 void
dudmuck 3:7c01b8978638 327 _load_beacon()
dudmuck 0:2ff18de8d48b 328 {
dudmuck 0:2ff18de8d48b 329 uint16_t crc;
dudmuck 12:9a8c13c4298b 330 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 12:9a8c13c4298b 331 // clear any flags which might have been set during beacon_guard
dudmuck 12:9a8c13c4298b 332 radio.write_reg(REG_LR_IRQFLAGS, 0xff);
dudmuck 0:2ff18de8d48b 333 lora.RegPayloadLength = BEACON_SIZE;
dudmuck 0:2ff18de8d48b 334 radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 335 lora.setHeaderMode(true);
dudmuck 3:7c01b8978638 336 restore_header_mode = true;
dudmuck 0:2ff18de8d48b 337
dudmuck 0:2ff18de8d48b 338 if (skip_beacon_cnt > 0) {
dudmuck 1:107435401168 339 //printf("skip_beacon_cnt:%d\r\n", skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 340 lora.invert_tx(true);
dudmuck 0:2ff18de8d48b 341 restore_tx_invert = true;
dudmuck 0:2ff18de8d48b 342 skip_beacon_cnt--;
dudmuck 0:2ff18de8d48b 343 }
dudmuck 0:2ff18de8d48b 344
dudmuck 9:a0ce66c18ec0 345 radio.tx_buf[0] = beacon_payload[0];
dudmuck 9:a0ce66c18ec0 346 radio.tx_buf[1] = beacon_payload[1];
dudmuck 9:a0ce66c18ec0 347 radio.tx_buf[2] = beacon_payload[2];
dudmuck 9:a0ce66c18ec0 348 radio.tx_buf[3] = beacon_payload[3];
dudmuck 9:a0ce66c18ec0 349 beacon_payload[0] = CMD_NONE;
dudmuck 9:a0ce66c18ec0 350
dudmuck 0:2ff18de8d48b 351 crc = beacon_crc(radio.tx_buf, 4);
dudmuck 0:2ff18de8d48b 352 radio.tx_buf[4] = crc & 0xff;
dudmuck 0:2ff18de8d48b 353 radio.tx_buf[5] = crc >> 8;
dudmuck 0:2ff18de8d48b 354
dudmuck 0:2ff18de8d48b 355 // DIO0 to TxDone
dudmuck 3:7c01b8978638 356 radio.RegDioMapping1.bits.Dio0Mapping = 1;
dudmuck 3:7c01b8978638 357 radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
dudmuck 0:2ff18de8d48b 358
dudmuck 0:2ff18de8d48b 359 // set FifoPtrAddr to FifoTxPtrBase
dudmuck 0:2ff18de8d48b 360 radio.write_reg(REG_LR_FIFOADDRPTR, radio.read_reg(REG_LR_FIFOTXBASEADDR));
dudmuck 0:2ff18de8d48b 361
dudmuck 0:2ff18de8d48b 362 // write PayloadLength bytes to fifo
dudmuck 0:2ff18de8d48b 363 lora.write_fifo(lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 364
dudmuck 0:2ff18de8d48b 365 // prepare for tx to occur in send_beacon()
dudmuck 0:2ff18de8d48b 366 radio.set_opmode(RF_OPMODE_SYNTHESIZER_TX);
dudmuck 0:2ff18de8d48b 367 beacon_loaded = true;
dudmuck 0:2ff18de8d48b 368
dudmuck 0:2ff18de8d48b 369 beacon_loaded_at = timer.read();
dudmuck 0:2ff18de8d48b 370 }
dudmuck 0:2ff18de8d48b 371
dudmuck 3:7c01b8978638 372 void load_beacon()
dudmuck 3:7c01b8978638 373 {
dudmuck 3:7c01b8978638 374 beacon_guard = true;
dudmuck 12:9a8c13c4298b 375 queue.call_in(200, _load_beacon);
dudmuck 3:7c01b8978638 376 }
dudmuck 3:7c01b8978638 377
dudmuck 0:2ff18de8d48b 378 void get_time_till_beacon()
dudmuck 0:2ff18de8d48b 379 {
dudmuck 0:2ff18de8d48b 380 uint16_t slots = tim_get_current_slot();
dudmuck 0:2ff18de8d48b 381 printf("slots:%u\r\n", slots);
dudmuck 0:2ff18de8d48b 382 }
dudmuck 0:2ff18de8d48b 383
dudmuck 4:7e743e402681 384 void rx_isr()
dudmuck 0:2ff18de8d48b 385 {
dudmuck 0:2ff18de8d48b 386 static uint8_t pcbuf_idx = 0;
dudmuck 0:2ff18de8d48b 387 static uint8_t prev_len = 0;;
dudmuck 0:2ff18de8d48b 388 char c = pc.getc();
dudmuck 0:2ff18de8d48b 389
dudmuck 0:2ff18de8d48b 390 if (c == 8) {
dudmuck 0:2ff18de8d48b 391 if (pcbuf_idx > 0) {
dudmuck 0:2ff18de8d48b 392 pc.putc(8);
dudmuck 0:2ff18de8d48b 393 pc.putc(' ');
dudmuck 0:2ff18de8d48b 394 pc.putc(8);
dudmuck 0:2ff18de8d48b 395 pcbuf_idx--;
dudmuck 0:2ff18de8d48b 396 }
dudmuck 0:2ff18de8d48b 397 } else if (c == 3) { // ctrl-C
dudmuck 0:2ff18de8d48b 398 pcbuf_len = -1;
dudmuck 0:2ff18de8d48b 399 } else if (c == '\r') {
dudmuck 0:2ff18de8d48b 400 if (pcbuf_idx == 0) {
dudmuck 0:2ff18de8d48b 401 pcbuf_len = prev_len;
dudmuck 0:2ff18de8d48b 402 } else {
dudmuck 0:2ff18de8d48b 403 pcbuf[pcbuf_idx] = 0; // null terminate
dudmuck 0:2ff18de8d48b 404 prev_len = pcbuf_idx;
dudmuck 0:2ff18de8d48b 405 pcbuf_idx = 0;
dudmuck 0:2ff18de8d48b 406 pcbuf_len = prev_len;
dudmuck 0:2ff18de8d48b 407 }
dudmuck 0:2ff18de8d48b 408 } else if (pcbuf_idx < sizeof(pcbuf)) {
dudmuck 0:2ff18de8d48b 409 pcbuf[pcbuf_idx++] = c;
dudmuck 0:2ff18de8d48b 410 pc.putc(c);
dudmuck 0:2ff18de8d48b 411 }
dudmuck 0:2ff18de8d48b 412 }
dudmuck 0:2ff18de8d48b 413
dudmuck 0:2ff18de8d48b 414 void cmd_skip_beacon(uint8_t idx)
dudmuck 0:2ff18de8d48b 415 {
dudmuck 0:2ff18de8d48b 416 if (pcbuf[idx] >= '0' && pcbuf[idx] <= '9') {
dudmuck 0:2ff18de8d48b 417 sscanf(pcbuf+idx, "%u", &skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 418 }
dudmuck 0:2ff18de8d48b 419 printf("skip_beacon_cnt:%u\r\n", skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 420 }
dudmuck 0:2ff18de8d48b 421
dudmuck 0:2ff18de8d48b 422 void cmd_list_motes(uint8_t idx)
dudmuck 0:2ff18de8d48b 423 {
dudmuck 0:2ff18de8d48b 424 int i;
dudmuck 0:2ff18de8d48b 425 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 426 if (motes[i].dev_addr != DEVADDR_NONE) {
dudmuck 9:a0ce66c18ec0 427 LoRaWan::print_octets_rev("", motes[i].dev_eui, LORA_EUI_LENGTH);
dudmuck 9:a0ce66c18ec0 428 printf(" %lx\r\n", motes[i].dev_addr);
dudmuck 0:2ff18de8d48b 429 }
dudmuck 0:2ff18de8d48b 430 }
dudmuck 0:2ff18de8d48b 431 }
dudmuck 0:2ff18de8d48b 432
dudmuck 0:2ff18de8d48b 433 void
dudmuck 0:2ff18de8d48b 434 cmd_beacon_payload(uint8_t idx)
dudmuck 0:2ff18de8d48b 435 {
dudmuck 9:a0ce66c18ec0 436 uint32_t i;
dudmuck 9:a0ce66c18ec0 437 uint32_t* ptr;
dudmuck 9:a0ce66c18ec0 438 sscanf(pcbuf+idx, "%x", &i);
dudmuck 9:a0ce66c18ec0 439 printf("beacon_payload:%08x\r\n", i);
dudmuck 9:a0ce66c18ec0 440 ptr = (uint32_t*)beacon_payload;
dudmuck 9:a0ce66c18ec0 441 *ptr = i;
dudmuck 8:307f7faeb594 442 }
dudmuck 8:307f7faeb594 443
dudmuck 0:2ff18de8d48b 444 void
dudmuck 0:2ff18de8d48b 445 cmd_send_downlink(uint8_t idx)
dudmuck 0:2ff18de8d48b 446 {
dudmuck 0:2ff18de8d48b 447 ota_mote_t* mote = NULL;
dudmuck 0:2ff18de8d48b 448 int i;
dudmuck 0:2ff18de8d48b 449 unsigned int dev_addr;
dudmuck 0:2ff18de8d48b 450 sscanf(pcbuf+idx, "%x", &dev_addr);
dudmuck 0:2ff18de8d48b 451 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 452 if (motes[i].dev_addr == dev_addr) {
dudmuck 0:2ff18de8d48b 453 break;
dudmuck 0:2ff18de8d48b 454 }
dudmuck 0:2ff18de8d48b 455 }
dudmuck 0:2ff18de8d48b 456 if (i == N_MOTES) {
dudmuck 0:2ff18de8d48b 457 printf("mote %x not found\r\n", dev_addr);
dudmuck 0:2ff18de8d48b 458 return;
dudmuck 0:2ff18de8d48b 459 }
dudmuck 0:2ff18de8d48b 460 mote = &motes[i];
dudmuck 0:2ff18de8d48b 461
dudmuck 0:2ff18de8d48b 462 while (pcbuf[idx] != ' ') {
dudmuck 0:2ff18de8d48b 463 if (pcbuf[++idx] == 0) {
dudmuck 0:2ff18de8d48b 464 printf("hit end\r\n");
dudmuck 0:2ff18de8d48b 465 return;
dudmuck 0:2ff18de8d48b 466 }
dudmuck 0:2ff18de8d48b 467 }
dudmuck 0:2ff18de8d48b 468 idx++; // step past space
dudmuck 0:2ff18de8d48b 469
dudmuck 0:2ff18de8d48b 470 mote->user_downlink_length = 0;
dudmuck 0:2ff18de8d48b 471 while (pcbuf[idx] > ' ') {
dudmuck 0:2ff18de8d48b 472 int o;
dudmuck 0:2ff18de8d48b 473 sscanf(pcbuf+idx, "%02x", &o);
dudmuck 0:2ff18de8d48b 474 LoRaWan::user_downlink[mote->user_downlink_length++] = o;
dudmuck 0:2ff18de8d48b 475 idx += 2;
dudmuck 0:2ff18de8d48b 476 }
dudmuck 0:2ff18de8d48b 477
dudmuck 0:2ff18de8d48b 478 printf("%u bytes scheduled for %lx\r\n", mote->user_downlink_length, mote->dev_addr);
dudmuck 0:2ff18de8d48b 479 }
dudmuck 0:2ff18de8d48b 480
dudmuck 9:a0ce66c18ec0 481 void cmd_rx_restart(uint8_t idx)
dudmuck 9:a0ce66c18ec0 482 {
dudmuck 9:a0ce66c18ec0 483 /*radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 9:a0ce66c18ec0 484 printf("standby\r\n");*/
dudmuck 9:a0ce66c18ec0 485 radio.set_opmode(RF_OPMODE_SLEEP);
dudmuck 9:a0ce66c18ec0 486 printf("sleep\r\n");
dudmuck 9:a0ce66c18ec0 487 wait(0.05);
dudmuck 9:a0ce66c18ec0 488 radio.set_opmode(RF_OPMODE_RECEIVER);
dudmuck 9:a0ce66c18ec0 489 printf("receive\r\n");
dudmuck 9:a0ce66c18ec0 490 }
dudmuck 9:a0ce66c18ec0 491
dudmuck 9:a0ce66c18ec0 492 void cmd_beacon_gpo(uint8_t idx)
dudmuck 9:a0ce66c18ec0 493 {
dudmuck 9:a0ce66c18ec0 494 int gpo;
dudmuck 9:a0ce66c18ec0 495 sscanf(pcbuf+idx, "%d", &gpo);
dudmuck 9:a0ce66c18ec0 496 beacon_payload[0] = CMD_GPIO_OUT;
dudmuck 9:a0ce66c18ec0 497 beacon_payload[1] = gpo;
dudmuck 9:a0ce66c18ec0 498 printf("beacon gpo: %d\r\n", gpo);
dudmuck 9:a0ce66c18ec0 499 }
dudmuck 9:a0ce66c18ec0 500
dudmuck 9:a0ce66c18ec0 501 void cmd_beacon_rgb(uint8_t idx)
dudmuck 9:a0ce66c18ec0 502 {
dudmuck 9:a0ce66c18ec0 503 int r, g ,b;
dudmuck 9:a0ce66c18ec0 504 sscanf(pcbuf+idx, "%d %d %d", &r, &g, &b);
dudmuck 9:a0ce66c18ec0 505 beacon_payload[0] = CMD_LED_RGB;
dudmuck 9:a0ce66c18ec0 506 beacon_payload[1] = r;
dudmuck 9:a0ce66c18ec0 507 beacon_payload[2] = g;
dudmuck 9:a0ce66c18ec0 508 beacon_payload[3] = b;
dudmuck 9:a0ce66c18ec0 509 printf("beacon rgb: %d %d %d\r\n", r, g, b);
dudmuck 9:a0ce66c18ec0 510 }
dudmuck 9:a0ce66c18ec0 511
dudmuck 9:a0ce66c18ec0 512 void cmd_downlink_rgb(uint8_t idx)
dudmuck 9:a0ce66c18ec0 513 {
dudmuck 9:a0ce66c18ec0 514 ota_mote_t* mote = NULL;
dudmuck 9:a0ce66c18ec0 515 int i, r, g ,b;
dudmuck 9:a0ce66c18ec0 516 unsigned int dev_addr;
dudmuck 9:a0ce66c18ec0 517 sscanf(pcbuf+idx, "%x %d %d %d", &dev_addr, &r, &g, &b);
dudmuck 9:a0ce66c18ec0 518 for (i = 0; i < N_MOTES; i++) {
dudmuck 9:a0ce66c18ec0 519 if (motes[i].dev_addr == dev_addr) {
dudmuck 9:a0ce66c18ec0 520 break;
dudmuck 9:a0ce66c18ec0 521 }
dudmuck 9:a0ce66c18ec0 522 }
dudmuck 9:a0ce66c18ec0 523 if (i == N_MOTES) {
dudmuck 9:a0ce66c18ec0 524 printf("mote %x not found\r\n", dev_addr);
dudmuck 9:a0ce66c18ec0 525 return;
dudmuck 9:a0ce66c18ec0 526 }
dudmuck 9:a0ce66c18ec0 527 mote = &motes[i];
dudmuck 9:a0ce66c18ec0 528
dudmuck 9:a0ce66c18ec0 529 mote->user_downlink_length = 0;
dudmuck 9:a0ce66c18ec0 530 LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_LED_RGB;
dudmuck 9:a0ce66c18ec0 531 LoRaWan::user_downlink[mote->user_downlink_length++] = r;
dudmuck 9:a0ce66c18ec0 532 LoRaWan::user_downlink[mote->user_downlink_length++] = g;
dudmuck 9:a0ce66c18ec0 533 LoRaWan::user_downlink[mote->user_downlink_length++] = b;
dudmuck 9:a0ce66c18ec0 534
dudmuck 9:a0ce66c18ec0 535 printf("rgb %d %d %d to mote %x\r\n", r, g, b, mote->dev_addr);
dudmuck 9:a0ce66c18ec0 536 }
dudmuck 9:a0ce66c18ec0 537
dudmuck 9:a0ce66c18ec0 538 void cmd_downlink_gpo(uint8_t idx)
dudmuck 9:a0ce66c18ec0 539 {
dudmuck 9:a0ce66c18ec0 540 ota_mote_t* mote = NULL;
dudmuck 9:a0ce66c18ec0 541 int i, gpo;
dudmuck 9:a0ce66c18ec0 542 unsigned int dev_addr;
dudmuck 9:a0ce66c18ec0 543 sscanf(pcbuf+idx, "%x %d", &dev_addr, &gpo);
dudmuck 9:a0ce66c18ec0 544 for (i = 0; i < N_MOTES; i++) {
dudmuck 9:a0ce66c18ec0 545 if (motes[i].dev_addr == dev_addr) {
dudmuck 9:a0ce66c18ec0 546 break;
dudmuck 9:a0ce66c18ec0 547 }
dudmuck 9:a0ce66c18ec0 548 }
dudmuck 9:a0ce66c18ec0 549 if (i == N_MOTES) {
dudmuck 9:a0ce66c18ec0 550 printf("mote %x not found\r\n", dev_addr);
dudmuck 9:a0ce66c18ec0 551 return;
dudmuck 9:a0ce66c18ec0 552 }
dudmuck 9:a0ce66c18ec0 553 mote = &motes[i];
dudmuck 9:a0ce66c18ec0 554
dudmuck 9:a0ce66c18ec0 555 mote->user_downlink_length = 0;
dudmuck 9:a0ce66c18ec0 556 LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_GPIO_OUT;
dudmuck 9:a0ce66c18ec0 557 LoRaWan::user_downlink[mote->user_downlink_length++] = gpo;
dudmuck 9:a0ce66c18ec0 558
dudmuck 9:a0ce66c18ec0 559 printf("gpo %d to mote %x\r\n", gpo, mote->dev_addr);
dudmuck 9:a0ce66c18ec0 560 }
dudmuck 9:a0ce66c18ec0 561
dudmuck 0:2ff18de8d48b 562 void cmd_status(uint8_t idx)
dudmuck 0:2ff18de8d48b 563 {
dudmuck 0:2ff18de8d48b 564 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 2:9628d5e4b1bf 565 printf("%.3fMHz sf%ubw%u ", radio.get_frf_MHz(), lora.getSf(), lora.getBw());
dudmuck 0:2ff18de8d48b 566 printOpMode();
dudmuck 0:2ff18de8d48b 567 if (!radio.RegOpMode.bits.LongRangeMode) {
dudmuck 0:2ff18de8d48b 568 printf("FSK\r\n");
dudmuck 0:2ff18de8d48b 569 return;
dudmuck 0:2ff18de8d48b 570 }
dudmuck 0:2ff18de8d48b 571
dudmuck 0:2ff18de8d48b 572 lora.RegIrqFlags.octet = radio.read_reg(REG_LR_IRQFLAGS);
dudmuck 0:2ff18de8d48b 573 printLoraIrqs(false);
dudmuck 0:2ff18de8d48b 574
dudmuck 12:9a8c13c4298b 575 printf(" do_downlink:%u get_tx_done:%u, ", LoRaWan::do_downlink, get_tx_done);
dudmuck 9:a0ce66c18ec0 576 lora.RegTest33.octet = radio.read_reg(REG_LR_TEST33); // invert_i_q
dudmuck 9:a0ce66c18ec0 577 lora.RegDriftInvert.octet = radio.read_reg(REG_LR_DRIFT_INVERT);
dudmuck 9:a0ce66c18ec0 578 printf("modemstat:%02x, rxinv:%x,%x\r\n", radio.read_reg(REG_LR_MODEMSTAT), lora.RegTest33.octet, lora.RegDriftInvert.octet);
dudmuck 2:9628d5e4b1bf 579 radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
dudmuck 2:9628d5e4b1bf 580 printf("\r\nskip_beacon_cnt:%u, currently:%u dio0map:%u\r\n", skip_beacon_cnt, tim_get_current_slot(), radio.RegDioMapping1.bits.Dio0Mapping);
dudmuck 12:9a8c13c4298b 581 printf("FIfoAddrPtr:%02x RxBase:%02x\r\n", radio.read_reg(REG_LR_FIFOADDRPTR), radio.read_reg(REG_LR_FIFORXBASEADDR));
dudmuck 0:2ff18de8d48b 582 }
dudmuck 0:2ff18de8d48b 583
dudmuck 11:50d0558a4e37 584 void cmd_op(uint8_t idx)
dudmuck 11:50d0558a4e37 585 {
dudmuck 11:50d0558a4e37 586 int i, dbm;
dudmuck 11:50d0558a4e37 587 RegPdsTrim1_t pds_trim;
dudmuck 11:50d0558a4e37 588 uint8_t adr;
dudmuck 11:50d0558a4e37 589 if (radio.type == SX1276)
dudmuck 11:50d0558a4e37 590 adr = REG_PDSTRIM1_SX1276;
dudmuck 11:50d0558a4e37 591 else
dudmuck 11:50d0558a4e37 592 adr = REG_PDSTRIM1_SX1272;
dudmuck 11:50d0558a4e37 593
dudmuck 11:50d0558a4e37 594 pds_trim.octet = radio.read_reg(adr);
dudmuck 11:50d0558a4e37 595
dudmuck 11:50d0558a4e37 596 if (pcbuf[idx] >= '0' && (pcbuf[idx] <= '9' || pcbuf[idx] == '-')) {
dudmuck 11:50d0558a4e37 597 sscanf(pcbuf+idx, "%d", &i);
dudmuck 11:50d0558a4e37 598 if (radio.RegPaConfig.bits.PaSelect) {
dudmuck 11:50d0558a4e37 599 /* PABOOST used: +2dbm to +17, or +20 */
dudmuck 11:50d0558a4e37 600 if (i == 20) {
dudmuck 11:50d0558a4e37 601 printf("+20dBm PADAC bias\r\n");
dudmuck 11:50d0558a4e37 602 i -= 3;
dudmuck 11:50d0558a4e37 603 pds_trim.bits.prog_txdac = 7;
dudmuck 11:50d0558a4e37 604 radio.write_reg(adr, pds_trim.octet);
dudmuck 11:50d0558a4e37 605 }
dudmuck 11:50d0558a4e37 606 if (i > 1)
dudmuck 11:50d0558a4e37 607 radio.RegPaConfig.bits.OutputPower = i - 2;
dudmuck 11:50d0558a4e37 608 } else {
dudmuck 11:50d0558a4e37 609 /* RFO used: -1 to +14dbm */
dudmuck 11:50d0558a4e37 610 if (i < 15)
dudmuck 11:50d0558a4e37 611 radio.RegPaConfig.bits.OutputPower = i + 1;
dudmuck 11:50d0558a4e37 612 }
dudmuck 11:50d0558a4e37 613 radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
dudmuck 11:50d0558a4e37 614 }
dudmuck 11:50d0558a4e37 615 radio.RegPaConfig.octet = radio.read_reg(REG_PACONFIG);
dudmuck 11:50d0558a4e37 616 if (radio.RegPaConfig.bits.PaSelect) {
dudmuck 11:50d0558a4e37 617 printf("PA_BOOST ");
dudmuck 11:50d0558a4e37 618 dbm = radio.RegPaConfig.bits.OutputPower + pds_trim.bits.prog_txdac - 2;
dudmuck 11:50d0558a4e37 619 } else {
dudmuck 11:50d0558a4e37 620 printf("RFO ");
dudmuck 11:50d0558a4e37 621 dbm = radio.RegPaConfig.bits.OutputPower - 1;
dudmuck 11:50d0558a4e37 622 }
dudmuck 11:50d0558a4e37 623 printf("OutputPower:%ddBm\r\n", dbm);
dudmuck 11:50d0558a4e37 624 }
dudmuck 11:50d0558a4e37 625
dudmuck 12:9a8c13c4298b 626 void cmd_set_time(uint8_t idx)
dudmuck 12:9a8c13c4298b 627 {
dudmuck 12:9a8c13c4298b 628 set_time(0);
dudmuck 12:9a8c13c4298b 629 printf("time:%u\r\n", time(NULL));
dudmuck 12:9a8c13c4298b 630 }
dudmuck 12:9a8c13c4298b 631
dudmuck 12:9a8c13c4298b 632 void cmd_pwm_period(uint8_t idx)
dudmuck 12:9a8c13c4298b 633 {
dudmuck 12:9a8c13c4298b 634 float f;
dudmuck 12:9a8c13c4298b 635 void *fptr;
dudmuck 12:9a8c13c4298b 636 ota_mote_t* mote = NULL;
dudmuck 12:9a8c13c4298b 637 int i;
dudmuck 12:9a8c13c4298b 638 unsigned int dev_addr;
dudmuck 12:9a8c13c4298b 639 sscanf(pcbuf+idx, "%x %f", &dev_addr, &f);
dudmuck 12:9a8c13c4298b 640 for (i = 0; i < N_MOTES; i++) {
dudmuck 12:9a8c13c4298b 641 if (motes[i].dev_addr == dev_addr) {
dudmuck 12:9a8c13c4298b 642 break;
dudmuck 12:9a8c13c4298b 643 }
dudmuck 12:9a8c13c4298b 644 }
dudmuck 12:9a8c13c4298b 645 if (i == N_MOTES) {
dudmuck 12:9a8c13c4298b 646 printf("mote %x not found\r\n", dev_addr);
dudmuck 12:9a8c13c4298b 647 return;
dudmuck 12:9a8c13c4298b 648 }
dudmuck 12:9a8c13c4298b 649 mote = &motes[i];
dudmuck 12:9a8c13c4298b 650
dudmuck 12:9a8c13c4298b 651 mote->user_downlink_length = 0;
dudmuck 12:9a8c13c4298b 652 LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_PWM_PERIOD;
dudmuck 12:9a8c13c4298b 653 fptr = (float*)&LoRaWan::user_downlink[mote->user_downlink_length];
dudmuck 12:9a8c13c4298b 654 memcpy(fptr, &f, sizeof(f));
dudmuck 12:9a8c13c4298b 655 mote->user_downlink_length += sizeof(f);
dudmuck 12:9a8c13c4298b 656
dudmuck 12:9a8c13c4298b 657 printf("period %f to mote %x\r\n", f, mote->dev_addr);
dudmuck 12:9a8c13c4298b 658 }
dudmuck 12:9a8c13c4298b 659
dudmuck 12:9a8c13c4298b 660 void cmd_pwm_duty(uint8_t idx)
dudmuck 12:9a8c13c4298b 661 {
dudmuck 12:9a8c13c4298b 662 float f;
dudmuck 12:9a8c13c4298b 663 void* fptr;
dudmuck 12:9a8c13c4298b 664 ota_mote_t* mote = NULL;
dudmuck 12:9a8c13c4298b 665 int i;
dudmuck 12:9a8c13c4298b 666 unsigned int dev_addr;
dudmuck 12:9a8c13c4298b 667 sscanf(pcbuf+idx, "%x %f", &dev_addr, &f);
dudmuck 12:9a8c13c4298b 668 for (i = 0; i < N_MOTES; i++) {
dudmuck 12:9a8c13c4298b 669 if (motes[i].dev_addr == dev_addr) {
dudmuck 12:9a8c13c4298b 670 break;
dudmuck 12:9a8c13c4298b 671 }
dudmuck 12:9a8c13c4298b 672 }
dudmuck 12:9a8c13c4298b 673 if (i == N_MOTES) {
dudmuck 12:9a8c13c4298b 674 printf("mote %x not found\r\n", dev_addr);
dudmuck 12:9a8c13c4298b 675 return;
dudmuck 12:9a8c13c4298b 676 }
dudmuck 12:9a8c13c4298b 677 mote = &motes[i];
dudmuck 12:9a8c13c4298b 678
dudmuck 12:9a8c13c4298b 679 mote->user_downlink_length = 0;
dudmuck 12:9a8c13c4298b 680 LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_PWM_DUTY;
dudmuck 12:9a8c13c4298b 681 fptr = (float*)&LoRaWan::user_downlink[mote->user_downlink_length];
dudmuck 12:9a8c13c4298b 682 memcpy(fptr, &f, sizeof(float));
dudmuck 12:9a8c13c4298b 683 mote->user_downlink_length += sizeof(float);
dudmuck 12:9a8c13c4298b 684
dudmuck 12:9a8c13c4298b 685 printf("duty %f to mote %x\r\n", f, mote->dev_addr);
dudmuck 12:9a8c13c4298b 686 }
dudmuck 12:9a8c13c4298b 687
dudmuck 0:2ff18de8d48b 688 void cmd_help(uint8_t);
dudmuck 0:2ff18de8d48b 689
dudmuck 0:2ff18de8d48b 690 typedef struct {
dudmuck 0:2ff18de8d48b 691 const char* const cmd;
dudmuck 0:2ff18de8d48b 692 void (*handler)(uint8_t args_at);
dudmuck 0:2ff18de8d48b 693 const char* const arg_descr;
dudmuck 0:2ff18de8d48b 694 const char* const description;
dudmuck 0:2ff18de8d48b 695 } menu_item_t;
dudmuck 0:2ff18de8d48b 696
dudmuck 0:2ff18de8d48b 697 const menu_item_t menu_items[] =
dudmuck 0:2ff18de8d48b 698 { /* after first character, command names must be [A-Za-z] */
dudmuck 0:2ff18de8d48b 699 { "?", cmd_help, "","show available commands"},
dudmuck 0:2ff18de8d48b 700 { ".", cmd_status, "","read status"},
dudmuck 0:2ff18de8d48b 701 { "b", cmd_beacon_payload, "<%x>","set beacon payload"},
dudmuck 0:2ff18de8d48b 702 { "sb", cmd_skip_beacon, "<%d>","skip beacons"},
dudmuck 0:2ff18de8d48b 703 { "list", cmd_list_motes, "","list active motes"},
dudmuck 0:2ff18de8d48b 704 { "dl", cmd_send_downlink, "[%x %s]","send downlink <mote-hex-dev-addr> <hex-payload>"},
dudmuck 9:a0ce66c18ec0 705 { "rxr", cmd_rx_restart, "", "restart RX"},
dudmuck 9:a0ce66c18ec0 706 { "brgb", cmd_beacon_rgb, "%u %u %u", "load RGB command into next beacon" },
dudmuck 9:a0ce66c18ec0 707 { "rgb", cmd_downlink_rgb, "%x %u %u %u", "load RGB command to mote"},
dudmuck 9:a0ce66c18ec0 708 { "bgpo", cmd_beacon_gpo, "%d", "load output pin command into next beacon"},
dudmuck 9:a0ce66c18ec0 709 { "gpo", cmd_downlink_gpo, "%x %d", "load output pin command to mote"},
dudmuck 12:9a8c13c4298b 710 { "op", cmd_op, "<dBm>","(TX) get/set TX power"},
dudmuck 12:9a8c13c4298b 711 { "t0", cmd_set_time, "", "set seconds to zero"},
dudmuck 12:9a8c13c4298b 712 { "pp", cmd_pwm_period, "%x %f", "set pwm period (devaddr period)"},
dudmuck 12:9a8c13c4298b 713 { "pd", cmd_pwm_duty, "%x %f", "set pwm duty (devaddr duty)"},
dudmuck 0:2ff18de8d48b 714 { NULL, NULL, NULL, NULL }
dudmuck 0:2ff18de8d48b 715 };
dudmuck 0:2ff18de8d48b 716
dudmuck 0:2ff18de8d48b 717 void cmd_help(uint8_t args_at)
dudmuck 0:2ff18de8d48b 718 {
dudmuck 0:2ff18de8d48b 719 int i;
dudmuck 0:2ff18de8d48b 720
dudmuck 0:2ff18de8d48b 721 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:2ff18de8d48b 722 printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description);
dudmuck 0:2ff18de8d48b 723 }
dudmuck 0:2ff18de8d48b 724
dudmuck 0:2ff18de8d48b 725 }
dudmuck 0:2ff18de8d48b 726
dudmuck 0:2ff18de8d48b 727 void
dudmuck 0:2ff18de8d48b 728 console()
dudmuck 0:2ff18de8d48b 729 {
dudmuck 0:2ff18de8d48b 730 int i;
dudmuck 0:2ff18de8d48b 731 uint8_t user_cmd_len;
dudmuck 0:2ff18de8d48b 732
dudmuck 0:2ff18de8d48b 733 if (pcbuf_len < 0) { // ctrl-C
dudmuck 0:2ff18de8d48b 734 //printf("abort\r\n");
dudmuck 0:2ff18de8d48b 735 return;
dudmuck 0:2ff18de8d48b 736 }
dudmuck 0:2ff18de8d48b 737 if (pcbuf_len == 0)
dudmuck 0:2ff18de8d48b 738 return;
dudmuck 0:2ff18de8d48b 739
dudmuck 0:2ff18de8d48b 740 printf("\r\n");
dudmuck 0:2ff18de8d48b 741
dudmuck 0:2ff18de8d48b 742 /* get end of user-entered command */
dudmuck 0:2ff18de8d48b 743 user_cmd_len = 1; // first character can be any character
dudmuck 0:2ff18de8d48b 744 for (i = 1; i <= pcbuf_len; i++) {
dudmuck 0:2ff18de8d48b 745 if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') {
dudmuck 0:2ff18de8d48b 746 user_cmd_len = i;
dudmuck 0:2ff18de8d48b 747 break;
dudmuck 0:2ff18de8d48b 748 }
dudmuck 0:2ff18de8d48b 749 }
dudmuck 0:2ff18de8d48b 750
dudmuck 0:2ff18de8d48b 751 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:2ff18de8d48b 752 int mi_len = strlen(menu_items[i].cmd);
dudmuck 0:2ff18de8d48b 753
dudmuck 0:2ff18de8d48b 754 if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) {
dudmuck 0:2ff18de8d48b 755 while (pcbuf[mi_len] == ' ') // skip past spaces
dudmuck 0:2ff18de8d48b 756 mi_len++;
dudmuck 0:2ff18de8d48b 757 menu_items[i].handler(mi_len);
dudmuck 0:2ff18de8d48b 758 break;
dudmuck 0:2ff18de8d48b 759 }
dudmuck 0:2ff18de8d48b 760 }
dudmuck 0:2ff18de8d48b 761
dudmuck 0:2ff18de8d48b 762 pcbuf_len = 0;
dudmuck 0:2ff18de8d48b 763 printf("> ");
dudmuck 0:2ff18de8d48b 764 fflush(stdout);
dudmuck 0:2ff18de8d48b 765 }
dudmuck 0:2ff18de8d48b 766
dudmuck 0:2ff18de8d48b 767 int main()
dudmuck 0:2ff18de8d48b 768 {
dudmuck 0:2ff18de8d48b 769 Thread eventThread;
dudmuck 3:7c01b8978638 770 pc.baud(38400);
dudmuck 12:9a8c13c4298b 771 printf("\r\nreset %f\r\n", radio.get_frf_MHz());
dudmuck 11:50d0558a4e37 772 set_time(0);
dudmuck 0:2ff18de8d48b 773
dudmuck 0:2ff18de8d48b 774 radio.hw_reset();
dudmuck 12:9a8c13c4298b 775 printf("%fMHz\r\n", radio.get_frf_MHz());
dudmuck 0:2ff18de8d48b 776
dudmuck 10:6783623cc886 777 //#ifndef TARGET_DISCO_L072CZ_LRWAN1
dudmuck 10:6783623cc886 778 #ifndef TYPE_ABZ
dudmuck 0:2ff18de8d48b 779 rfsw.input();
dudmuck 0:2ff18de8d48b 780 if (rfsw.read()) {
dudmuck 0:2ff18de8d48b 781 printf("LAS\r\n");
dudmuck 0:2ff18de8d48b 782 /* LAS HF=PA_BOOST LF=RFO */
dudmuck 0:2ff18de8d48b 783 radio.RegPaConfig.bits.PaSelect = 1;
dudmuck 0:2ff18de8d48b 784 } else {
dudmuck 0:2ff18de8d48b 785 printf("MAS\r\n");
dudmuck 0:2ff18de8d48b 786 radio.RegPaConfig.bits.PaSelect = 0;
dudmuck 0:2ff18de8d48b 787 }
dudmuck 0:2ff18de8d48b 788 rfsw.output();
dudmuck 5:ceacaa560cfd 789 #endif /* !TARGET_DISCO_L072CZ_LRWAN1 */
dudmuck 0:2ff18de8d48b 790
dudmuck 0:2ff18de8d48b 791 radio.rf_switch = rfsw_callback;
dudmuck 0:2ff18de8d48b 792
dudmuck 0:2ff18de8d48b 793 init_radio();
dudmuck 0:2ff18de8d48b 794
dudmuck 0:2ff18de8d48b 795 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 796
dudmuck 0:2ff18de8d48b 797 eventThread.start(callback(&queue, &EventQueue::dispatch_forever));
dudmuck 0:2ff18de8d48b 798 LoRaWan::init();
dudmuck 0:2ff18de8d48b 799
dudmuck 0:2ff18de8d48b 800 timer.start();
dudmuck 0:2ff18de8d48b 801 tim_init();
dudmuck 4:7e743e402681 802
dudmuck 4:7e743e402681 803 pc.attach(&rx_isr);
dudmuck 9:a0ce66c18ec0 804
dudmuck 0:2ff18de8d48b 805
dudmuck 0:2ff18de8d48b 806 for (;;) {
dudmuck 0:2ff18de8d48b 807 console();
dudmuck 1:107435401168 808
dudmuck 1:107435401168 809 if (radio.dio0) {
dudmuck 3:7c01b8978638 810 if (get_tx_done) {
dudmuck 3:7c01b8978638 811 get_tx_done = false;
dudmuck 3:7c01b8978638 812 lora.RegIrqFlags.octet = 0;
dudmuck 3:7c01b8978638 813 lora.RegIrqFlags.bits.TxDone = 1;
dudmuck 3:7c01b8978638 814 radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);
dudmuck 3:7c01b8978638 815
dudmuck 3:7c01b8978638 816 if (restore_tx_invert) {
dudmuck 3:7c01b8978638 817 lora.invert_tx(false);
dudmuck 3:7c01b8978638 818 restore_tx_invert = false;
dudmuck 3:7c01b8978638 819 }
dudmuck 3:7c01b8978638 820
dudmuck 3:7c01b8978638 821 if (restore_header_mode) {
dudmuck 3:7c01b8978638 822 lora.setHeaderMode(false);
dudmuck 3:7c01b8978638 823 restore_header_mode = false;
dudmuck 3:7c01b8978638 824 }
dudmuck 12:9a8c13c4298b 825
dudmuck 3:7c01b8978638 826 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 12:9a8c13c4298b 827
dudmuck 12:9a8c13c4298b 828 if (beacon_guard) { // beacon done transmitting
dudmuck 12:9a8c13c4298b 829 measure_ambient();
dudmuck 12:9a8c13c4298b 830 beacon_guard = false;
dudmuck 12:9a8c13c4298b 831 }
dudmuck 3:7c01b8978638 832 } else {
dudmuck 3:7c01b8978638 833 if (!beacon_guard)
dudmuck 3:7c01b8978638 834 service_radio();
dudmuck 3:7c01b8978638 835 }
dudmuck 12:9a8c13c4298b 836 } else if (get_tx_done) {
dudmuck 12:9a8c13c4298b 837 /* dio0 not yet asserted */
dudmuck 12:9a8c13c4298b 838 uint32_t since = timer.read_ms() - tx_ms;
dudmuck 12:9a8c13c4298b 839 if (since > 4000) {
dudmuck 12:9a8c13c4298b 840 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 12:9a8c13c4298b 841 if (radio.RegOpMode.bits.Mode == RF_OPMODE_STANDBY) {
dudmuck 12:9a8c13c4298b 842 /* done transmitting, but dio0 not asserted */
dudmuck 12:9a8c13c4298b 843 printf("txDone but no dio0\r\n");
dudmuck 12:9a8c13c4298b 844 radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
dudmuck 12:9a8c13c4298b 845 if (radio.RegDioMapping1.bits.Dio0Mapping != 1) {
dudmuck 12:9a8c13c4298b 846 printf("dio0mapping-fail\r\n");
dudmuck 12:9a8c13c4298b 847 radio.RegDioMapping1.bits.Dio0Mapping = 1;
dudmuck 12:9a8c13c4298b 848 radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
dudmuck 12:9a8c13c4298b 849 }
dudmuck 12:9a8c13c4298b 850 }
dudmuck 12:9a8c13c4298b 851 }
dudmuck 12:9a8c13c4298b 852 }
dudmuck 12:9a8c13c4298b 853
dudmuck 12:9a8c13c4298b 854 if (LoRaWan::do_downlink && !beacon_guard) {
dudmuck 12:9a8c13c4298b 855 uint32_t since = timer.read_ms() - LoRaWan::rx_ms;
dudmuck 12:9a8c13c4298b 856 if (since > 110)
dudmuck 12:9a8c13c4298b 857 printf("since-tx:%u\r\n", since);
dudmuck 12:9a8c13c4298b 858 if (since > 500) {
dudmuck 12:9a8c13c4298b 859 LoRaWan::do_downlink = false;
dudmuck 12:9a8c13c4298b 860 printf("stalled-tx\r\n");
dudmuck 12:9a8c13c4298b 861 cmd_rx_restart(0);
dudmuck 12:9a8c13c4298b 862 }
dudmuck 1:107435401168 863 }
dudmuck 8:307f7faeb594 864
dudmuck 0:2ff18de8d48b 865 } // ..for(;;)
dudmuck 0:2ff18de8d48b 866 }