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 13 23:11:43 2017 +0000
Revision:
11:50d0558a4e37
Parent:
10:6783623cc886
Child:
12:9a8c13c4298b
add tx power adj to application layer.  increase beacon guard to 2.5s

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 11:50d0558a4e37 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 0:2ff18de8d48b 62
dudmuck 0:2ff18de8d48b 63 unsigned int skip_beacon_cnt;
dudmuck 0:2ff18de8d48b 64
dudmuck 0:2ff18de8d48b 65 SX127x_lora lora(radio);
dudmuck 0:2ff18de8d48b 66
dudmuck 0:2ff18de8d48b 67 #define LORAMAC_FIRST_CHANNEL ( (uint32_t)910.0e6 )
dudmuck 0:2ff18de8d48b 68 #define LORAMAC_STEPWIDTH_CHANNEL ( (uint32_t)800e3 )
dudmuck 0:2ff18de8d48b 69 #define LORA_MAX_NB_CHANNELS 8
dudmuck 0:2ff18de8d48b 70
dudmuck 0:2ff18de8d48b 71 #define N_SAMPLES 64
dudmuck 0:2ff18de8d48b 72 void channel_scan()
dudmuck 0:2ff18de8d48b 73 {
dudmuck 0:2ff18de8d48b 74 int min_ch, ch;
dudmuck 0:2ff18de8d48b 75 uint32_t hz = LORAMAC_FIRST_CHANNEL;
dudmuck 0:2ff18de8d48b 76 int acc[LORA_MAX_NB_CHANNELS];
dudmuck 0:2ff18de8d48b 77
dudmuck 0:2ff18de8d48b 78 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 79
dudmuck 0:2ff18de8d48b 80 for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
dudmuck 0:2ff18de8d48b 81 int i;
dudmuck 0:2ff18de8d48b 82 float MHz = (float)hz / 1e6;
dudmuck 0:2ff18de8d48b 83 radio.set_frf_MHz(MHz);
dudmuck 0:2ff18de8d48b 84 radio.set_opmode(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 85 acc[ch] = 0;
dudmuck 0:2ff18de8d48b 86 for (i = 0; i < N_SAMPLES; i++) {
dudmuck 0:2ff18de8d48b 87 int rssi = lora.get_current_rssi();
dudmuck 0:2ff18de8d48b 88 acc[ch] += rssi;
dudmuck 0:2ff18de8d48b 89 wait(0.01);
dudmuck 0:2ff18de8d48b 90 }
dudmuck 0:2ff18de8d48b 91 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 92 printf("ch%u: %f\r\n", ch, acc[ch] / (float)N_SAMPLES);
dudmuck 0:2ff18de8d48b 93 hz += LORAMAC_STEPWIDTH_CHANNEL;
dudmuck 0:2ff18de8d48b 94 radio.set_frf_MHz((float)hz/1e6);
dudmuck 0:2ff18de8d48b 95 }
dudmuck 0:2ff18de8d48b 96
dudmuck 0:2ff18de8d48b 97 int min = 0x7fffffff;
dudmuck 0:2ff18de8d48b 98 min_ch = 0;
dudmuck 0:2ff18de8d48b 99 for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
dudmuck 0:2ff18de8d48b 100 if (acc[ch] < min) {
dudmuck 0:2ff18de8d48b 101 min = acc[ch];
dudmuck 0:2ff18de8d48b 102 min_ch = ch;
dudmuck 0:2ff18de8d48b 103 }
dudmuck 0:2ff18de8d48b 104 }
dudmuck 0:2ff18de8d48b 105 hz = LORAMAC_FIRST_CHANNEL + (min_ch * LORAMAC_STEPWIDTH_CHANNEL);
dudmuck 0:2ff18de8d48b 106 printf("using ch%u, %luhz\r\n", min_ch, hz);
dudmuck 0:2ff18de8d48b 107 radio.set_frf_MHz((float)hz/1e6);
dudmuck 0:2ff18de8d48b 108 }
dudmuck 0:2ff18de8d48b 109
dudmuck 0:2ff18de8d48b 110 void init_radio()
dudmuck 0:2ff18de8d48b 111 {
dudmuck 0:2ff18de8d48b 112 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 113
dudmuck 0:2ff18de8d48b 114 radio.RegPaConfig.bits.OutputPower = 15;
dudmuck 0:2ff18de8d48b 115 radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
dudmuck 0:2ff18de8d48b 116 lora.enable();
dudmuck 0:2ff18de8d48b 117 lora.setBw_KHz(500);
dudmuck 0:2ff18de8d48b 118 lora.setSf(LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);
dudmuck 0:2ff18de8d48b 119 printf("using sf%u\r\n", LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);
dudmuck 0:2ff18de8d48b 120
dudmuck 0:2ff18de8d48b 121 channel_scan();
dudmuck 0:2ff18de8d48b 122
dudmuck 0:2ff18de8d48b 123 radio.write_reg(REG_LR_SYNC_BYTE, LORA_MAC_PUBLIC_SYNCWORD);
dudmuck 0:2ff18de8d48b 124 radio.write_reg(REG_LR_RX_MAX_PAYLOADLENGTH, 255);
dudmuck 0:2ff18de8d48b 125 }
dudmuck 0:2ff18de8d48b 126
dudmuck 0:2ff18de8d48b 127 void printLoraIrqs(bool clear)
dudmuck 0:2ff18de8d48b 128 {
dudmuck 0:2ff18de8d48b 129 printf("\r\nIrqFlags:");
dudmuck 0:2ff18de8d48b 130 if (lora.RegIrqFlags.bits.CadDetected)
dudmuck 0:2ff18de8d48b 131 printf("CadDetected ");
dudmuck 0:2ff18de8d48b 132 if (lora.RegIrqFlags.bits.FhssChangeChannel) {
dudmuck 0:2ff18de8d48b 133 printf("FhssChangeChannel:%d ", lora.RegHopChannel.bits.FhssPresentChannel);
dudmuck 0:2ff18de8d48b 134 }
dudmuck 0:2ff18de8d48b 135 if (lora.RegIrqFlags.bits.CadDone)
dudmuck 0:2ff18de8d48b 136 printf("CadDone ");
dudmuck 0:2ff18de8d48b 137 if (lora.RegIrqFlags.bits.TxDone)
dudmuck 0:2ff18de8d48b 138 printf("TxDone ");
dudmuck 0:2ff18de8d48b 139 if (lora.RegIrqFlags.bits.ValidHeader)
dudmuck 0:2ff18de8d48b 140 printf("ValidHeader ");
dudmuck 0:2ff18de8d48b 141 if (lora.RegIrqFlags.bits.PayloadCrcError)
dudmuck 0:2ff18de8d48b 142 printf("PayloadCrcError ");
dudmuck 0:2ff18de8d48b 143 if (lora.RegIrqFlags.bits.RxDone)
dudmuck 0:2ff18de8d48b 144 printf("RxDone ");
dudmuck 0:2ff18de8d48b 145 if (lora.RegIrqFlags.bits.RxTimeout)
dudmuck 0:2ff18de8d48b 146 printf("RxTimeout ");
dudmuck 0:2ff18de8d48b 147
dudmuck 0:2ff18de8d48b 148 printf("\r\n");
dudmuck 0:2ff18de8d48b 149
dudmuck 0:2ff18de8d48b 150 if (clear)
dudmuck 0:2ff18de8d48b 151 radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);
dudmuck 0:2ff18de8d48b 152 }
dudmuck 0:2ff18de8d48b 153
dudmuck 0:2ff18de8d48b 154 void printOpMode()
dudmuck 0:2ff18de8d48b 155 {
dudmuck 0:2ff18de8d48b 156 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 0:2ff18de8d48b 157 switch (radio.RegOpMode.bits.Mode) {
dudmuck 0:2ff18de8d48b 158 case RF_OPMODE_SLEEP: printf("sleep"); break;
dudmuck 0:2ff18de8d48b 159 case RF_OPMODE_STANDBY: printf("stby"); break;
dudmuck 0:2ff18de8d48b 160 case RF_OPMODE_SYNTHESIZER_TX: printf("fstx"); break;
dudmuck 0:2ff18de8d48b 161 case RF_OPMODE_TRANSMITTER: printf("tx"); break;
dudmuck 0:2ff18de8d48b 162 case RF_OPMODE_SYNTHESIZER_RX: printf("fsrx"); break;
dudmuck 0:2ff18de8d48b 163 case RF_OPMODE_RECEIVER: printf("rx"); break;
dudmuck 0:2ff18de8d48b 164 case 6:
dudmuck 0:2ff18de8d48b 165 if (radio.RegOpMode.bits.LongRangeMode)
dudmuck 0:2ff18de8d48b 166 printf("rxs");
dudmuck 0:2ff18de8d48b 167 else
dudmuck 0:2ff18de8d48b 168 printf("-6-");
dudmuck 0:2ff18de8d48b 169 break; // todo: different lora/fsk
dudmuck 0:2ff18de8d48b 170 case 7:
dudmuck 0:2ff18de8d48b 171 if (radio.RegOpMode.bits.LongRangeMode)
dudmuck 0:2ff18de8d48b 172 printf("cad");
dudmuck 0:2ff18de8d48b 173 else
dudmuck 0:2ff18de8d48b 174 printf("-7-");
dudmuck 0:2ff18de8d48b 175 break; // todo: different lora/fsk
dudmuck 0:2ff18de8d48b 176 }
dudmuck 0:2ff18de8d48b 177 }
dudmuck 0:2ff18de8d48b 178
dudmuck 9:a0ce66c18ec0 179 #define GPO_IDX 6
dudmuck 9:a0ce66c18ec0 180 void decrypted_uplink(uint8_t* buf, uint8_t buflen, uint8_t port)
dudmuck 9:a0ce66c18ec0 181 {
dudmuck 9:a0ce66c18ec0 182 if (port == SENSOR_PORT) {
dudmuck 9:a0ce66c18ec0 183 uint16_t a_1, a_3, lum;
dudmuck 9:a0ce66c18ec0 184 a_1 = buf[0] << 8;
dudmuck 9:a0ce66c18ec0 185 a_1 += buf[1];
dudmuck 9:a0ce66c18ec0 186 a_3 = buf[2] << 8;
dudmuck 9:a0ce66c18ec0 187 a_3 += buf[3];
dudmuck 9:a0ce66c18ec0 188 lum = buf[4] << 8;
dudmuck 9:a0ce66c18ec0 189 lum += buf[5];
dudmuck 10:6783623cc886 190 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 191 } else if (port == TEXT_PORT) {
dudmuck 9:a0ce66c18ec0 192 buf[buflen] = 0;
dudmuck 9:a0ce66c18ec0 193 printf("TEXT %s\r\n", buf);
dudmuck 9:a0ce66c18ec0 194 } else {
dudmuck 9:a0ce66c18ec0 195 int i;
dudmuck 9:a0ce66c18ec0 196 printf("port%u: ", port);
dudmuck 9:a0ce66c18ec0 197 for (i = 0; i < buflen; i++)
dudmuck 9:a0ce66c18ec0 198 printf("%02x ", buf[i]);
dudmuck 9:a0ce66c18ec0 199 printf("\r\n");
dudmuck 9:a0ce66c18ec0 200 }
dudmuck 9:a0ce66c18ec0 201 }
dudmuck 9:a0ce66c18ec0 202
dudmuck 0:2ff18de8d48b 203 EventQueue queue;
dudmuck 3:7c01b8978638 204
dudmuck 3:7c01b8978638 205 volatile bool get_tx_done;
dudmuck 3:7c01b8978638 206 volatile bool beacon_guard;
dudmuck 0:2ff18de8d48b 207 bool restore_tx_invert;
dudmuck 3:7c01b8978638 208 bool restore_header_mode;
dudmuck 0:2ff18de8d48b 209
dudmuck 0:2ff18de8d48b 210 void send_downlink()
dudmuck 0:2ff18de8d48b 211 {
dudmuck 0:2ff18de8d48b 212 if (LoRaWan::do_downlink) {
dudmuck 0:2ff18de8d48b 213 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 214 radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 215 lora.invert_tx(true);
dudmuck 0:2ff18de8d48b 216 restore_tx_invert = true;
dudmuck 0:2ff18de8d48b 217 lora.setRxPayloadCrcOn(false);
dudmuck 0:2ff18de8d48b 218 lora.start_tx(lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 219 LoRaWan::do_downlink = false;
dudmuck 3:7c01b8978638 220 get_tx_done = true;
dudmuck 0:2ff18de8d48b 221 }
dudmuck 0:2ff18de8d48b 222 }
dudmuck 0:2ff18de8d48b 223
dudmuck 0:2ff18de8d48b 224 void
dudmuck 0:2ff18de8d48b 225 service_radio()
dudmuck 0:2ff18de8d48b 226 {
dudmuck 0:2ff18de8d48b 227 service_action_e act = lora.service();
dudmuck 0:2ff18de8d48b 228
dudmuck 0:2ff18de8d48b 229 switch (act) {
dudmuck 0:2ff18de8d48b 230 case SERVICE_ERROR:
dudmuck 0:2ff18de8d48b 231 printf("SERVICE_ERROR\r\n");
dudmuck 0:2ff18de8d48b 232 case SERVICE_TX_DONE:
dudmuck 0:2ff18de8d48b 233 case SERVICE_NONE:
dudmuck 0:2ff18de8d48b 234 break;
dudmuck 0:2ff18de8d48b 235 case SERVICE_READ_FIFO:
dudmuck 3:7c01b8978638 236 LoRaWan::rx_slot = tim_get_current_slot();
dudmuck 3:7c01b8978638 237 LoRaWan::rx_ms = timer.read_ms();
dudmuck 11:50d0558a4e37 238 printf("%u, %.1fdB, %ddBm, ", time(NULL), lora.RegPktSnrValue / 4.0, lora.get_pkt_rssi());
dudmuck 3:7c01b8978638 239 if (LoRaWan::parse_receive()) {
dudmuck 3:7c01b8978638 240 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 3:7c01b8978638 241 wait(0.05);
dudmuck 3:7c01b8978638 242 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 243 }
dudmuck 0:2ff18de8d48b 244 break;
dudmuck 0:2ff18de8d48b 245 } // ..switch (act)
dudmuck 0:2ff18de8d48b 246 }
dudmuck 0:2ff18de8d48b 247
dudmuck 0:2ff18de8d48b 248 volatile float prev_beacon_send_at;
dudmuck 0:2ff18de8d48b 249 volatile float beacon_send_at;
dudmuck 0:2ff18de8d48b 250 volatile float beacon_loaded_at;
dudmuck 0:2ff18de8d48b 251
dudmuck 0:2ff18de8d48b 252 volatile bool beacon_loaded;
dudmuck 0:2ff18de8d48b 253 void
dudmuck 0:2ff18de8d48b 254 send_beacon()
dudmuck 0:2ff18de8d48b 255 {
dudmuck 0:2ff18de8d48b 256 prev_beacon_send_at = beacon_send_at;
dudmuck 0:2ff18de8d48b 257 beacon_send_at = timer.read();
dudmuck 0:2ff18de8d48b 258
dudmuck 0:2ff18de8d48b 259 if (!beacon_loaded)
dudmuck 0:2ff18de8d48b 260 return;
dudmuck 0:2ff18de8d48b 261
dudmuck 0:2ff18de8d48b 262 radio.set_opmode(RF_OPMODE_TRANSMITTER);
dudmuck 0:2ff18de8d48b 263 beacon_loaded = false;
dudmuck 3:7c01b8978638 264 get_tx_done = true;
dudmuck 3:7c01b8978638 265 beacon_guard = false;
dudmuck 0:2ff18de8d48b 266 }
dudmuck 0:2ff18de8d48b 267
dudmuck 0:2ff18de8d48b 268 static uint16_t beacon_crc( uint8_t *buffer, uint16_t length )
dudmuck 0:2ff18de8d48b 269 {
dudmuck 0:2ff18de8d48b 270 // The CRC calculation follows CCITT
dudmuck 0:2ff18de8d48b 271 const uint16_t polynom = 0x1021;
dudmuck 0:2ff18de8d48b 272 // CRC initial value
dudmuck 0:2ff18de8d48b 273 uint16_t crc = 0x0000;
dudmuck 0:2ff18de8d48b 274
dudmuck 0:2ff18de8d48b 275 if( buffer == NULL )
dudmuck 0:2ff18de8d48b 276 {
dudmuck 0:2ff18de8d48b 277 return 0;
dudmuck 0:2ff18de8d48b 278 }
dudmuck 0:2ff18de8d48b 279
dudmuck 0:2ff18de8d48b 280 for( uint16_t i = 0; i < length; ++i )
dudmuck 0:2ff18de8d48b 281 {
dudmuck 0:2ff18de8d48b 282 crc ^= ( uint16_t ) buffer[i] << 8;
dudmuck 0:2ff18de8d48b 283 for( uint16_t j = 0; j < 8; ++j )
dudmuck 0:2ff18de8d48b 284 {
dudmuck 0:2ff18de8d48b 285 crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
dudmuck 0:2ff18de8d48b 286 }
dudmuck 0:2ff18de8d48b 287 }
dudmuck 0:2ff18de8d48b 288
dudmuck 0:2ff18de8d48b 289 return crc;
dudmuck 0:2ff18de8d48b 290 }
dudmuck 0:2ff18de8d48b 291
dudmuck 0:2ff18de8d48b 292 void
dudmuck 3:7c01b8978638 293 _load_beacon()
dudmuck 0:2ff18de8d48b 294 {
dudmuck 0:2ff18de8d48b 295 uint16_t crc;
dudmuck 0:2ff18de8d48b 296 lora.RegPayloadLength = BEACON_SIZE;
dudmuck 0:2ff18de8d48b 297 radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 298 lora.setHeaderMode(true);
dudmuck 3:7c01b8978638 299 restore_header_mode = true;
dudmuck 0:2ff18de8d48b 300
dudmuck 0:2ff18de8d48b 301 if (skip_beacon_cnt > 0) {
dudmuck 1:107435401168 302 //printf("skip_beacon_cnt:%d\r\n", skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 303 lora.invert_tx(true);
dudmuck 0:2ff18de8d48b 304 restore_tx_invert = true;
dudmuck 0:2ff18de8d48b 305 skip_beacon_cnt--;
dudmuck 0:2ff18de8d48b 306 }
dudmuck 0:2ff18de8d48b 307
dudmuck 9:a0ce66c18ec0 308 radio.tx_buf[0] = beacon_payload[0];
dudmuck 9:a0ce66c18ec0 309 radio.tx_buf[1] = beacon_payload[1];
dudmuck 9:a0ce66c18ec0 310 radio.tx_buf[2] = beacon_payload[2];
dudmuck 9:a0ce66c18ec0 311 radio.tx_buf[3] = beacon_payload[3];
dudmuck 9:a0ce66c18ec0 312 beacon_payload[0] = CMD_NONE;
dudmuck 9:a0ce66c18ec0 313
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 9:a0ce66c18ec0 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 9:a0ce66c18ec0 393 LoRaWan::print_octets_rev("", motes[i].dev_eui, LORA_EUI_LENGTH);
dudmuck 9:a0ce66c18ec0 394 printf(" %lx\r\n", motes[i].dev_addr);
dudmuck 0:2ff18de8d48b 395 }
dudmuck 0:2ff18de8d48b 396 }
dudmuck 0:2ff18de8d48b 397 }
dudmuck 0:2ff18de8d48b 398
dudmuck 0:2ff18de8d48b 399 void
dudmuck 0:2ff18de8d48b 400 cmd_beacon_payload(uint8_t idx)
dudmuck 0:2ff18de8d48b 401 {
dudmuck 9:a0ce66c18ec0 402 uint32_t i;
dudmuck 9:a0ce66c18ec0 403 uint32_t* ptr;
dudmuck 9:a0ce66c18ec0 404 sscanf(pcbuf+idx, "%x", &i);
dudmuck 9:a0ce66c18ec0 405 printf("beacon_payload:%08x\r\n", i);
dudmuck 9:a0ce66c18ec0 406 ptr = (uint32_t*)beacon_payload;
dudmuck 9:a0ce66c18ec0 407 *ptr = i;
dudmuck 8:307f7faeb594 408 }
dudmuck 8:307f7faeb594 409
dudmuck 0:2ff18de8d48b 410 void
dudmuck 0:2ff18de8d48b 411 cmd_send_downlink(uint8_t idx)
dudmuck 0:2ff18de8d48b 412 {
dudmuck 0:2ff18de8d48b 413 ota_mote_t* mote = NULL;
dudmuck 0:2ff18de8d48b 414 int i;
dudmuck 0:2ff18de8d48b 415 unsigned int dev_addr;
dudmuck 0:2ff18de8d48b 416 sscanf(pcbuf+idx, "%x", &dev_addr);
dudmuck 0:2ff18de8d48b 417 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 418 if (motes[i].dev_addr == dev_addr) {
dudmuck 0:2ff18de8d48b 419 break;
dudmuck 0:2ff18de8d48b 420 }
dudmuck 0:2ff18de8d48b 421 }
dudmuck 0:2ff18de8d48b 422 if (i == N_MOTES) {
dudmuck 0:2ff18de8d48b 423 printf("mote %x not found\r\n", dev_addr);
dudmuck 0:2ff18de8d48b 424 return;
dudmuck 0:2ff18de8d48b 425 }
dudmuck 0:2ff18de8d48b 426 mote = &motes[i];
dudmuck 0:2ff18de8d48b 427
dudmuck 0:2ff18de8d48b 428 while (pcbuf[idx] != ' ') {
dudmuck 0:2ff18de8d48b 429 if (pcbuf[++idx] == 0) {
dudmuck 0:2ff18de8d48b 430 printf("hit end\r\n");
dudmuck 0:2ff18de8d48b 431 return;
dudmuck 0:2ff18de8d48b 432 }
dudmuck 0:2ff18de8d48b 433 }
dudmuck 0:2ff18de8d48b 434 idx++; // step past space
dudmuck 0:2ff18de8d48b 435
dudmuck 0:2ff18de8d48b 436 mote->user_downlink_length = 0;
dudmuck 0:2ff18de8d48b 437 while (pcbuf[idx] > ' ') {
dudmuck 0:2ff18de8d48b 438 int o;
dudmuck 0:2ff18de8d48b 439 sscanf(pcbuf+idx, "%02x", &o);
dudmuck 0:2ff18de8d48b 440 LoRaWan::user_downlink[mote->user_downlink_length++] = o;
dudmuck 0:2ff18de8d48b 441 idx += 2;
dudmuck 0:2ff18de8d48b 442 }
dudmuck 0:2ff18de8d48b 443
dudmuck 0:2ff18de8d48b 444 printf("%u bytes scheduled for %lx\r\n", mote->user_downlink_length, mote->dev_addr);
dudmuck 0:2ff18de8d48b 445 }
dudmuck 0:2ff18de8d48b 446
dudmuck 9:a0ce66c18ec0 447 void cmd_rx_restart(uint8_t idx)
dudmuck 9:a0ce66c18ec0 448 {
dudmuck 9:a0ce66c18ec0 449 /*radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 9:a0ce66c18ec0 450 printf("standby\r\n");*/
dudmuck 9:a0ce66c18ec0 451 radio.set_opmode(RF_OPMODE_SLEEP);
dudmuck 9:a0ce66c18ec0 452 printf("sleep\r\n");
dudmuck 9:a0ce66c18ec0 453 wait(0.05);
dudmuck 9:a0ce66c18ec0 454 radio.set_opmode(RF_OPMODE_RECEIVER);
dudmuck 9:a0ce66c18ec0 455 printf("receive\r\n");
dudmuck 9:a0ce66c18ec0 456 }
dudmuck 9:a0ce66c18ec0 457
dudmuck 9:a0ce66c18ec0 458 void cmd_beacon_gpo(uint8_t idx)
dudmuck 9:a0ce66c18ec0 459 {
dudmuck 9:a0ce66c18ec0 460 int gpo;
dudmuck 9:a0ce66c18ec0 461 sscanf(pcbuf+idx, "%d", &gpo);
dudmuck 9:a0ce66c18ec0 462 beacon_payload[0] = CMD_GPIO_OUT;
dudmuck 9:a0ce66c18ec0 463 beacon_payload[1] = gpo;
dudmuck 9:a0ce66c18ec0 464 printf("beacon gpo: %d\r\n", gpo);
dudmuck 9:a0ce66c18ec0 465 }
dudmuck 9:a0ce66c18ec0 466
dudmuck 9:a0ce66c18ec0 467 void cmd_beacon_rgb(uint8_t idx)
dudmuck 9:a0ce66c18ec0 468 {
dudmuck 9:a0ce66c18ec0 469 int r, g ,b;
dudmuck 9:a0ce66c18ec0 470 sscanf(pcbuf+idx, "%d %d %d", &r, &g, &b);
dudmuck 9:a0ce66c18ec0 471 beacon_payload[0] = CMD_LED_RGB;
dudmuck 9:a0ce66c18ec0 472 beacon_payload[1] = r;
dudmuck 9:a0ce66c18ec0 473 beacon_payload[2] = g;
dudmuck 9:a0ce66c18ec0 474 beacon_payload[3] = b;
dudmuck 9:a0ce66c18ec0 475 printf("beacon rgb: %d %d %d\r\n", r, g, b);
dudmuck 9:a0ce66c18ec0 476 }
dudmuck 9:a0ce66c18ec0 477
dudmuck 9:a0ce66c18ec0 478 void cmd_downlink_rgb(uint8_t idx)
dudmuck 9:a0ce66c18ec0 479 {
dudmuck 9:a0ce66c18ec0 480 ota_mote_t* mote = NULL;
dudmuck 9:a0ce66c18ec0 481 int i, r, g ,b;
dudmuck 9:a0ce66c18ec0 482 unsigned int dev_addr;
dudmuck 9:a0ce66c18ec0 483 sscanf(pcbuf+idx, "%x %d %d %d", &dev_addr, &r, &g, &b);
dudmuck 9:a0ce66c18ec0 484 for (i = 0; i < N_MOTES; i++) {
dudmuck 9:a0ce66c18ec0 485 if (motes[i].dev_addr == dev_addr) {
dudmuck 9:a0ce66c18ec0 486 break;
dudmuck 9:a0ce66c18ec0 487 }
dudmuck 9:a0ce66c18ec0 488 }
dudmuck 9:a0ce66c18ec0 489 if (i == N_MOTES) {
dudmuck 9:a0ce66c18ec0 490 printf("mote %x not found\r\n", dev_addr);
dudmuck 9:a0ce66c18ec0 491 return;
dudmuck 9:a0ce66c18ec0 492 }
dudmuck 9:a0ce66c18ec0 493 mote = &motes[i];
dudmuck 9:a0ce66c18ec0 494
dudmuck 9:a0ce66c18ec0 495 mote->user_downlink_length = 0;
dudmuck 9:a0ce66c18ec0 496 LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_LED_RGB;
dudmuck 9:a0ce66c18ec0 497 LoRaWan::user_downlink[mote->user_downlink_length++] = r;
dudmuck 9:a0ce66c18ec0 498 LoRaWan::user_downlink[mote->user_downlink_length++] = g;
dudmuck 9:a0ce66c18ec0 499 LoRaWan::user_downlink[mote->user_downlink_length++] = b;
dudmuck 9:a0ce66c18ec0 500
dudmuck 9:a0ce66c18ec0 501 printf("rgb %d %d %d to mote %x\r\n", r, g, b, mote->dev_addr);
dudmuck 9:a0ce66c18ec0 502 }
dudmuck 9:a0ce66c18ec0 503
dudmuck 9:a0ce66c18ec0 504 void cmd_downlink_gpo(uint8_t idx)
dudmuck 9:a0ce66c18ec0 505 {
dudmuck 9:a0ce66c18ec0 506 ota_mote_t* mote = NULL;
dudmuck 9:a0ce66c18ec0 507 int i, gpo;
dudmuck 9:a0ce66c18ec0 508 unsigned int dev_addr;
dudmuck 9:a0ce66c18ec0 509 sscanf(pcbuf+idx, "%x %d", &dev_addr, &gpo);
dudmuck 9:a0ce66c18ec0 510 for (i = 0; i < N_MOTES; i++) {
dudmuck 9:a0ce66c18ec0 511 if (motes[i].dev_addr == dev_addr) {
dudmuck 9:a0ce66c18ec0 512 break;
dudmuck 9:a0ce66c18ec0 513 }
dudmuck 9:a0ce66c18ec0 514 }
dudmuck 9:a0ce66c18ec0 515 if (i == N_MOTES) {
dudmuck 9:a0ce66c18ec0 516 printf("mote %x not found\r\n", dev_addr);
dudmuck 9:a0ce66c18ec0 517 return;
dudmuck 9:a0ce66c18ec0 518 }
dudmuck 9:a0ce66c18ec0 519 mote = &motes[i];
dudmuck 9:a0ce66c18ec0 520
dudmuck 9:a0ce66c18ec0 521 mote->user_downlink_length = 0;
dudmuck 9:a0ce66c18ec0 522 LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_GPIO_OUT;
dudmuck 9:a0ce66c18ec0 523 LoRaWan::user_downlink[mote->user_downlink_length++] = gpo;
dudmuck 9:a0ce66c18ec0 524
dudmuck 9:a0ce66c18ec0 525 printf("gpo %d to mote %x\r\n", gpo, mote->dev_addr);
dudmuck 9:a0ce66c18ec0 526 }
dudmuck 9:a0ce66c18ec0 527
dudmuck 0:2ff18de8d48b 528 void cmd_status(uint8_t idx)
dudmuck 0:2ff18de8d48b 529 {
dudmuck 0:2ff18de8d48b 530 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 2:9628d5e4b1bf 531 printf("%.3fMHz sf%ubw%u ", radio.get_frf_MHz(), lora.getSf(), lora.getBw());
dudmuck 0:2ff18de8d48b 532 printOpMode();
dudmuck 0:2ff18de8d48b 533 if (!radio.RegOpMode.bits.LongRangeMode) {
dudmuck 0:2ff18de8d48b 534 printf("FSK\r\n");
dudmuck 0:2ff18de8d48b 535 return;
dudmuck 0:2ff18de8d48b 536 }
dudmuck 0:2ff18de8d48b 537
dudmuck 0:2ff18de8d48b 538 lora.RegIrqFlags.octet = radio.read_reg(REG_LR_IRQFLAGS);
dudmuck 0:2ff18de8d48b 539 printLoraIrqs(false);
dudmuck 0:2ff18de8d48b 540
dudmuck 3:7c01b8978638 541 printf("get_tx_done:%u, ", get_tx_done);
dudmuck 9:a0ce66c18ec0 542 lora.RegTest33.octet = radio.read_reg(REG_LR_TEST33); // invert_i_q
dudmuck 9:a0ce66c18ec0 543 lora.RegDriftInvert.octet = radio.read_reg(REG_LR_DRIFT_INVERT);
dudmuck 9:a0ce66c18ec0 544 printf("modemstat:%02x, rxinv:%x,%x\r\n", radio.read_reg(REG_LR_MODEMSTAT), lora.RegTest33.octet, lora.RegDriftInvert.octet);
dudmuck 2:9628d5e4b1bf 545 radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
dudmuck 2:9628d5e4b1bf 546 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 547
dudmuck 0:2ff18de8d48b 548 }
dudmuck 0:2ff18de8d48b 549
dudmuck 11:50d0558a4e37 550 void cmd_op(uint8_t idx)
dudmuck 11:50d0558a4e37 551 {
dudmuck 11:50d0558a4e37 552 int i, dbm;
dudmuck 11:50d0558a4e37 553 RegPdsTrim1_t pds_trim;
dudmuck 11:50d0558a4e37 554 uint8_t adr;
dudmuck 11:50d0558a4e37 555 if (radio.type == SX1276)
dudmuck 11:50d0558a4e37 556 adr = REG_PDSTRIM1_SX1276;
dudmuck 11:50d0558a4e37 557 else
dudmuck 11:50d0558a4e37 558 adr = REG_PDSTRIM1_SX1272;
dudmuck 11:50d0558a4e37 559
dudmuck 11:50d0558a4e37 560 pds_trim.octet = radio.read_reg(adr);
dudmuck 11:50d0558a4e37 561
dudmuck 11:50d0558a4e37 562 if (pcbuf[idx] >= '0' && (pcbuf[idx] <= '9' || pcbuf[idx] == '-')) {
dudmuck 11:50d0558a4e37 563 sscanf(pcbuf+idx, "%d", &i);
dudmuck 11:50d0558a4e37 564 if (radio.RegPaConfig.bits.PaSelect) {
dudmuck 11:50d0558a4e37 565 /* PABOOST used: +2dbm to +17, or +20 */
dudmuck 11:50d0558a4e37 566 if (i == 20) {
dudmuck 11:50d0558a4e37 567 printf("+20dBm PADAC bias\r\n");
dudmuck 11:50d0558a4e37 568 i -= 3;
dudmuck 11:50d0558a4e37 569 pds_trim.bits.prog_txdac = 7;
dudmuck 11:50d0558a4e37 570 radio.write_reg(adr, pds_trim.octet);
dudmuck 11:50d0558a4e37 571 }
dudmuck 11:50d0558a4e37 572 if (i > 1)
dudmuck 11:50d0558a4e37 573 radio.RegPaConfig.bits.OutputPower = i - 2;
dudmuck 11:50d0558a4e37 574 } else {
dudmuck 11:50d0558a4e37 575 /* RFO used: -1 to +14dbm */
dudmuck 11:50d0558a4e37 576 if (i < 15)
dudmuck 11:50d0558a4e37 577 radio.RegPaConfig.bits.OutputPower = i + 1;
dudmuck 11:50d0558a4e37 578 }
dudmuck 11:50d0558a4e37 579 radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
dudmuck 11:50d0558a4e37 580 }
dudmuck 11:50d0558a4e37 581 radio.RegPaConfig.octet = radio.read_reg(REG_PACONFIG);
dudmuck 11:50d0558a4e37 582 if (radio.RegPaConfig.bits.PaSelect) {
dudmuck 11:50d0558a4e37 583 printf("PA_BOOST ");
dudmuck 11:50d0558a4e37 584 dbm = radio.RegPaConfig.bits.OutputPower + pds_trim.bits.prog_txdac - 2;
dudmuck 11:50d0558a4e37 585 } else {
dudmuck 11:50d0558a4e37 586 printf("RFO ");
dudmuck 11:50d0558a4e37 587 dbm = radio.RegPaConfig.bits.OutputPower - 1;
dudmuck 11:50d0558a4e37 588 }
dudmuck 11:50d0558a4e37 589 printf("OutputPower:%ddBm\r\n", dbm);
dudmuck 11:50d0558a4e37 590 }
dudmuck 11:50d0558a4e37 591
dudmuck 0:2ff18de8d48b 592 void cmd_help(uint8_t);
dudmuck 0:2ff18de8d48b 593
dudmuck 0:2ff18de8d48b 594 typedef struct {
dudmuck 0:2ff18de8d48b 595 const char* const cmd;
dudmuck 0:2ff18de8d48b 596 void (*handler)(uint8_t args_at);
dudmuck 0:2ff18de8d48b 597 const char* const arg_descr;
dudmuck 0:2ff18de8d48b 598 const char* const description;
dudmuck 0:2ff18de8d48b 599 } menu_item_t;
dudmuck 0:2ff18de8d48b 600
dudmuck 0:2ff18de8d48b 601 const menu_item_t menu_items[] =
dudmuck 0:2ff18de8d48b 602 { /* after first character, command names must be [A-Za-z] */
dudmuck 0:2ff18de8d48b 603 { "?", cmd_help, "","show available commands"},
dudmuck 0:2ff18de8d48b 604 { ".", cmd_status, "","read status"},
dudmuck 0:2ff18de8d48b 605 { "b", cmd_beacon_payload, "<%x>","set beacon payload"},
dudmuck 0:2ff18de8d48b 606 { "sb", cmd_skip_beacon, "<%d>","skip beacons"},
dudmuck 0:2ff18de8d48b 607 { "list", cmd_list_motes, "","list active motes"},
dudmuck 0:2ff18de8d48b 608 { "dl", cmd_send_downlink, "[%x %s]","send downlink <mote-hex-dev-addr> <hex-payload>"},
dudmuck 9:a0ce66c18ec0 609 { "rxr", cmd_rx_restart, "", "restart RX"},
dudmuck 9:a0ce66c18ec0 610 { "brgb", cmd_beacon_rgb, "%u %u %u", "load RGB command into next beacon" },
dudmuck 9:a0ce66c18ec0 611 { "rgb", cmd_downlink_rgb, "%x %u %u %u", "load RGB command to mote"},
dudmuck 9:a0ce66c18ec0 612 { "bgpo", cmd_beacon_gpo, "%d", "load output pin command into next beacon"},
dudmuck 9:a0ce66c18ec0 613 { "gpo", cmd_downlink_gpo, "%x %d", "load output pin command to mote"},
dudmuck 11:50d0558a4e37 614 { "op", cmd_op, "<dBm>","(TX) get/set TX power"},
dudmuck 0:2ff18de8d48b 615 { NULL, NULL, NULL, NULL }
dudmuck 0:2ff18de8d48b 616 };
dudmuck 0:2ff18de8d48b 617
dudmuck 0:2ff18de8d48b 618 void cmd_help(uint8_t args_at)
dudmuck 0:2ff18de8d48b 619 {
dudmuck 0:2ff18de8d48b 620 int i;
dudmuck 0:2ff18de8d48b 621
dudmuck 0:2ff18de8d48b 622 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:2ff18de8d48b 623 printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description);
dudmuck 0:2ff18de8d48b 624 }
dudmuck 0:2ff18de8d48b 625
dudmuck 0:2ff18de8d48b 626 }
dudmuck 0:2ff18de8d48b 627
dudmuck 0:2ff18de8d48b 628 void
dudmuck 0:2ff18de8d48b 629 console()
dudmuck 0:2ff18de8d48b 630 {
dudmuck 0:2ff18de8d48b 631 int i;
dudmuck 0:2ff18de8d48b 632 uint8_t user_cmd_len;
dudmuck 0:2ff18de8d48b 633
dudmuck 0:2ff18de8d48b 634 if (pcbuf_len < 0) { // ctrl-C
dudmuck 0:2ff18de8d48b 635 //printf("abort\r\n");
dudmuck 0:2ff18de8d48b 636 return;
dudmuck 0:2ff18de8d48b 637 }
dudmuck 0:2ff18de8d48b 638 if (pcbuf_len == 0)
dudmuck 0:2ff18de8d48b 639 return;
dudmuck 0:2ff18de8d48b 640
dudmuck 0:2ff18de8d48b 641 printf("\r\n");
dudmuck 0:2ff18de8d48b 642
dudmuck 0:2ff18de8d48b 643 /* get end of user-entered command */
dudmuck 0:2ff18de8d48b 644 user_cmd_len = 1; // first character can be any character
dudmuck 0:2ff18de8d48b 645 for (i = 1; i <= pcbuf_len; i++) {
dudmuck 0:2ff18de8d48b 646 if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') {
dudmuck 0:2ff18de8d48b 647 user_cmd_len = i;
dudmuck 0:2ff18de8d48b 648 break;
dudmuck 0:2ff18de8d48b 649 }
dudmuck 0:2ff18de8d48b 650 }
dudmuck 0:2ff18de8d48b 651
dudmuck 0:2ff18de8d48b 652 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:2ff18de8d48b 653 int mi_len = strlen(menu_items[i].cmd);
dudmuck 0:2ff18de8d48b 654
dudmuck 0:2ff18de8d48b 655 if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) {
dudmuck 0:2ff18de8d48b 656 while (pcbuf[mi_len] == ' ') // skip past spaces
dudmuck 0:2ff18de8d48b 657 mi_len++;
dudmuck 0:2ff18de8d48b 658 menu_items[i].handler(mi_len);
dudmuck 0:2ff18de8d48b 659 break;
dudmuck 0:2ff18de8d48b 660 }
dudmuck 0:2ff18de8d48b 661 }
dudmuck 0:2ff18de8d48b 662
dudmuck 0:2ff18de8d48b 663 pcbuf_len = 0;
dudmuck 0:2ff18de8d48b 664 printf("> ");
dudmuck 0:2ff18de8d48b 665 fflush(stdout);
dudmuck 0:2ff18de8d48b 666 }
dudmuck 0:2ff18de8d48b 667
dudmuck 0:2ff18de8d48b 668 int main()
dudmuck 0:2ff18de8d48b 669 {
dudmuck 0:2ff18de8d48b 670 Thread eventThread;
dudmuck 3:7c01b8978638 671 pc.baud(38400);
dudmuck 1:107435401168 672 printf("\r\nreset\r\n");
dudmuck 11:50d0558a4e37 673 set_time(0);
dudmuck 0:2ff18de8d48b 674
dudmuck 0:2ff18de8d48b 675 radio.hw_reset();
dudmuck 0:2ff18de8d48b 676
dudmuck 10:6783623cc886 677 //#ifndef TARGET_DISCO_L072CZ_LRWAN1
dudmuck 10:6783623cc886 678 #ifndef TYPE_ABZ
dudmuck 0:2ff18de8d48b 679 rfsw.input();
dudmuck 0:2ff18de8d48b 680 if (rfsw.read()) {
dudmuck 0:2ff18de8d48b 681 printf("LAS\r\n");
dudmuck 0:2ff18de8d48b 682 /* LAS HF=PA_BOOST LF=RFO */
dudmuck 0:2ff18de8d48b 683 radio.RegPaConfig.bits.PaSelect = 1;
dudmuck 0:2ff18de8d48b 684 } else {
dudmuck 0:2ff18de8d48b 685 printf("MAS\r\n");
dudmuck 0:2ff18de8d48b 686 radio.RegPaConfig.bits.PaSelect = 0;
dudmuck 0:2ff18de8d48b 687 }
dudmuck 0:2ff18de8d48b 688 rfsw.output();
dudmuck 5:ceacaa560cfd 689 #endif /* !TARGET_DISCO_L072CZ_LRWAN1 */
dudmuck 0:2ff18de8d48b 690
dudmuck 0:2ff18de8d48b 691 radio.rf_switch = rfsw_callback;
dudmuck 0:2ff18de8d48b 692
dudmuck 0:2ff18de8d48b 693 init_radio();
dudmuck 0:2ff18de8d48b 694
dudmuck 0:2ff18de8d48b 695 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 696
dudmuck 0:2ff18de8d48b 697 eventThread.start(callback(&queue, &EventQueue::dispatch_forever));
dudmuck 0:2ff18de8d48b 698 LoRaWan::init();
dudmuck 0:2ff18de8d48b 699
dudmuck 0:2ff18de8d48b 700 timer.start();
dudmuck 0:2ff18de8d48b 701 tim_init();
dudmuck 4:7e743e402681 702
dudmuck 4:7e743e402681 703 pc.attach(&rx_isr);
dudmuck 9:a0ce66c18ec0 704
dudmuck 0:2ff18de8d48b 705
dudmuck 0:2ff18de8d48b 706 for (;;) {
dudmuck 0:2ff18de8d48b 707 console();
dudmuck 1:107435401168 708
dudmuck 1:107435401168 709 if (radio.dio0) {
dudmuck 3:7c01b8978638 710 if (get_tx_done) {
dudmuck 3:7c01b8978638 711 get_tx_done = false;
dudmuck 3:7c01b8978638 712 lora.RegIrqFlags.octet = 0;
dudmuck 3:7c01b8978638 713 lora.RegIrqFlags.bits.TxDone = 1;
dudmuck 3:7c01b8978638 714 radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);
dudmuck 3:7c01b8978638 715
dudmuck 3:7c01b8978638 716 if (restore_tx_invert) {
dudmuck 3:7c01b8978638 717 lora.invert_tx(false);
dudmuck 3:7c01b8978638 718 restore_tx_invert = false;
dudmuck 3:7c01b8978638 719 }
dudmuck 3:7c01b8978638 720
dudmuck 3:7c01b8978638 721 if (restore_header_mode) {
dudmuck 3:7c01b8978638 722 lora.setHeaderMode(false);
dudmuck 3:7c01b8978638 723 restore_header_mode = false;
dudmuck 3:7c01b8978638 724 }
dudmuck 3:7c01b8978638 725 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 3:7c01b8978638 726 } else {
dudmuck 3:7c01b8978638 727 if (!beacon_guard)
dudmuck 3:7c01b8978638 728 service_radio();
dudmuck 3:7c01b8978638 729 }
dudmuck 1:107435401168 730 }
dudmuck 8:307f7faeb594 731
dudmuck 0:2ff18de8d48b 732 } // ..for(;;)
dudmuck 0:2ff18de8d48b 733 }