Practical tips for the board NZ32-SC151 of Modtronix
----------------------------------------------
Creating a LoRa network using InAir9B using STM32
Hardware:
We use the board InAir9B that includes the chip SX1276 (modtronix.com)
The board NZ32-SC151, with STM32L151RC that is pin compatible with
AnAir9B (modtronix.com)
To
program the microproc, you need just a usb cable that can be connected
to the NZ32, however there is no way to debug/emulate. You just change
the firmware usng USB ootloader each time you need to test a
program.
Sofware tool:
You need to go to download the "System Workbench for STM32" from
http://www.openstm32.org.
More details can be found here:
https://github.com/modtronix-com/devkit_sx1276
--------------------------------
Libraries
Add the required library in order to send/receive packets
Go on Github
https://github.com/modtronix-com/devkit_sx1276
click on the "clone or download"
Save the file .zip
extract into a known directory (for exemple projetLoRa)
The folder's name is "devkit_sx1276-master"
Run System Workbench, but select the folder of the workspace : "devkit_sx1276-master/SW4STM32"
goto File -> import then general-> existing projects
in workspace, then select the folder SW4STM32, then finish.
At the next opening of project, use the folder of the work space SW4STM32
--------------------------------------------------------------------------------------------------------
A complete working project used at ENSIL can be dowloaded from : project
--------------------------------------------------------------------------------------------------------
How to create and download the bootloader
from modtronics site;
http://wiki.modtronix.com/doku.php?id=products:nz-stm32:nz32-sc151#programming_and_debugging
dans la partie "Required software", télécharger STSW-STM32080. Dézipper
et installet DFuSe demo.exe
C'est pour envoyer le fichier .dfu dans la memoire du microC.
Mais
il faut déjà générer ce fichier .dfu. Pour cela, il faut que l'IDE
(eclipse) génère un fichier .hex, que l'on transformera en .dfu
Mais l'IDE ne génère qu'un fichier .bin. Pour le faire génerer le .hex,
aller, sous eclipse, dans le menu
projet -> properties -> c/c++ build -> Setting
-> build steps -> post-build step
et ajouter à la fin de la ligne de commande:
&& arm-none-eabi-objcopy -O ihex
"${BuildArtifactFileBaseName}.elf" "${BuildArtifactFileBaseName}.hex"
Rebuilder le projet et le fichier hex se trouve dans le répertoir debug.
Pour le transformet en .dfu, lancre l'appli
ST Microelectronics et puis dfu file manager
Remarque:
Quand
vous connecter la carte au PC, il n'est pas visible, appuyer sur Boot,
et puis reseter. La carte est visible (à verifier sur device manager).
L'appli DFuSeDemo peut fonctionner.
Il est possible de faire une copie de la mémoire sur PC au cas où
(cadre "upload Action").
Utiliser maintenat STMicroelectyronics -> DFuSeDemo pour envoyer
au Micro.
Tres important:
Quand
vous utilisez DFuSe Demo, il donne trois valeurs importante: Vendor ID,
Product ID, version. Ces 3 valeurs devoient être utilisées pour générer
le fichier .hex avec le DFU File Manager.
Un reset executera le programme sur la carte !
--------------------------------------------------------------------------------------------
How to send/receive a packet
1- Add the .h
#include "mbed.h"
#include "../SX1276Lib/registers/sx1276Regs-LoRa.h"
#include "../SX1276Lib/sx1276/sx1276-inAir.h"
--------------------------------------------
2-define configration constants:
#define
RF_FREQUENCY
868700000
// 868MHz
#define
TX_OUTPUT_POWER
14
// 14 dBm for inAir9
#define
LORA_BANDWIDTH
8
// 0: 7.8 kHz, 1: 10.4 kHz, 2: 15.6kHz, 3: 20.8kHz,
// 4: 31.25kHz, 5: 41.7 kHz, 6: 62.5 kHz,
// 7: 125 kHz, 8: 250 kHz, 9: 500 kHz
#define
LORA_SPREADING_FACTOR
12
// SF7..SF12
#define
LORA_CODINGRATE
1
// 1=4/5, 2=4/6, 3=4/7, 4=4/8
#define
LORA_PREAMBLE_LENGTH
8
// Same for Tx and Rx
#define
LORA_SYMBOL_TIMEOUT
5
// Symbols
#define
LORA_FIX_LENGTH_PAYLOAD_ON
false
#define
LORA_FHSS_ENABLED
false
#define
LORA_NB_SYMB_HOP
4
#define
LORA_IQ_INVERSION_ON
false
#define
LORA_CRC_ENABLED
true
#define
TX_TIMEOUT_VALUE
2000000 // in us
#define
RX_TIMEOUT_VALUE
3500000 // in us
--------------------------------------------
3- define of the buffer that you want to send
#define
BUFFER_SIZE
32
// Define the
payload size here
uint8_t Buffer[BUFFER_SIZE];
--------------------------------------------
4- define the prototype of the functions correponsing to the events
void OnTxDone(void);
void OnTxTimeout(void);
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t
snr);
void OnRxTimeout(void);
void OnRxError(void);
--------------------------------------------
5- If you want to send/receive text to/from PC, then add:
Serial pc(USBTX,
USBRX);
--------------------------------------------
6- Create an instance of SX1276inAir:
SX1276inAir radio(OnTxDone,
OnTxTimeout, OnRxDone, OnRxTimeout, OnRxError, NULL, NULL);
The
constructor is simplified with just 7 functions. You can take a look at
sx1276lib->sx1276->sx1276-inAir.h to see the othet
constructor
with more functions.
you may now define all the functions, like
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
radio.Sleep();
BufferSize = size;
memcpy(Buffer, payload, BufferSize);
LoRaRssi = rssi;
LoRaSNR = snr;
State = RX_DONE;
radio.Rx(RX_TIMEOUT_VALUE); // I don't know why but you should put this
line !! Or you can juste put O as argument to push in continuous mode
}
The class SX1276inAir inherts from the class SX1276 and this latter
inherits from the class "radio".
So for the configuration you can use the following at the main.cpp:
radio.SetBoardType(BOARD_INAIR9);
radio.SetChannel(RF_FREQUENCY);
--------------------------------------------
7- In the main program, you can configure the UART, and the modem:
int main() {
wait_ms(500); // start delay
// configure uart port
pc.baud(19200);
pc.format(8, SerialBase::None, 1);
// setup the modern
radio.SetTxConfig(
MODEM_LORA,
TX_OUTPUT_POWER,
0,
LORA_BANDWIDTH,
LORA_SPREADING_FACTOR,
LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH,
LORA_FIX_LENGTH_PAYLOAD_ON,
LORA_CRC_ENABLED,
LORA_FHSS_ENABLED,
LORA_NB_SYMB_HOP,
LORA_IQ_INVERSION_ON,
TX_TIMEOUT_VALUE
);
radio.SetRxConfig(
MODEM_LORA,
LORA_BANDWIDTH,
LORA_SPREADING_FACTOR,
LORA_CODINGRATE,
0,
LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT,
LORA_FIX_LENGTH_PAYLOAD_ON,
0,
LORA_CRC_ENABLED,
LORA_FHSS_ENABLED,
LORA_NB_SYMB_HOP,
LORA_IQ_INVERSION_ON,
true
);
--------------------------------------------
8- to be sure that you are connected to the board:
while (radio.Read(REG_VERSION) == 0x00)
{
pc.printf("Trying to connect to radio device\r\n");
wait_ms(200);
}
--------------------------------------------
9- To turn on/off a board LED:
define first as global
DigitalOut led(LED1);
then use in main led = 1 ; or led = 0; or led = !led;
--------------------------------------------
10- sending a packet:
first define a buffer and fill it:
uint8_t Buffer[BUFFER_SIZE]; to be defined in global
radio.Send(Buffer,
BUFFER_SIZE);
you may change the State that is defined as
volatile RadioState State = LOWPOWER;
to LOWPOWER, and then waiting for the OnTxDone event.
In the function related to the event OnTxDone:
void OnTxDone(void)
{
radio.Sleep();
State = TX_DONE;
}
--------------------------------------------
11- Receiving a packet
When a packet is received by sx1276, the event onRxDone is called (according to what we define above). We
may handle it by the following function:
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
radio.Sleep();
BufferSize = size;
memcpy(Buffer, payload, BufferSize);
LoRaRssi = rssi;
LoRaSNR = snr;
State = RX_DONE;
}
--------------------------------------------
12- the main prgram is a state machine like:
switch(State)
{
case TX_DONE:
State =
LOWPOWER;
break;
case RX_DONE:
led =
!led;
pc.printf("N%dM%dT%d%dH%dP%dR%d\n",Buffer[0], Buffer[1], Buffer[2],
Buffer[3], Buffer[4], Buffer[5], LoRaRssi);
State =
LOWPOWER;
break;
case LOWPOWER:
break;
default:
State =
LOWPOWER;
} // end
switch
--------------------------------------------
13- Using a timer to create periodc events
in the global part
Ticker myTicker;
Then create the timer object in the main with the name of the function
that should be used:
myTicker.attach(ticker_callback, 5.0);
then defin the call back function:
void ticker_callback() {
led = !led;
}
int main() {
myTicker.attach(ticker_callback, 0.5f);
while(1) {}
}
------------------------------------------------------------------------------------------------------
Making the micrproc sleep and wake up.
Two
modes: sleep and deep sleep. Sleep mode the µP sleeps but not all the
other peripherals, the consumption is reduced. But in deep sleep mode
everything that need clock is in sleep and only the asynchronous
circuits can work. You need a wake-up mechanism to get out of deep
sleep mode. In the latter case, the consumption is almost zero.
You need to include the file
#include "WakeUp.h"
Before
you use the function deepsleep( ), you need to fix the duration of
sleep. You must call therefore the wakeup::set(duration in second)
function:
WakeUp::set(240); // for a sleep of 4 minutes
deepsleep( );
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
Some details about SX1276
------------------------------------------------------------------------------------------------------
Packet size in mode fixed packet length.
The length must be the same for all the network nodes RX nodes, TX
nodes, RX/TX nodes
- One optional nodeID byte
- The payload has the bytes whose length can be from 0 to 2047
- Two byte optional CRC
The real packet will have a preamble (0 to 65536 bytes) of 010101 ... ,
and a syncWord
(network ID), before the above payload.
IMPORTANT
At
each OnTxDone (in the onTxDone routine) you must manually call
radio.Standby() or radio.Sleep(). If not, the send command does not
work (it is what I tested practically but in the data sheet of
sx1276, it is written that the radio goes automatically to standby
mode) so I don't know why should we do that manually.
Note:
in the standby mode, the µP does not resume your program after waking
up. I think it goes to a reset because in this mode, even the memory is
not powered.
------------------------------------------------------------------------------
Sleep mode before writing into the configuration registers
Registers are readable in all device mode including Sleep.
However, they should be written only
in Sleep and Standby modes.
------------------------------------------------------------------------------
Receiver mode
In
a typical case when at the gateway a packet can arrive in any time,
continous mod (Rx continuous) is selected. Butif we are in the node and
after transmitting we just allow a temporal window for receiving
possible incoming packet, we should use the Rx single mode.
In Rx
Single, if a preamble is received before end of the window, RxDone
interrupt is generated and radio goes to standby state. If not,
RxTimeOut interrupt is generated and radio goes to standby.
Rx Single and Rx
Continuous Use Cases
(from the data sheet)
The LoRa single reception mode is used mainly in battery operated
systems or in systems where the companion
microcontroller has a limited availability of timers. In such systems,
the use of the timeout present in Rx Single reception
mode allows the end user to limit the amount of time spent in reception
(and thus limiting the power consumption) while not
using any of the companion MCU timers (the MCU can then be in sleep
mode while the radio is in the reception mode). The
RxTimeout interrupt generated at the end of the reception period is
then used to wake-up the companion MCU. One of the
advantages of the RxSingle mode is that the interrupt RxTimeout will
not be triggered if the device is currently receiving
data, thus giving the priority to the reception of the data over the
timeout. However, if during the reception, the device loses
track of the data due to external perturbation, the device will drop
the reception, flag the interrupt RxTimeout and go in
StandBy mode to decrease the power consumption of the system.
On the other hand, The LoRa continuous reception mode is used in
systems which do not have power restrictions or on
system where the use of a companion MCU timer is preferred over the
radio embedded timeout system. In RxContinuous
mode, the radio will track any LoRa signal present in the air and carry
on the reception of packets until the companion MCU
sets the radio into another mode of operation. Upon reception the
interrupt RxDone will be trigged but the device will stay in
Rx Mode, ready for the reception of the next packet.
Payload Data Extraction
from FIFO
(from data sheet)
In order to retrieve received data from FIFO the user must ensure that
ValidHeader, PayloadCrcError, RxDone and
RxTimeout interrupts in the status register RegIrqFlags are not
asserted to ensure that packet reception has terminated
successfully (i.e. no flags should be set).
In case of errors the steps below should be skipped and the packet
discarded. In order to retrieve valid received data from
the FIFO the user must:
RegRxNbBytes Indicates the number of bytes that have been received
thus far.
RegFifoAddrPtr is a dynamic pointer that indicates precisely where
the Lora modem received data has been written up
to.
Set RegFifoAddrPtr to RegFifoRxCurrentAddr. This sets the FIFO
pointer to the location of the last packet received in
the FIFO. The payload can then be extracted by reading the register
RegFifo, RegRxNbBytes times.
Alternatively, it is possible to manually point to the location of
the last packet received, from the start of the current
packet, by setting RegFifoAddrPtr to RegFifoRxByteAddr minus
RegRxNbBytes. The payload bytes can then be read
from the FIFO by reading the RegFifo address RegRxNbBytes times.
------------------------------------------------------------------------------
Packet Filtering
The SX1276/77/78/79 packet handler offers several mechanisms for packet
filtering, ensuring that only useful packets are
made available to the uC, reducing significantly system power
consumption and software complexity.
Sync Word Based
Sync word filtering/recognition is used for identifying the start of
the payload and also for network identification. As
previously described, the Sync word recognition block is configured
(size, value) in RegSyncConfig and RegSyncValue(i)
registers. This information is used, both for appending Sync word in
Tx, and filtering packets in Rx.
Every received packet which does not start with this locally configured
Sync word is automatically discarded and no
interrupt is generated.
When the Sync word is detected, payload reception automatically starts
and SyncAddressMatch is asserted.
Address Based
Address filtering can be enabled via the AddressFiltering bits. It adds
another level of filtering, above Sync word (i.e. Sync
must match first), typically useful in a multi-node networks where a
network ID is shared between all nodes (Sync word)
and each node has its own ID (address).
Two address based filtering options are available:
AddressFiltering = 01: Received address field is compared with
internal register NodeAddress. If they match then the
packet is accepted and processed, otherwise it is discarded.
AddressFiltering = 10: Received address field is compared with
internal registers NodeAddress and BroadcastAddress.
If either is a match, the received packet is accepted and processed,
otherwise it is discarded. This additional check with
a constant is useful for implementing broadcast in a multi-node
networks
Please note that the received address byte, as part of the payload, is
not stripped off the packet and is made available in
the FIFO. In addition, NodeAddress and AddressFiltering only apply to
Rx. On Tx side, if address filtering is expected, the
address byte should simply be put into the FIFO like any other byte of
the payload.
As address filtering requires a Sync word match, both features share
the same interrupt flag SyncAddressMatch.