-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrating LoRaWAN feature into Mbed-OS #5590
Integrating LoRaWAN feature into Mbed-OS #5590
Conversation
|
@sg- @AnttiKauppila @0xc0170 @janjongboom Please review. |
d69a4f1
to
86498bf
Compare
features/netsocket/LoRaWANBase.h
Outdated
| #define LORAWAN_BASE_H_ | ||
|
|
||
| #include "lorawan/system/lorawan_data_structures.h" | ||
| #include "mbed.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What headers this file needs, should not pull in mbed.h, but be more specific with inclusion. I've seen this in couple of files here.
features/netsocket/LoRaRadio.h
Outdated
| /** | ||
| * Sets the reception parameters. | ||
| * | ||
| * \param modem The radio modem to be used [0: FSK, 1: LoRa]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we be consistent, our doxy in this codebase uses the other style @param, Not big deal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I think at some point I had all Arm files with @param sort of doxy flags. LoRaMac.cpp uses a different fashion, and I think it just got mixed at some point. We squashed from 65 commits to 7 commits :) , so you can have an idea what we went through.
|
|
||
| using namespace utest::v1; | ||
|
|
||
| #define MBED_CONF_LORA_PHY 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this default value be set via config, rather than here?
| LORA_DIO5, NC, NC, LORA_TXCTL, LORA_RXCTL, NC, NC); | ||
| #endif | ||
| #if TARGET_K64F | ||
| SX1276_LoRaRadio Radio(D11, D12, D13, D10, A0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
K64F does not have defined pins LORA_MOSI, etc? Can be done via config? Same for disco below? to have one object in this file rather than ifdef else per target?
Same could be applied to specific header file per target ?
features/lorawan/LoRaWANStack.cpp
Outdated
| mlme_confirm_handler(&lora_mlme_confirm); | ||
| } | ||
|
|
||
| void LoRaWANStack::set_lora_event_cb(Callback<void(lora_events_t)> event_cb) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this attach() method ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used to post events to user. User provided callback function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In our API we use attach(), this is not the case here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actual API visible to user is lora_event_callback() defined in LoRaWANInterface class.
This name was suggested by Jan and Sam. I am not keen on any name :), if majority wants it to be attach, we can name it attach. Let's wait for what others say.
|
|
||
| #include "lorawan/LoRaWANInterface.h" | ||
|
|
||
| #define STK_OBJ LoRaWANStack::get_lorawan_stack() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we just use inlined local function rather macro for this
features/lorawan/LoRaWANStack.h
Outdated
| * | ||
| * @return The number of bytes sent, a negative error code on failure. | ||
| */ | ||
| int16_t handle_tx(uint8_t port, const uint8_t* data, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handle_tx instead of write ? same for handle_rx
the doc above does seem to be trimmed message is sent, for example:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not exactly write(). This initiates a process for TX. If duty cycle is off, it sends immidiately. If duty cycle is on, it only schedules the transmission. In my opinion handle_tx() is better suited name. Comment is right, it returns the number of bytes sent from your message.
features/lorawan/LoRaWANStack.h
Outdated
| * | ||
| * In response to the user call for disconnection, the stack shuts down itself. | ||
| */ | ||
| void shutdown_lorawan(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only shutdown not sufficient, suffix needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean prefix ? It does have a suffix 'lorawan'. That what it is, it shuts down lorawan protocol i.e., the mac layer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, prefix. if I invoke lorawanstack.shutdown() if that is not already explicit that what is happening.
features/lorawan/LoRaWANStack.h
Outdated
| void mcps_indication(McpsIndication_t *mcps_indication); | ||
| void mlme_confirm(MlmeConfirm_t *mlme_confirm); | ||
|
|
||
| lora_mac_status_t mlme_request_handler(lora_mac_mlme_req_t *mlme_request); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should private methods be documented also? Might help developers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I will put something there.
features/netsocket/LoRaWANBase.h
Outdated
| #include "lorawan/system/lorawan_data_structures.h" | ||
| #include "mbed.h" | ||
|
|
||
| class LoRaWANBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no namespace, at least mbed should be
|
Please review jenkins CI failures |
|
I haven't looked at this in-depth, but this seems weird: Max TX size depends on spread factor chosen, so why have this here? Also TX can never be 255 bytes, max is 224 (give or take). |
|
@janjongboom The phy layer sets a limit to max size which is 255 bytes. So that is what our buffer size is. |
ad7d341
to
89366fd
Compare
All Mbed-OS drivers for LoRa radio devices must implement this pure virtual class in order to be compliant with Mbed-OS applications. This class comes loaded with all necessary data structures. The implementations of this class can come out of tree.
89366fd
to
519c79d
Compare
|
@0xc0170 It's rather weird. CI is building properly for every other target except NUMAKER_PFM_NUC472. Do you have any idea why this is happening ? |
When you check the log (first indication) you would see compiler errors, same as other indications, for instance |
|
@0xc0170 Yes I checked that. Why it is declared for other targets then ? |
@tommikas Can you help @hasnainvirk to reproduce locally the failures ? |
|
@0xc0170 For some reason, NUMAKER_PFM_NUC472 does not create object files for the items in lorawan/system/ and lorawan/lorastack/mac . |
|
@0xc0170 I can reproduce that locally. I have tried at least 4 different targets with blinky example. Only NUMAKER_ fails. |
|
@0xc0170 Found the issue. timer.h had a header inclusion flag which was exactly like the NUMAKER_'s flag, i.e., TIMER_H |
519c79d
to
ecfa161
Compare
|
About timer.cpp/.h. Would it be better to rename these timer sources as more specific name (LoRaTimer?) ? This can now conflict with mbed os' Timer.cpp. |
|
@kivaisan I like that idea. With the current naming, there will be no problem even in Windows OS because we use qualified paths. But still I like the idea to rename these. |
|
The qualified paths don't help with the inclusion guards either. |
|
@tommikas inclusion guards was a different story. Already dealt with. |
ecfa161
to
869de3e
Compare
|
@AnotherButler new API |
|
@hasnainvirk Can you please share here what files are user facing API? is it all in the root directory ( I can then edit my review. |
All network interfaces for LoRaWAN protocol must implement this class. In order to be compatible with Mbed-OS applications, any implementation of this class must use the data structures and Mbed-OS timers provided. lorawan_data_structures may look repetitive but this is essential as we have a plan to use a reference implementation for LoRaWAN mac layer from Semtech. Some of the data structures provide seemless transition from semtech implementation (as MAC layer) to the Mbed-OS control layers above. features/lorawan/lorastack is the placeholder for future items like mac and phy layers. system/ will contain all the common bits.
LoRaPHY is the abstract class for the LoRa PHY layer which governs the LoRaRadio and provides some common functionality to all regional implementations. We support 10 regions and every region comes loaded with default parameters. These parameters can be changed by the Mac layer or explicitely by the stack controller layer using APIs provided. This layer in essence detaches Mac completely from PHY and provides more modular approach to the entire system. Apart from class structure, the internal functionality is directly deduced from semtech reference implementation that's why most of the internal data structures are used on 'as is' basis. In addition to that, the PHY layer provides APIs to control the LoRaRadio layer, i.e., the lora radio driver, ensuring that the radio is accessed from a single entry point. A seperate data structure file is added which is common to PHY layers only.
5b59851
to
ca2535c
Compare
The actual mac algorithms are being used as it is in the reference implementation. We introduce an internal class that starts a thread to handle deffered calls from interrupt context for RTOS. The code base is compatible with Mbed-OS 2 as well. GetPhyEventHandlers() API provides mac callback funtions for PHY layer, which are in turn delegated to radio driver from the PHY layer. LoRaMacInitialization() is augmented with LoRaPHY parameter which let's the MAC layer know which particular PHY layer is in use. LoRaMacSetTxTimer() and LoRaMacStopTxTimer() are used when duty cycle is off for testing purpose or to support custom application timers. If the duty cycle is off, mac and phy layer work togather to figure out the next possible transmission time. LoRaMacCrypto APIs are provided which provide seemless integration of mbedTLS into mac layer for cryptography. User application is supposed to provide proper mbedTLS configuration file. All other APIs are retained as it is.
ca2535c
to
0a79b73
Compare
|
@0xc0170 I took care of the namespcae pollution. LoRaWANInterface is the class users will be using for LoRaWAN stack access https://github.com/ARMmbed/mbed-os/pull/5590/files#diff-e347400efdb946f61d2df07c372c3da5
Everything else is internal. |
0a79b73
to
8f56f50
Compare
LoRaWANStack class is our controller layer on top of our current MAC and PHY layer. It provides services to an implementation of LoRaWANBase class. It is a singleton class owing to the fact that the mac layer underneath is not a class object. Instead, it uses the MAC via setting mib, mlme, mcps requests and getting responses back from the mac layer using confirmations and indications. In essense this class is a special handle for mac layer underneath which is predominantly reference design based. In future we may refactor the LoRaMac.cpp code to make it object oriented and cleaner. At one end, it binds the application selected radio driver with the PHY layer and at the other end it provides services to upper layers handling the mac via well defined APIs. For proper selection of a PHY layer, user must use Mbed config system. For this purpose an mbed_lib.json is provided which can be overriden by the user defined mbed_app.json. By default the EU868 band is selected as a PHY layer. User must set relevant keys for the selected connection mechanism.
This class is the doorway for the user application into the Mbed-OS implementation of LoRaWAN protocol. It implements LoRaWANBase and hence would work with any stack implementation underneath, ensuring seemless portability for applications. It takes a pre-constructed object of LoRaRadio and delegates it in the downward direction. Before calling connect() user must call initialize() function in order to initialize stack and mac layers. connect() APIs can be used to either provide relevent keys and connection method at runtime or compile time (using Mbed config system). enable_adaptive_datarate() and disable_adaptive_datarate() are used to turn on/off automatic rate control. Otherwisem set_datarate() could be used to set a particular data rate on the current channel. set_confirmed_msg_retries() is valid only for CONFIRMED messages. It means that the stack will retry for a given number of times before timing out. set_channel_plan() and get_channel_plan() are used to set or get a particular channel plan. These APIs are particularly useful in case of ABP (activation by personalization). Because in case of OTAA(over the air activation), by default the stack takes in a CF List (carrier frequency list) sent by the base station in conjunction with Network server. This list overwrites all user configured channels or channel plan. set_channel_plan() can be used to set a single channel as well by setting the parameter for number of channels to 1. remove_channel_plan() or remove_channel() are used to remove a currently active channel plan or a specific channel. send() and receive() APIs follow posix design except the socket descriptor is replaced with port number here. lora_event_callback() API is used to set a callback function from the application side which is used by the stack to inform user of particular events like CONNECTED, DISCONNECTED, CRYPTO_FAILURE, TX_TIMEOUT etc.
8f56f50
to
c743109
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a large change set is there any design documentation available which makes the review process easier.
|
@SenRamakri I can send you the document by email. |
|
@hasnainvirk As this is only pre-release code this should NOT be landing on master. This should be going to a feature branch... |
| @@ -0,0 +1,177 @@ | |||
| /** | |||
| * @file | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any design docs available for this large change set. That can help with code reviewing?
| * User application data buffer size if compliance test is used | ||
| */ | ||
| #if (MBED_CONF_LORA_PHY == 0 || MBED_CONF_LORA_PHY == 4 || MBED_CONF_LORA_PHY == 6 || MBED_CONF_LORA_PHY == 7) | ||
| #define LORAWAN_COMPLIANCE_TEST_DATA_SIZE 16 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the phy defines from 0-9 above, the checks here seem to be missing 3,5. Are they not supported? Is this expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is for compliance testing only. We know for sure only about EU868 MHz band. Chinese 470 MHz band is not up yet although Semtech have defined regional parameters for it. Same is the case for some other regions too. I am inclined to change this for only EU868 MHz band. But then I am not sure, how would we certify our stack for other regions. For more information https://github.com/Lora-net/LoRaMac-node check the table LoRaWAN certification results.
| ****************************************************************************/ | ||
| bool LoRaWANStack::is_port_valid(uint8_t port) | ||
| { | ||
| //Application should not use reserved and illegal port numbers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we #define these numbers(224)? Makes it more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These reserved ports don't have names as per specification. That's why they are not defined as a macro.
| int16_t LoRaWANStack::prepare_special_tx_frame(uint8_t port) | ||
| { | ||
| switch (port) { | ||
| case 224: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need switch/case construct to handle just one case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part of the code base is incomplete as the Compliance Testing has not started yet. This method is part of Compliance Testing.
| } | ||
| break; | ||
| default: | ||
| break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ok to not set f_buffer_isze when we hit this "default" case, as the function is returning this vale?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As in the previous comment, this part of code base is incomplete. We may need to handle port 0 as well, another reserved part. Anyway, can't really say much as we have not yet started compliance testing.
| } | ||
|
|
||
| bool LoRaPHYAS923::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, | ||
| int8_t* txPowOut, uint32_t* adrAckCounter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for adrNex for NULL before using?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mac layer makes sure that nothing null is being sent.
| } | ||
|
|
||
| bool LoRaPHYAS923::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again check for rxConfg == NULL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above. Mac layer takes care of it
| } | ||
| } | ||
|
|
||
| *drOut = datarate; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for drOut == NULL and txPowOut == NULL before using?
| uint8_t LoRaPHYEU868::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, | ||
| int8_t* drOut, int8_t* txPowOut, | ||
| uint8_t* nbRepOut, uint8_t* nbBytesParsed) | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for validity of pointers - linkAdrReq, drOut, txPowOut etc?
| lorawan_connect_t connection_params; | ||
|
|
||
| if (OVER_THE_AIR_ACTIVATION != 0) { | ||
| static uint8_t dev_eui[] = LORAWAN_DEVICE_EUI; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this connect routine be made thread safe in case multiple threads call the function simultaneously?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this moment the system underneath LoRaWANInterface is NonCopyable. It's documented above the constructor of LoRaWANInterface. The intention is to use one and only object of the stack from whichever context it's being accessed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good. Some changes needed though.
Also, I'm not convinced that the attach (callback API) is right. Having the developer manage the state machine does seem very nice. I would thinking about a pattern where we store Callback pointers for each event type but register with a flag. I think this needs some application/user testing to get right.
|
|
||
| #include "lorawan/LoRaWANInterface.h" | ||
|
|
||
| inline LoRaWANStack& stk_obj() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return should be a pointer, not a reference as NetworkStack does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reference makes sure that we have only one instance and it cannot be NULL.
For consistency it would make sense otherwise I don't see a difference. I wanted to avoid Null checks, if its a reference I can make it const and ensure that it is being passed around correctly otherwise throw me a compilation error. With pointer that is not possible.
| static uint8_t nwk_skey[] = LORAWAN_NWKSKEY; | ||
| static uint8_t app_skey[] = LORAWAN_APPSKEY; | ||
| static uint32_t dev_addr = LORAWAN_DEVICE_ADDRESS; | ||
| static uint32_t nwk_id = (LORAWAN_DEVICE_ADDRESS & LORAWAN_NETWORK_ID_MASK); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this only used in one place? If so can this be moved from the header file into local scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah you are right. I will look into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the macros are used elsewhere. And there are other macros as well which are configurable via json and for consistency they should remain together. If I move these ones out, its not consistent anymore. All configurable parameters are defined in lora_mac_data_structures.h
| @@ -0,0 +1,402 @@ | |||
| /** | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file should be alongside other lora code, not netsocket.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pure virtual class just like LoRaWANBase class. It can be used even if the feature lorawan is not available. If we move it to lorawan/ directory it would lose its purpose.
| #define LORARADIO_H_ | ||
|
|
||
| // Radio wake-up time from sleep - unit ms. | ||
| #define RADIO_WAKEUP_TIME 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Who uses this and why not part of the local phy scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a left over, I will clean it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I studied it a bit more. What I find is that this time is used for calculating rx windows parameters and is used by mac and all phy layers. Could move it to lorawan_data_structures though
|
Closing the PR, as it will go to the feature branch. |
If the branch is not created, let me know, I'll set up one if needed |
|
'Reopening and Comment' button is disabled for me. @0xc0170 |
|
@AnttiKauppila I can't reopen this PR. And the one #5620 is closed by Martin. |
|
Github disallows to reopen once it was forced pushed after closing. This is what I found:
I would assume based on this if you can get that branch to the state (SHA) that it was, reopen and then push whatever you have now, it should work :-) If this is too cumbersome, let's reopen the new one. |
Description
This PR adds LoRaWAN protocol support in Mbed-OS.
Radio drivers and the business application will be out of the tree.
Status
READY