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 
descriptor_cb(uint32_t descriptor)44 int descriptor_cb(uint32_t descriptor)
45 {
46 	/*
47 	 * In an actual application the firmware may be able to handle
48 	 * the descriptor field
49 	 */
50 
51 	LOG_INF("Received descriptor %u", descriptor);
52 
53 	return 0;
54 }
55 
fuota_finished(void)56 static void fuota_finished(void)
57 {
58 	LOG_INF("FUOTA finished. Reset device to apply firmware upgrade.");
59 
60 	/*
61 	 * In an actual application the firmware should be rebooted here if
62 	 * no important tasks are pending
63 	 */
64 }
65 
main(void)66 int main(void)
67 {
68 	const struct device *lora_dev;
69 	struct lorawan_join_config join_cfg;
70 	uint8_t dev_eui[] = LORAWAN_DEV_EUI;
71 	uint8_t join_eui[] = LORAWAN_JOIN_EUI;
72 	uint8_t app_key[] = LORAWAN_APP_KEY;
73 	int ret;
74 
75 	struct lorawan_downlink_cb downlink_cb = {
76 		.port = LW_RECV_PORT_ANY,
77 		.cb = downlink_info
78 	};
79 
80 	lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
81 	if (!device_is_ready(lora_dev)) {
82 		LOG_ERR("%s: device not ready.", lora_dev->name);
83 		return -ENODEV;
84 	}
85 
86 	ret = lorawan_start();
87 	if (ret < 0) {
88 		LOG_ERR("lorawan_start failed: %d", ret);
89 		return ret;
90 	}
91 
92 	lorawan_register_downlink_callback(&downlink_cb);
93 	lorawan_register_dr_changed_callback(datarate_changed);
94 
95 	join_cfg.mode = LORAWAN_ACT_OTAA;
96 	join_cfg.dev_eui = dev_eui;
97 	join_cfg.otaa.join_eui = join_eui;
98 	join_cfg.otaa.app_key = app_key;
99 	join_cfg.otaa.nwk_key = app_key;
100 
101 	LOG_INF("Joining network over OTAA");
102 	ret = lorawan_join(&join_cfg);
103 	if (ret < 0) {
104 		LOG_ERR("lorawan_join_network failed: %d", ret);
105 		return ret;
106 	}
107 
108 	lorawan_enable_adr(true);
109 
110 	/*
111 	 * Clock synchronization is required to schedule the multicast session
112 	 * in class C mode. It can also be used independent of FUOTA.
113 	 */
114 	lorawan_clock_sync_run();
115 
116 	/*
117 	 * The multicast session setup service is automatically started in the
118 	 * background. It is also responsible for switching to class C at a
119 	 * specified time.
120 	 */
121 
122 	/*
123 	 * The fragmented data transport transfers the actual firmware image.
124 	 * It could also be used in a class A session, but would take very long
125 	 * in that case.
126 	 */
127 	lorawan_frag_transport_run(fuota_finished);
128 
129 	lorawan_frag_transport_register_descriptor_callback(descriptor_cb);
130 
131 	/*
132 	 * Regular uplinks are required to open downlink slots in class A for
133 	 * FUOTA setup by the server.
134 	 */
135 	while (1) {
136 		ret = lorawan_send(2, data, sizeof(data), LORAWAN_MSG_UNCONFIRMED);
137 		if (ret == 0) {
138 			LOG_INF("Hello World sent!");
139 		} else {
140 			LOG_ERR("lorawan_send failed: %d", ret);
141 		}
142 
143 		k_sleep(DELAY);
144 	}
145 
146 	return 0;
147 }
148