Merge branch 'master' into 3.5.2-merge
[ExpressLRS.git] / src / lib / WIFI / lr1121.cpp
blob4837bd79bbddd773cc0f39f4df241fec8aa4ad2c
1 #include "targets.h"
3 #if defined(RADIO_LR1121)
5 #include "ArduinoJson.h"
6 #include "AsyncJson.h"
7 #include <ESPAsyncWebServer.h>
8 #include <StreamString.h>
10 #include "common.h"
11 #include "SPIEx.h"
12 #include "logging.h"
14 #if defined(TARGET_TX)
15 #include "wifiJoystick.h"
16 #endif
18 struct lr1121UpdateState_s {
19 size_t expectedFilesize;
20 size_t totalSize;
21 SX12XX_Radio_Number_t updatingRadio;
22 size_t left_over;
23 struct {
24 uint8_t header[6];
25 uint8_t buffer[256];
26 } __attribute__((packed)) packet;
28 static lr1121UpdateState_s *lr1121UpdateState;
30 extern LR1121Hal hal;
32 static void writeLR1121Bytes(uint8_t *data, uint32_t data_size) {
33 lr1121UpdateState->packet.header[0] = (uint8_t)(LR11XX_BL_WRITE_FLASH_ENCRYPTED_OC >> 8);
34 lr1121UpdateState->packet.header[1] = (uint8_t)(LR11XX_BL_WRITE_FLASH_ENCRYPTED_OC);
35 lr1121UpdateState->packet.header[2] = (uint8_t)(lr1121UpdateState->totalSize >> 24);
36 lr1121UpdateState->packet.header[3] = (uint8_t)(lr1121UpdateState->totalSize >> 16);
37 lr1121UpdateState->packet.header[4] = (uint8_t)(lr1121UpdateState->totalSize >> 8);
38 lr1121UpdateState->packet.header[5] = (uint8_t)(lr1121UpdateState->totalSize);
40 uint32_t write_size = lr1121UpdateState->left_over;
41 if (data != nullptr)
43 DBGLN("new %x", data_size);
44 memcpy(lr1121UpdateState->packet.buffer + lr1121UpdateState->left_over, data, data_size);
45 write_size += data_size;
47 DBGLN("flashing %x at %x", write_size, lr1121UpdateState->totalSize);
49 // Have to do this the OLD way, so we can pump out more than 64 bytes in one message
50 digitalWrite(lr1121UpdateState->updatingRadio == SX12XX_Radio_1 ? GPIO_PIN_NSS : GPIO_PIN_NSS_2, LOW);
51 SPIEx.transferBytes(lr1121UpdateState->packet.header, nullptr, 6 + write_size);
52 digitalWrite(lr1121UpdateState->updatingRadio == SX12XX_Radio_1 ? GPIO_PIN_NSS : GPIO_PIN_NSS_2, HIGH);
54 while (digitalRead(lr1121UpdateState->updatingRadio == SX12XX_Radio_1 ? GPIO_PIN_BUSY : GPIO_PIN_BUSY_2) == HIGH)
56 delay(1);
58 lr1121UpdateState->totalSize += write_size;
59 lr1121UpdateState->left_over = 0;
60 DBGLN("flashed");
63 static void readRegister(SX12XX_Radio_Number_t radio, uint16_t reg, uint8_t *buffer, uint32_t buffer_len)
65 uint8_t read[5];
66 read[0] = reg >> 8;
67 read[1] = (uint8_t)reg;
68 SPIEx.write(radio, read, 2);
69 hal.WaitOnBusy(radio);
70 memset(buffer, 0, buffer_len);
71 SPIEx.read(radio, buffer, buffer_len);
72 hal.WaitOnBusy(radio);
75 static void WebUploadLR1121ResponseHandler(AsyncWebServerRequest *request) {
76 // Complete upload and set error flag
77 bool uploadError = false;
78 writeLR1121Bytes(nullptr, 0);
79 lr1121UpdateState->totalSize += lr1121UpdateState->left_over;
81 SPIEx.setHwCs(true);
82 if (GPIO_PIN_NSS_2 != UNDEF_PIN)
84 spiAttachSS(SPIEx.bus(), 1, GPIO_PIN_NSS_2);
87 if (lr1121UpdateState->totalSize == lr1121UpdateState->expectedFilesize)
89 DBGLN("reboot 1121");
90 uint8_t reboot_cmd[] = {
91 (uint8_t)(LR11XX_BL_REBOOT_OC >> 8),
92 (uint8_t)LR11XX_BL_REBOOT_OC,
95 SPIEx.write(lr1121UpdateState->updatingRadio, reboot_cmd, 3);
96 while(!hal.WaitOnBusy(lr1121UpdateState->updatingRadio));
98 DBGLN("check not in BL mode");
99 uint8_t packet[5];
100 readRegister(lr1121UpdateState->updatingRadio, LR11XX_SYSTEM_GET_VERSION_OC, packet, 5);
101 uploadError = (packet[2] != 3);
102 DBGLN("hardware %x", packet[1]);
103 DBGLN("type %x", packet[2]);
104 DBGLN("firmware %x", ( ( uint16_t )packet[3] << 8 ) + ( uint16_t )packet[4]);
107 String msg;
108 if (!uploadError && lr1121UpdateState->totalSize == lr1121UpdateState->expectedFilesize) {
109 msg = String(R"({"status": "ok", "msg": "Update complete. Refresh page to see new version information."})");
110 DBGLN("Update complete");
111 } else {
112 StreamString p = StreamString();
113 if (lr1121UpdateState->totalSize != lr1121UpdateState->expectedFilesize) {
114 p.println("Not enough data uploaded!");
115 } else {
116 p.println("Update failed, refresh and try again.");
118 DBGLN("Failed to upload firmware: %s", p.c_str());
119 msg = String(R"({"status": "error", "msg": ")") + p + R"("})";
121 AsyncWebServerResponse *response = request->beginResponse(200, "application/json", msg);
122 response->addHeader("Connection", "close");
123 request->send(response);
124 delete lr1121UpdateState;
125 lr1121UpdateState = nullptr;
128 static void WebUploadLR1121DataHandler(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
129 if (index == 0) {
130 #if defined(TARGET_TX)
131 WifiJoystick::StopJoystickService();
132 #endif
133 lr1121UpdateState = new lr1121UpdateState_s;
134 lr1121UpdateState->expectedFilesize = request->header("X-FileSize").toInt();
135 lr1121UpdateState->updatingRadio = request->header("X-Radio").toInt();
136 DBGLN("Update: '%s' size %u on radio %d", filename.c_str(), lr1121UpdateState->expectedFilesize, lr1121UpdateState->updatingRadio);
137 lr1121UpdateState->totalSize = 0;
139 // Reboot to BL mode
140 DBGLN("Reboot 1121 to bootloader mode");
141 uint8_t reboot_cmd[] = {
142 (uint8_t)(LR11XX_SYSTEM_REBOOT_OC >> 8),
143 (uint8_t)LR11XX_SYSTEM_REBOOT_OC,
146 SPIEx.write(lr1121UpdateState->updatingRadio, reboot_cmd, 3);
147 while(!hal.WaitOnBusy(lr1121UpdateState->updatingRadio)) {
148 DBGLN("Waiting...");
149 delay(10);
152 // Ensure we're in BL mode
153 DBGLN("Ensure BL mode");
154 uint8_t packet[5];
155 readRegister(lr1121UpdateState->updatingRadio, LR11XX_BL_GET_VERSION_OC, packet, 5);
156 if (packet[2] != 0xDF) {
157 AsyncWebServerResponse *response = request->beginResponse(200, "application/json", R"({"status": "error", "msg": "Not in bootloader mode"})");
158 response->addHeader("Connection", "close");
159 request->send(response);
160 request->client()->close();
161 return;
164 // Erase flash
165 DBGLN("Erasing");
166 packet[0] = LR11XX_BL_ERASE_FLASH_OC >> 8;
167 packet[1] = (uint8_t)LR11XX_BL_ERASE_FLASH_OC;
168 SPIEx.write(lr1121UpdateState->updatingRadio, packet, 2);
169 while(!hal.WaitOnBusy(lr1121UpdateState->updatingRadio))
171 DBGLN("Waiting...");
172 delay(100);
174 DBGLN("Erased");
176 lr1121UpdateState->left_over = 0;
177 SPIEx.setHwCs(false);
179 pinMode(lr1121UpdateState->updatingRadio == SX12XX_Radio_1 ? GPIO_PIN_NSS : GPIO_PIN_NSS_2, OUTPUT);
180 digitalWrite(lr1121UpdateState->updatingRadio == SX12XX_Radio_1 ? GPIO_PIN_NSS : GPIO_PIN_NSS_2, HIGH);
182 if (len) {
183 DBGLN("writing %x", len);
184 // Write len bytes to LR1121 from data
185 while (len >= 256 - lr1121UpdateState->left_over)
187 uint32_t chunk_size = len > 256 - lr1121UpdateState->left_over ? 256 - lr1121UpdateState->left_over : len;
188 writeLR1121Bytes(data, chunk_size);
189 len -= chunk_size;
190 data += chunk_size;
192 memcpy(lr1121UpdateState->packet.buffer, data, len);
193 lr1121UpdateState->left_over = len;
197 static void ReadStatusForRadio(JsonObject json, SX12XX_Radio_Number_t radio)
199 uint8_t packet[9];
200 readRegister(radio, LR11XX_BL_GET_VERSION_OC, packet, 5);
201 json["hardware"] = packet[1];
202 json["type"] = packet[2];
203 json["firmware"] = ( ( uint16_t )packet[3] << 8 ) + ( uint16_t )packet[4];
205 readRegister(radio, LR11XX_BL_GET_PIN_OC, packet, 5);
206 copyArray(packet+1, 4, json["pin"].to<JsonArray>());
208 readRegister(radio, LR11XX_BL_READ_CHIP_EUI_OC, packet, 9);
209 copyArray(packet+1, 8, json["ceui"].to<JsonArray>());
211 readRegister(radio, LR11XX_BL_READ_JOIN_EUI_OC, packet, 9);
212 copyArray(packet+1, 8, json["jeui"].to<JsonArray>());
215 static void GetLR1121Status(AsyncWebServerRequest *request)
217 AsyncJsonResponse *response = new AsyncJsonResponse();
218 JsonObject json = response->getRoot();
219 hal.end();
220 hal.init();
221 hal.reset();
223 ReadStatusForRadio(json["radio1"].to<JsonObject>(), SX12XX_Radio_1);
224 if (GPIO_PIN_NSS_2 != UNDEF_PIN)
226 ReadStatusForRadio(json["radio2"].to<JsonObject>(), SX12XX_Radio_2);
228 response->setLength();
229 request->send(response);
232 void addLR1121Handlers(AsyncWebServer &server)
234 server.on("/lr1121.json", HTTP_GET, GetLR1121Status);
235 server.on("/lr1121", HTTP_POST, WebUploadLR1121ResponseHandler, WebUploadLR1121DataHandler);
237 #endif