3 #if defined(RADIO_LR1121)
5 #include "ArduinoJson.h"
7 #include <ESPAsyncWebServer.h>
8 #include <StreamString.h>
14 #if defined(TARGET_TX)
15 #include "wifiJoystick.h"
18 struct lr1121UpdateState_s
{
19 size_t expectedFilesize
;
21 SX12XX_Radio_Number_t updatingRadio
;
26 } __attribute__((packed
)) packet
;
28 static lr1121UpdateState_s
*lr1121UpdateState
;
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
;
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
)
58 lr1121UpdateState
->totalSize
+= write_size
;
59 lr1121UpdateState
->left_over
= 0;
63 static void readRegister(SX12XX_Radio_Number_t radio
, uint16_t reg
, uint8_t *buffer
, uint32_t buffer_len
)
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
;
82 if (GPIO_PIN_NSS_2
!= UNDEF_PIN
)
84 spiAttachSS(SPIEx
.bus(), 1, GPIO_PIN_NSS_2
);
87 if (lr1121UpdateState
->totalSize
== lr1121UpdateState
->expectedFilesize
)
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");
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]);
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");
112 StreamString p
= StreamString();
113 if (lr1121UpdateState
->totalSize
!= lr1121UpdateState
->expectedFilesize
) {
114 p
.println("Not enough data uploaded!");
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
) {
130 #if defined(TARGET_TX)
131 WifiJoystick::StopJoystickService();
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;
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
)) {
152 // Ensure we're in BL mode
153 DBGLN("Ensure BL mode");
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();
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
))
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
);
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
);
192 memcpy(lr1121UpdateState
->packet
.buffer
, data
, len
);
193 lr1121UpdateState
->left_over
= len
;
197 static void ReadStatusForRadio(JsonObject json
, SX12XX_Radio_Number_t radio
)
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();
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
);