1 /*
2 * LoRaWAN FUOTA sample application
3 *
4 * Copyright (c) 2022-2024 Libre Solar Technologies GmbH
5 * Copyright (c) 2022-2024 tado GmbH
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <zephyr/device.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/lorawan/lorawan.h>
14
15 LOG_MODULE_REGISTER(lorawan_fuota, CONFIG_LORAWAN_SERVICES_LOG_LEVEL);
16
17 /* Customize based on device configuration */
18 #define LORAWAN_DEV_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
19 #define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
20 #define LORAWAN_APP_KEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
21 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
22
23 #define DELAY K_SECONDS(180)
24
25 char data[] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'};
26
downlink_info(uint8_t port,uint8_t flags,int16_t rssi,int8_t snr,uint8_t len,const uint8_t * data)27 static void downlink_info(uint8_t port, uint8_t flags, int16_t rssi, int8_t snr, uint8_t len,
28 const uint8_t *data)
29 {
30 LOG_INF("Received from port %d, flags %d, RSSI %ddB, SNR %ddBm", port, flags, rssi, snr);
31 if (data) {
32 LOG_HEXDUMP_INF(data, len, "Payload: ");
33 }
34 }
35
datarate_changed(enum lorawan_datarate dr)36 static void datarate_changed(enum lorawan_datarate dr)
37 {
38 uint8_t unused, max_size;
39
40 lorawan_get_payload_sizes(&unused, &max_size);
41 LOG_INF("New Datarate: DR %d, Max Payload %d", dr, max_size);
42 }
43
fuota_finished(void)44 static void fuota_finished(void)
45 {
46 LOG_INF("FUOTA finished. Reset device to apply firmware upgrade.");
47
48 /*
49 * In an actual application the firmware should be rebooted here if
50 * no important tasks are pending
51 */
52 }
53
main(void)54 int main(void)
55 {
56 const struct device *lora_dev;
57 struct lorawan_join_config join_cfg;
58 uint8_t dev_eui[] = LORAWAN_DEV_EUI;
59 uint8_t join_eui[] = LORAWAN_JOIN_EUI;
60 uint8_t app_key[] = LORAWAN_APP_KEY;
61 int ret;
62
63 struct lorawan_downlink_cb downlink_cb = {
64 .port = LW_RECV_PORT_ANY,
65 .cb = downlink_info
66 };
67
68 lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
69 if (!device_is_ready(lora_dev)) {
70 LOG_ERR("%s: device not ready.", lora_dev->name);
71 return -ENODEV;
72 }
73
74 ret = lorawan_start();
75 if (ret < 0) {
76 LOG_ERR("lorawan_start failed: %d", ret);
77 return ret;
78 }
79
80 lorawan_register_downlink_callback(&downlink_cb);
81 lorawan_register_dr_changed_callback(datarate_changed);
82
83 join_cfg.mode = LORAWAN_ACT_OTAA;
84 join_cfg.dev_eui = dev_eui;
85 join_cfg.otaa.join_eui = join_eui;
86 join_cfg.otaa.app_key = app_key;
87 join_cfg.otaa.nwk_key = app_key;
88
89 LOG_INF("Joining network over OTAA");
90 ret = lorawan_join(&join_cfg);
91 if (ret < 0) {
92 LOG_ERR("lorawan_join_network failed: %d", ret);
93 return ret;
94 }
95
96 lorawan_enable_adr(true);
97
98 /*
99 * Clock synchronization is required to schedule the multicast session
100 * in class C mode. It can also be used independent of FUOTA.
101 */
102 lorawan_clock_sync_run();
103
104 /*
105 * The multicast session setup service is automatically started in the
106 * background. It is also responsible for switching to class C at a
107 * specified time.
108 */
109
110 /*
111 * The fragmented data transport transfers the actual firmware image.
112 * It could also be used in a class A session, but would take very long
113 * in that case.
114 */
115 lorawan_frag_transport_run(fuota_finished);
116
117 /*
118 * Regular uplinks are required to open downlink slots in class A for
119 * FUOTA setup by the server.
120 */
121 while (1) {
122 ret = lorawan_send(2, data, sizeof(data), LORAWAN_MSG_UNCONFIRMED);
123 if (ret == 0) {
124 LOG_INF("Hello World sent!");
125 } else {
126 LOG_ERR("lorawan_send failed: %d", ret);
127 }
128
129 k_sleep(DELAY);
130 }
131
132 return 0;
133 }
134