Skip to content

Commit 8bb9bb6

Browse files
martinjaegercarlescufi
authored andcommitted
lorawan: add emulator for unit testing
The emulator can be used for unit testing of LoRaWAN services. It provides interfaces to send arbitrary messages to the LoRaWAN stack and receive the response through callbacks without using actual LoRa hardware. Signed-off-by: Martin Jäger <[email protected]>
1 parent 248bee9 commit 8bb9bb6

File tree

4 files changed

+222
-2
lines changed

4 files changed

+222
-2
lines changed

include/zephyr/lorawan/emul.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2024 A Labs GmbH
3+
* Copyright (c) 2024 tado GmbH
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#ifndef ZEPHYR_INCLUDE_LORAWAN_EMUL_H_
9+
#define ZEPHYR_INCLUDE_LORAWAN_EMUL_H_
10+
11+
#include <stdbool.h>
12+
#include <stdint.h>
13+
14+
#include <zephyr/lorawan/lorawan.h>
15+
16+
/**
17+
* @brief Defines the emulator uplink callback handler function signature.
18+
*
19+
* @param port LoRaWAN port
20+
* @param len Payload data length
21+
* @param data Pointer to the payload data
22+
*/
23+
typedef void (*lorawan_uplink_cb_t)(uint8_t port, uint8_t len, const uint8_t *data);
24+
25+
/**
26+
* @brief Emulate LoRaWAN downlink message
27+
*
28+
* @param port Port message was sent on
29+
* @param data_pending Network server has more downlink packets pending
30+
* @param rssi Received signal strength in dBm
31+
* @param snr Signal to Noise ratio in dBm
32+
* @param len Length of data received, will be 0 for ACKs
33+
* @param data Data received, will be NULL for ACKs
34+
*/
35+
void lorawan_emul_send_downlink(uint8_t port, bool data_pending, int16_t rssi, int8_t snr,
36+
uint8_t len, const uint8_t *data);
37+
38+
/**
39+
* @brief Register callback for emulated uplink messages
40+
*
41+
* @param cb Pointer to the uplink callback handler function
42+
*/
43+
void lorawan_emul_register_uplink_callback(lorawan_uplink_cb_t cb);
44+
45+
#endif /* ZEPHYR_INCLUDE_LORAWAN_EMUL_H_ */

subsys/lorawan/CMakeLists.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,15 @@ zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_IN865 REGION_IN865)
2020
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_US915 REGION_US915)
2121
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_RU864 REGION_RU864)
2222

23-
zephyr_library_sources_ifdef(CONFIG_LORAWAN lorawan.c)
24-
zephyr_library_sources_ifdef(CONFIG_LORAWAN lw_priv.c)
23+
if(CONFIG_LORAWAN)
24+
if(CONFIG_LORAWAN_EMUL)
25+
zephyr_library_sources(lorawan_emul.c)
26+
else()
27+
zephyr_library_sources(lorawan.c)
28+
endif()
29+
30+
zephyr_library_sources(lw_priv.c)
31+
endif()
2532

2633
add_subdirectory(services)
2734
add_subdirectory(nvm)

subsys/lorawan/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ module = LORAWAN
2020
module-str = lorawan
2121
source "subsys/logging/Kconfig.template.log_config"
2222

23+
config LORAWAN_EMUL
24+
bool "LoRaWAN Emulator"
25+
help
26+
The emulator can be used for unit testing of LoRaWAN services.
27+
It provides interfaces to send arbitrary messages to the LoRaWAN
28+
stack and receive the response through callbacks without using
29+
actual LoRa hardware.
30+
31+
See include/zephyr/lorawan/emul.h for the emulator API.
32+
2333
config LORAWAN_SYSTEM_MAX_RX_ERROR
2434
int "LoRaWAN System Max Rx Error"
2535
default 20

subsys/lorawan/lorawan_emul.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright (c) 2024 A Labs GmbH
3+
* Copyright (c) 2024 tado GmbH
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <zephyr/lorawan/emul.h>
9+
#include <zephyr/lorawan/lorawan.h>
10+
11+
#include <errno.h>
12+
13+
#include <LoRaMac.h>
14+
#include <Region.h>
15+
16+
#include <zephyr/kernel.h>
17+
#include <zephyr/init.h>
18+
#include <zephyr/logging/log.h>
19+
20+
LOG_MODULE_REGISTER(lorawan_emul, CONFIG_LORAWAN_LOG_LEVEL);
21+
22+
static bool lorawan_adr_enable;
23+
24+
static sys_slist_t dl_callbacks;
25+
26+
static DeviceClass_t current_class;
27+
28+
static lorawan_battery_level_cb_t battery_level_cb;
29+
static lorawan_dr_changed_cb_t dr_changed_cb;
30+
static lorawan_uplink_cb_t uplink_cb;
31+
32+
/* implementation required by the soft-se (software secure element) */
33+
void BoardGetUniqueId(uint8_t *id)
34+
{
35+
/* Do not change the default value */
36+
}
37+
38+
void lorawan_emul_send_downlink(uint8_t port, bool data_pending, int16_t rssi, int8_t snr,
39+
uint8_t len, const uint8_t *data)
40+
{
41+
struct lorawan_downlink_cb *cb;
42+
43+
/* Iterate over all registered downlink callbacks */
44+
SYS_SLIST_FOR_EACH_CONTAINER(&dl_callbacks, cb, node) {
45+
if ((cb->port == LW_RECV_PORT_ANY) || (cb->port == port)) {
46+
cb->cb(port, data_pending, rssi, snr, len, data);
47+
}
48+
}
49+
}
50+
51+
int lorawan_join(const struct lorawan_join_config *join_cfg)
52+
{
53+
return 0;
54+
}
55+
56+
int lorawan_set_class(enum lorawan_class dev_class)
57+
{
58+
switch (dev_class) {
59+
case LORAWAN_CLASS_A:
60+
current_class = CLASS_A;
61+
break;
62+
case LORAWAN_CLASS_B:
63+
LOG_ERR("Class B not supported yet!");
64+
return -ENOTSUP;
65+
case LORAWAN_CLASS_C:
66+
current_class = CLASS_C;
67+
break;
68+
default:
69+
return -EINVAL;
70+
}
71+
72+
return 0;
73+
}
74+
75+
int lorawan_set_datarate(enum lorawan_datarate dr)
76+
{
77+
ARG_UNUSED(dr);
78+
79+
/* Bail out if using ADR */
80+
if (lorawan_adr_enable) {
81+
return -EINVAL;
82+
}
83+
84+
return 0;
85+
}
86+
87+
void lorawan_get_payload_sizes(uint8_t *max_next_payload_size, uint8_t *max_payload_size)
88+
{
89+
LoRaMacTxInfo_t tx_info;
90+
91+
/* QueryTxPossible cannot fail */
92+
(void)LoRaMacQueryTxPossible(0, &tx_info);
93+
94+
*max_next_payload_size = tx_info.MaxPossibleApplicationDataSize;
95+
*max_payload_size = tx_info.CurrentPossiblePayloadSize;
96+
}
97+
98+
enum lorawan_datarate lorawan_get_min_datarate(void)
99+
{
100+
return LORAWAN_DR_0;
101+
}
102+
103+
void lorawan_enable_adr(bool enable)
104+
{
105+
lorawan_adr_enable = enable;
106+
}
107+
108+
int lorawan_set_conf_msg_tries(uint8_t tries)
109+
{
110+
return 0;
111+
}
112+
113+
int lorawan_send(uint8_t port, uint8_t *data, uint8_t len, enum lorawan_message_type type)
114+
{
115+
if (data == NULL) {
116+
return -EINVAL;
117+
}
118+
119+
if (uplink_cb != NULL) {
120+
uplink_cb(port, len, data);
121+
}
122+
123+
return 0;
124+
}
125+
126+
void lorawan_register_battery_level_callback(lorawan_battery_level_cb_t cb)
127+
{
128+
battery_level_cb = cb;
129+
}
130+
131+
void lorawan_register_downlink_callback(struct lorawan_downlink_cb *cb)
132+
{
133+
sys_slist_append(&dl_callbacks, &cb->node);
134+
}
135+
136+
void lorawan_register_dr_changed_callback(lorawan_dr_changed_cb_t cb)
137+
{
138+
dr_changed_cb = cb;
139+
}
140+
141+
int lorawan_start(void)
142+
{
143+
return 0;
144+
}
145+
146+
static int lorawan_init(void)
147+
{
148+
sys_slist_init(&dl_callbacks);
149+
150+
return 0;
151+
}
152+
153+
void lorawan_emul_register_uplink_callback(lorawan_uplink_cb_t cb)
154+
{
155+
uplink_cb = cb;
156+
}
157+
158+
SYS_INIT(lorawan_init, POST_KERNEL, 0);

0 commit comments

Comments
 (0)