Skip to content

Commit 39b73a3

Browse files
authored
Merge pull request #527 from helium/kent-williams/add-disco-l072cs-lrwan1-board
Add ST Disco L072CZ LRWAN1 Board Support
2 parents 98be617 + 76adb5d commit 39b73a3

File tree

3 files changed

+383
-0
lines changed

3 files changed

+383
-0
lines changed
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
3+
* Copyright (c) 2018 Terry Moore, MCCI
4+
*
5+
* Permission is hereby granted, free of charge, to anyone
6+
* obtaining a copy of this document and accompanying files,
7+
* to do whatever they want with them without any restriction,
8+
* including, but not limited to, copying, modification and redistribution.
9+
* NO WARRANTY OF ANY KIND IS PROVIDED.
10+
*
11+
* This example sends a valid LoRaWAN packet with payload "Hello,
12+
* world!", using frequency and encryption settings matching those of
13+
* the The Things Network. It's pre-configured for the Adafruit
14+
* Feather M0 LoRa.
15+
*
16+
* This uses OTAA (Over-the-air activation), where where a DevEUI and
17+
* application key is configured, which are used in an over-the-air
18+
* activation procedure where a DevAddr and session keys are
19+
* assigned/generated for use with all further communication.
20+
*
21+
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
22+
* g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
23+
* violated by this sketch when left running for longer)!
24+
25+
* To use this sketch, first register your application and device with
26+
* the things network, to set or generate an AppEUI, DevEUI and AppKey.
27+
* Multiple devices can use the same AppEUI, but each device has its own
28+
* DevEUI and AppKey.
29+
*
30+
* Do not forget to define the radio type correctly in
31+
* arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
32+
*
33+
*******************************************************************************/
34+
35+
#include <lmic.h>
36+
#include <hal/hal.h>
37+
#include <SPI.h>
38+
39+
//
40+
// For normal use, we require that you edit the sketch to replace FILLMEIN
41+
// with values assigned by the TTN console. However, for regression tests,
42+
// we want to be able to compile these scripts. The regression tests define
43+
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
44+
// working but innocuous value.
45+
//
46+
#ifdef COMPILE_REGRESSION_TEST
47+
# define FILLMEIN 0
48+
#else
49+
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
50+
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
51+
#endif
52+
53+
// This EUI must be in little-endian format, so least-significant-byte
54+
// first. When copying an EUI from ttnctl output, this means to reverse
55+
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
56+
// 0x70.
57+
static const u1_t PROGMEM APPEUI[8]= { FILLMEIN };
58+
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
59+
60+
// This should also be in little endian format, see above.
61+
static const u1_t PROGMEM DEVEUI[8]= { FILLMEIN };
62+
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
63+
64+
// This key should be in big endian format (or, since it is not really a
65+
// number but a block of memory, endianness does not really apply). In
66+
// practice, a key taken from the TTN console can be copied as-is.
67+
static const u1_t PROGMEM APPKEY[16] = { FILLMEIN };
68+
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
69+
70+
static uint8_t mydata[] = "Hello, world!";
71+
static osjob_t sendjob;
72+
73+
// Schedule TX every this many seconds (might become longer due to duty
74+
// cycle limitations).
75+
const unsigned TX_INTERVAL = 60;
76+
77+
// Pin mapping
78+
//
79+
// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0,
80+
// m0 defs ADAFRUIT_FEATHER_M0
81+
//
82+
#if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0)
83+
// Pin mapping for Adafruit Feather M0 LoRa, etc.
84+
const lmic_pinmap lmic_pins = {
85+
.nss = 8,
86+
.rxtx = LMIC_UNUSED_PIN,
87+
.rst = 4,
88+
.dio = {3, 6, LMIC_UNUSED_PIN},
89+
.rxtx_rx_active = 0,
90+
.rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB
91+
.spi_freq = 8000000,
92+
};
93+
#elif defined(ARDUINO_AVR_FEATHER32U4)
94+
// Pin mapping for Adafruit Feather 32u4 LoRa, etc.
95+
// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only
96+
// because MCCI doesn't have a test board; probably higher frequencies
97+
// will work.
98+
const lmic_pinmap lmic_pins = {
99+
.nss = 8,
100+
.rxtx = LMIC_UNUSED_PIN,
101+
.rst = 4,
102+
.dio = {7, 6, LMIC_UNUSED_PIN},
103+
.rxtx_rx_active = 0,
104+
.rssi_cal = 8, // LBT cal for the Adafruit Feather 32U4 LoRa, in dB
105+
.spi_freq = 1000000,
106+
};
107+
#elif defined(ARDUINO_CATENA_4551)
108+
// Pin mapping for Murata module / Catena 4551
109+
const lmic_pinmap lmic_pins = {
110+
.nss = 7,
111+
.rxtx = 29,
112+
.rst = 8,
113+
.dio = { 25, // DIO0 (IRQ) is D25
114+
26, // DIO1 is D26
115+
27, // DIO2 is D27
116+
},
117+
.rxtx_rx_active = 1,
118+
.rssi_cal = 10,
119+
.spi_freq = 8000000 // 8MHz
120+
};
121+
#elif defined(MCCI_CATENA_4610)
122+
#include "arduino_lmic_hal_boards.h"
123+
const lmic_pinmap lmic_pins = *Arduino_LMIC::GetPinmap_Catena4610();
124+
#elif defined(ARDUINO_DISCO_L072CZ_LRWAN1)
125+
#include "arduino_lmic_hal_boards.h"
126+
// Pin mapping Discovery
127+
const lmic_pinmap lmic_pins = *Arduino_LMIC::GetPinmap_Disco_L072cz_Lrwan1();
128+
#else
129+
# error "Unknown target"
130+
#endif
131+
132+
void printHex2(unsigned v) {
133+
v &= 0xff;
134+
if (v < 16)
135+
Serial.print('0');
136+
Serial.print(v, HEX);
137+
}
138+
139+
void onEvent (ev_t ev) {
140+
Serial.print(os_getTime());
141+
Serial.print(": ");
142+
switch(ev) {
143+
case EV_SCAN_TIMEOUT:
144+
Serial.println(F("EV_SCAN_TIMEOUT"));
145+
break;
146+
case EV_BEACON_FOUND:
147+
Serial.println(F("EV_BEACON_FOUND"));
148+
break;
149+
case EV_BEACON_MISSED:
150+
Serial.println(F("EV_BEACON_MISSED"));
151+
break;
152+
case EV_BEACON_TRACKED:
153+
Serial.println(F("EV_BEACON_TRACKED"));
154+
break;
155+
case EV_JOINING:
156+
Serial.println(F("EV_JOINING"));
157+
break;
158+
case EV_JOINED:
159+
Serial.println(F("EV_JOINED"));
160+
{
161+
u4_t netid = 0;
162+
devaddr_t devaddr = 0;
163+
u1_t nwkKey[16];
164+
u1_t artKey[16];
165+
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
166+
Serial.print("netid: ");
167+
Serial.println(netid, DEC);
168+
Serial.print("devaddr: ");
169+
Serial.println(devaddr, HEX);
170+
Serial.print("AppSKey: ");
171+
for (size_t i=0; i<sizeof(artKey); ++i) {
172+
if (i != 0)
173+
Serial.print("-");
174+
printHex2(artKey[i]);
175+
}
176+
Serial.println("");
177+
Serial.print("NwkSKey: ");
178+
for (size_t i=0; i<sizeof(nwkKey); ++i) {
179+
if (i != 0)
180+
Serial.print("-");
181+
printHex2(nwkKey[i]);
182+
}
183+
Serial.println();
184+
}
185+
// Disable link check validation (automatically enabled
186+
// during join, but because slow data rates change max TX
187+
// size, we don't use it in this example.
188+
LMIC_setLinkCheckMode(0);
189+
break;
190+
/*
191+
|| This event is defined but not used in the code. No
192+
|| point in wasting codespace on it.
193+
||
194+
|| case EV_RFU1:
195+
|| Serial.println(F("EV_RFU1"));
196+
|| break;
197+
*/
198+
case EV_JOIN_FAILED:
199+
Serial.println(F("EV_JOIN_FAILED"));
200+
break;
201+
case EV_REJOIN_FAILED:
202+
Serial.println(F("EV_REJOIN_FAILED"));
203+
break;
204+
break;
205+
case EV_TXCOMPLETE:
206+
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
207+
if (LMIC.txrxFlags & TXRX_ACK)
208+
Serial.println(F("Received ack"));
209+
if (LMIC.dataLen) {
210+
Serial.println(F("Received "));
211+
Serial.println(LMIC.dataLen);
212+
Serial.println(F(" bytes of payload"));
213+
}
214+
// Schedule next transmission
215+
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
216+
break;
217+
case EV_LOST_TSYNC:
218+
Serial.println(F("EV_LOST_TSYNC"));
219+
break;
220+
case EV_RESET:
221+
Serial.println(F("EV_RESET"));
222+
break;
223+
case EV_RXCOMPLETE:
224+
// data received in ping slot
225+
Serial.println(F("EV_RXCOMPLETE"));
226+
break;
227+
case EV_LINK_DEAD:
228+
Serial.println(F("EV_LINK_DEAD"));
229+
break;
230+
case EV_LINK_ALIVE:
231+
Serial.println(F("EV_LINK_ALIVE"));
232+
break;
233+
/*
234+
|| This event is defined but not used in the code. No
235+
|| point in wasting codespace on it.
236+
||
237+
|| case EV_SCAN_FOUND:
238+
|| Serial.println(F("EV_SCAN_FOUND"));
239+
|| break;
240+
*/
241+
case EV_TXSTART:
242+
Serial.println(F("EV_TXSTART"));
243+
break;
244+
case EV_TXCANCELED:
245+
Serial.println(F("EV_TXCANCELED"));
246+
break;
247+
case EV_RXSTART:
248+
/* do not print anything -- it wrecks timing */
249+
break;
250+
case EV_JOIN_TXCOMPLETE:
251+
Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
252+
break;
253+
254+
default:
255+
Serial.print(F("Unknown event: "));
256+
Serial.println((unsigned) ev);
257+
break;
258+
}
259+
}
260+
261+
void do_send(osjob_t* j){
262+
// Check if there is not a current TX/RX job running
263+
if (LMIC.opmode & OP_TXRXPEND) {
264+
Serial.println(F("OP_TXRXPEND, not sending"));
265+
} else {
266+
// Prepare upstream data transmission at the next possible time.
267+
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
268+
Serial.println(F("Packet queued"));
269+
}
270+
// Next TX is scheduled after TX_COMPLETE event.
271+
}
272+
273+
void setup() {
274+
delay(5000);
275+
while (! Serial)
276+
;
277+
Serial.begin(9600);
278+
Serial.println(F("Starting"));
279+
280+
#ifdef VCC_ENABLE
281+
// For Pinoccio Scout boards
282+
pinMode(VCC_ENABLE, OUTPUT);
283+
digitalWrite(VCC_ENABLE, HIGH);
284+
delay(1000);
285+
#endif
286+
287+
#if defined(ARDUINO_DISCO_L072CZ_LRWAN1)
288+
SPI.setMOSI(RADIO_MOSI_PORT);
289+
SPI.setMISO(RADIO_MISO_PORT);
290+
SPI.setSCLK(RADIO_SCLK_PORT);
291+
SPI.setSSEL(RADIO_NSS_PORT);
292+
#endif
293+
294+
// LMIC init
295+
os_init();
296+
// Reset the MAC state. Session and pending data transfers will be discarded.
297+
LMIC_reset();
298+
299+
// allow much more clock error than the X/1000 default. See:
300+
// https://github.com/mcci-catena/arduino-lorawan/issues/74#issuecomment-462171974
301+
// https://github.com/mcci-catena/arduino-lmic/commit/42da75b56#diff-16d75524a9920f5d043fe731a27cf85aL633
302+
// the X/1000 means an error rate of 0.1%; the above issue discusses using values up to 10%.
303+
// so, values from 10 (10% error, the most lax) to 1000 (0.1% error, the most strict) can be used.
304+
LMIC_setClockError(1 * MAX_CLOCK_ERROR / 40);
305+
306+
LMIC_setLinkCheckMode(0);
307+
LMIC_setDrTxpow(DR_SF7,14);
308+
LMIC_selectSubBand(6);
309+
310+
// Start job (sending automatically starts OTAA too)
311+
do_send(&sendjob);
312+
}
313+
314+
void loop() {
315+
os_runloop_once();
316+
}

src/arduino_lmic_hal_boards.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const HalPinmap_t *GetPinmap_Catena4630();
3737
const HalPinmap_t *GetPinmap_Catena4801();
3838
const HalPinmap_t* GetPinmap_ttgo_lora32_v1();
3939
const HalPinmap_t* GetPinmap_heltec_lora32();
40+
const HalPinmap_t* GetPinmap_Disco_L072cz_Lrwan1();
4041

4142
const HalPinmap_t *GetPinmap_ThisBoard();
4243

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
3+
Module: getpinmap_disco_l072cz_lrwan1.cpp
4+
5+
Function:
6+
Arduino-LMIC C++ HAL pinmaps for the Discovery L072CZ LRWAN1
7+
8+
Copyright & License:
9+
See accompanying LICENSE file.
10+
11+
Author:
12+
Helium February 2020
13+
14+
*/
15+
16+
#if defined(ARDUINO_DISCO_L072CZ_LRWAN1)
17+
18+
#include <arduino_lmic_hal_boards.h>
19+
#include <Arduino.h>
20+
21+
#include "../lmic/oslmic.h"
22+
23+
namespace Arduino_LMIC {
24+
25+
class HalConfiguration_Disco_L072cz_Lrwan1_t : public HalConfiguration_t
26+
{
27+
public:
28+
enum DIGITAL_PINS : uint8_t
29+
{
30+
PIN_SX1276_NSS = 37,
31+
PIN_SX1276_NRESET = 33,
32+
PIN_SX1276_DIO0 = 38,
33+
PIN_SX1276_DIO1 = 39,
34+
PIN_SX1276_DIO2 = 40,
35+
PIN_SX1276_RXTX = 21,
36+
};
37+
38+
virtual bool queryUsingTcxo(void) override { return false; };
39+
};
40+
// save some typing by bringing the pin numbers into scope
41+
static HalConfiguration_Disco_L072cz_Lrwan1_t myConfig;
42+
43+
static const HalPinmap_t myPinmap =
44+
{
45+
.nss = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NSS,
46+
.rxtx = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_RXTX,
47+
.rst = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NRESET,
48+
49+
.dio = {HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO0,
50+
HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO1,
51+
HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO2,
52+
},
53+
.rxtx_rx_active = 1,
54+
.rssi_cal = 10,
55+
.spi_freq = 8000000, /* 8MHz */
56+
.pConfig = &myConfig
57+
};
58+
59+
const HalPinmap_t *GetPinmap_Disco_L072cz_Lrwan1(void)
60+
{
61+
return &myPinmap;
62+
}
63+
64+
}; // namespace Arduino_LMIC
65+
66+
#endif /* defined(ARDUINO_DISCO_L072CZ_LRWAN1) */

0 commit comments

Comments
 (0)