Skip to content

Commit 5399336

Browse files
authored
Add LHTTPUpdate library fot FOTA update (#68)
* Enable FOTA middleware in liblinkit.a. * LHTTPUpdate implementation by refering to the fota_cli implementation.
1 parent c816e4c commit 5399336

File tree

14 files changed

+512
-22
lines changed

14 files changed

+512
-22
lines changed

middleware/third_party/arduino/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ include $(SOURCE_DIR)/driver/chip/mt7687/module.mk
6767
# Bluetooth files
6868
include $(SOURCE_DIR)/middleware/MTK/bluetooth/module.mk
6969

70+
# FOTA
71+
include $(SOURCE_DIR)/middleware/MTK/fota/module.mk
72+
7073
# WiFi files
7174
include $(SOURCE_DIR)/middleware/MTK/wifi_service/combo/module.mk
7275
CXXFLAGS += -I$(SOURCE_DIR)/middleware/MTK/wifi_service/combo/inc

middleware/third_party/arduino/hardware/arduino/mt7697/boards.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,15 @@ linkit_7697.build.rtos_inc=kernel/rtos/FreeRTOS/Source/include/
4444
linkit_7697.build.rtos_cm4_inc=kernel/rtos/FreeRTOS/Source/portable/GCC/ARM_CM4F/
4545
linkit_7697.build.lwip_inc=middleware/third_party/lwip/src/include/
4646
linkit_7697.build.lwip_ports_inc=middleware/third_party/lwip/ports/include/
47-
linkit_7697.build.httpclient_inc=middleware/third_party/httpclient/src/inc/
47+
linkit_7697.build.httpclient_inc=middleware/third_party/httpclient/inc/
4848
linkit_7697.build.mbedtls_inc=middleware/third_party/mbedtls/include
4949
linkit_7697.build.mbedtls_configs_inc=middleware/third_party/mbedtls/configs
5050
linkit_7697.build.connsys_inc=middleware/MTK/connsys/inc/
5151
linkit_7697.build.wifi_inc=middleware/MTK/wifi_service/combo/inc/
5252
linkit_7697.build.ble_inc=middleware/MTK/bluetooth/inc/
5353
linkit_7697.build.nvdm_inc=middleware/MTK/nvdm/inc/
5454
linkit_7697.build.dhcpd_inc=middleware/MTK/dhcpd/inc/
55+
linkit_7697.build.fota_inc=middleware/MTK/fota/inc/
5556

5657
# New in SDK v4.3: prebuilt headers are separated
5758
linkit_7697.build.prebuilt_ble_inc=prebuilt/middleware/MTK/bluetooth/inc/
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include <LWiFi.h>
2+
#include <LHTTPUpdate.h>
3+
4+
char ssid[] = "yourapssid"; // your WPA2 network SSID (AP name)
5+
char pass[] = "yourpassword"; // your WPA2 network password
6+
7+
// URL to the FOTA(Firmware-over-the-air) binary.
8+
//
9+
// The FOTA binary is a compressed application binary.
10+
// Download the "FOTA ROM Package" Tool from
11+
// https://docs.labs.mediatek.com/resource/mt7687-mt7697/en/downloads#Downloads-Tools
12+
// to convert the application binary built by Ardino IDE to FOTA binary.
13+
//
14+
// To get the application binary of your sketch,
15+
// select "Sketch > Export Compiled Binary" from the IDE menu.
16+
char fotaBinaryURL[] = "http://download.labs.mediatek.com/resource/fota_example.bin";
17+
18+
void setup() {
19+
Serial.begin(9600);
20+
21+
// Show our firmware name - so that we know update succeeded or not.
22+
Serial.println("HTTPUpdate example begins.");
23+
24+
// attempt to connect to Wifi network:
25+
int status = WL_IDLE_STATUS;
26+
while (status != WL_CONNECTED) {
27+
Serial.print("Attempting to connect to SSID: ");
28+
Serial.println(ssid);
29+
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
30+
status = WiFi.begin(ssid, pass);
31+
}
32+
Serial.println("Connected to wifi");
33+
34+
Serial.println("Begin downloading new firmware");
35+
36+
LHTTPUpdate fotaUpdater;
37+
38+
// change the "false" below to "true" to reboot immediately in the update() call.
39+
fotaUpdater.rebootOnUpdate(false);
40+
41+
// Connect to URL, download the FOTA binary and store it to on-board flash.
42+
// After reboot, the new firmware will be decompressed from the FOTA binary and applied.
43+
if (fotaUpdater.update(fotaBinaryURL)) {
44+
Serial.println("Press RST key to reboot to updated firmware version.");
45+
Serial.println("After reboot, it may take a while to update the firmware.");
46+
Serial.println("The USR LED lights up during the update process.");
47+
} else {
48+
Serial.println("Update Failed - check your internet connection and FOTA binary.");
49+
Serial.print("Possible reason: ");
50+
Serial.println(fotaUpdater.getLastErrorString());
51+
}
52+
53+
}
54+
55+
void loop() {
56+
delay(10);
57+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#######################################
2+
# Syntax Coloring Map For LHTTPUpdate
3+
#######################################
4+
5+
6+
#######################################
7+
# Datatypes (KEYWORD1)
8+
#######################################
9+
10+
LHTTPUpdate KEYWORD1
11+
FotaError KEYWORD1
12+
13+
#######################################
14+
# Methods and Functions (KEYWORD2)
15+
#######################################
16+
17+
getLastError KEYWORD2
18+
getLastErrorString KEYWORD2
19+
update KEYWORD2
20+
rebootOnUpdate KEYWORD2
21+
22+
23+
#######################################
24+
# Constants (LITERAL1)
25+
#######################################
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name=LHTTPUpdate
2+
version=1.0
3+
author=MediaTek Labs
4+
maintainer=Pablo Sun <https://github.com/pablosun>
5+
sentence=Firmware Over The Air through HTTP client
6+
paragraph=Use this library to download a FOTA binary hosted in a HTTP server and perform a firmware update.
7+
category=Communication
8+
url=
9+
architectures=linkit_rtos
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
#include "LHTTPUpdate.h"
2+
#include <mutex>
3+
4+
extern "C" {
5+
#include <FreeRTOS.h>
6+
#include <task.h>
7+
#include <hal_flash.h>
8+
#include <flash_map.h>
9+
#include <httpclient.h>
10+
#include <hal_wdt.h>
11+
#include <fota.h>
12+
#include "log_dump.h"
13+
}
14+
15+
class IRQLock
16+
{
17+
public:
18+
IRQLock() {
19+
taskDISABLE_INTERRUPTS();
20+
}
21+
22+
~IRQLock() {
23+
taskENABLE_INTERRUPTS();
24+
}
25+
26+
// Copy constructor is deleted.
27+
IRQLock( const IRQLock& ) = delete;
28+
};
29+
30+
enum FotaStatus {
31+
FOTA_STATUS_OK = 1,
32+
FOTA_STATUS_FAILED = 0
33+
};
34+
35+
class FOTARegion
36+
{
37+
public:
38+
FOTARegion():
39+
m_offset(0)
40+
{
41+
42+
}
43+
44+
public:
45+
46+
// resets the internal file pointer to beginning of the FOTA region
47+
void reset() {
48+
m_offset = 0;
49+
}
50+
51+
// the internal file pointer increments automatically
52+
FotaError write(const uint8_t* buffer, uint32_t length);
53+
54+
protected:
55+
const size_t m_size = FOTA_LENGTH; // available length of the flash region
56+
const size_t m_base = FOTA_BASE;
57+
const size_t m_blockSize = 4096;
58+
size_t m_offset; // the file pointer
59+
};
60+
61+
FotaError FOTARegion::write(const uint8_t* buffer, uint32_t length)
62+
{
63+
//
64+
// Check the validity of parameters
65+
//
66+
if (buffer == 0 || length == 0) {
67+
return FOTA_ERROR_BINARY_EMPTY;
68+
}
69+
70+
if ((m_offset + length) > m_size) {
71+
return FOTA_ERROR_BINARY_TOO_LARGE;
72+
}
73+
74+
//
75+
// Erase blocks: if the write is to the block boundary, erase the block
76+
//
77+
const uint32_t addr = m_base + m_offset;
78+
const uint32_t block_idx_start = addr / m_blockSize;
79+
const uint32_t block_idx_end = (addr + length - 1) / m_blockSize;
80+
81+
if ((addr % m_blockSize) == 0) {
82+
IRQLock irq;
83+
if (hal_flash_erase(addr, HAL_FLASH_BLOCK_4K) < 0) {
84+
return FOTA_ERROR_FLASH_OP;
85+
}
86+
}
87+
88+
uint32_t i = block_idx_start + 1;
89+
while (i <= block_idx_end) {
90+
const uint32_t erase_addr = i * m_blockSize;
91+
IRQLock irq;
92+
if (hal_flash_erase(erase_addr, HAL_FLASH_BLOCK_4K) < 0) {
93+
return FOTA_ERROR_FLASH_OP;
94+
}
95+
i++;
96+
}
97+
98+
//
99+
// Write data
100+
//
101+
do {
102+
IRQLock irq;
103+
if (hal_flash_write(addr, (uint8_t *)buffer, length) < 0) {
104+
return FOTA_ERROR_FLASH_OP;
105+
}
106+
} while (false);
107+
108+
// Increment file pointer
109+
m_offset += length;
110+
return FOTA_ERROR_NO_ERROR;
111+
}
112+
113+
LHTTPUpdate::LHTTPUpdate(void)
114+
{
115+
memset(&_httpClient, 0, sizeof(_httpClient));
116+
}
117+
118+
LHTTPUpdate::~LHTTPUpdate(void)
119+
{
120+
}
121+
122+
void LHTTPUpdate::rebootOnUpdate(bool reboot)
123+
{
124+
_rebootOnUpdate = reboot;
125+
}
126+
127+
int LHTTPUpdate::_fota_http_retrieve_get(const char* get_url)
128+
{
129+
//
130+
// send GET request to server
131+
//
132+
httpclient_data_t client_data = {
133+
.is_more = 0,
134+
.is_chunked = 0,
135+
.retrieve_len = 0,
136+
.response_content_len = 0,
137+
.post_buf_len = 0,
138+
.response_buf_len = 0,
139+
.header_buf_len = 0,
140+
.post_content_type = NULL,
141+
.post_buf = NULL,
142+
.response_buf = NULL,
143+
.header_buf = NULL,
144+
};
145+
146+
_fotaBuffer.resize(4 * 1024 + 1);
147+
client_data.response_buf = (char*)&_fotaBuffer[0];
148+
client_data.response_buf_len = (int)_fotaBuffer.size();
149+
150+
HTTPCLIENT_RESULT ret = httpclient_send_request(&_httpClient, (char*)get_url, HTTPCLIENT_GET, &client_data);
151+
if (ret < 0) {
152+
pr_debug("[FOTA DL] http client fail to send request \n");
153+
_lastError = FOTA_ERROR_CONNECTION;
154+
return FOTA_STATUS_FAILED;
155+
}
156+
157+
int count = 0;
158+
int recv_temp = 0;
159+
int data_len = 0;
160+
161+
//
162+
// get server response & write to flash
163+
//
164+
FOTARegion fotaRegion;
165+
do {
166+
ret = httpclient_recv_response(&_httpClient, &client_data);
167+
if (ret < 0) {
168+
pr_debug("[FOTA DL] http client recv response error, ret = %d \n", ret);
169+
_lastError = FOTA_ERROR_CONNECTION;
170+
return FOTA_STATUS_FAILED;
171+
}
172+
173+
if (recv_temp == 0)
174+
{
175+
recv_temp = client_data.response_content_len;
176+
}
177+
178+
pr_debug("[FOTA DL] retrieve_len = %d \n", client_data.retrieve_len);
179+
180+
data_len = recv_temp - client_data.retrieve_len;
181+
pr_debug("[FOTA DL] data_len = %u \n", data_len);
182+
183+
count += data_len;
184+
recv_temp = client_data.retrieve_len;
185+
186+
pr_debug("[FOTA DL] total data received %u \n", count);
187+
188+
const FotaError write_ret = fotaRegion.write((const uint8_t*)client_data.response_buf, data_len);
189+
if (FOTA_ERROR_NO_ERROR != write_ret) {
190+
pr_debug("[FOTA DL] fail to write flash, write_ret = %d \n", write_ret);
191+
_lastError = write_ret;
192+
return FOTA_STATUS_FAILED;
193+
}
194+
195+
pr_debug("[FOTA DL] download progrses = %u \n", count * 100 / client_data.response_content_len);
196+
197+
} while (ret == HTTPCLIENT_RETRIEVE_MORE_DATA);
198+
199+
//
200+
// report back to user
201+
//
202+
pr_debug("[FOTA DL] total length: %d \n", client_data.response_content_len);
203+
if (count != client_data.response_content_len || httpclient_get_response_code(&_httpClient) != 200) {
204+
pr_debug("[FOTA DL] data received not completed, or invalid error code \r\n");
205+
_lastError = FOTA_ERROR_BINARY_INCOMPLETE;
206+
return FOTA_STATUS_FAILED;
207+
}
208+
else if (count == 0) {
209+
pr_debug("[FOTA DL] receive length is zero, file not found \n");
210+
_lastError = FOTA_ERROR_BINARY_EMPTY;
211+
return FOTA_STATUS_FAILED;
212+
}
213+
else {
214+
pr_debug("[FOTA DL] download success \n");
215+
return FOTA_STATUS_OK;
216+
}
217+
}
218+
219+
int LHTTPUpdate::update(const String& url)
220+
{
221+
// connect to server
222+
HTTPCLIENT_RESULT connectResult = httpclient_connect(&_httpClient, (char*)url.c_str());
223+
224+
if (HTTPCLIENT_OK != connectResult) {
225+
pr_debug("[FOTA DL] http client connect error\n");
226+
_lastError = FOTA_ERROR_CONNECTION;
227+
httpclient_close(&_httpClient);
228+
return FOTA_STATUS_FAILED;
229+
}
230+
231+
// download FOTA bin and write to FOTA flash region
232+
int ret = _fota_http_retrieve_get(url.c_str());
233+
pr_debug("[FOTA DL] Download result = %d\n", (int)ret);
234+
235+
if (FOTA_STATUS_OK == ret) {
236+
// continue...cd
237+
//Trigger a FOTA update.
238+
fota_ret_t ret;
239+
ret = fota_trigger_update();
240+
if (ret == FOTA_TRIGGER_SUCCESS && _rebootOnUpdate) {
241+
pr_debug("[FOTA] reboot for update");
242+
hal_wdt_software_reset();
243+
}
244+
return FOTA_STATUS_OK;
245+
} else {
246+
return FOTA_STATUS_FAILED;
247+
}
248+
}
249+
250+
// returns one of the reasons in enum FotaError.
251+
int LHTTPUpdate::getLastError(void)
252+
{
253+
return _lastError;
254+
}
255+
256+
// returns string of error reason
257+
String LHTTPUpdate::getLastErrorString(void)
258+
{
259+
switch (getLastError()) {
260+
case FOTA_ERROR_NO_ERROR:
261+
return "No error";
262+
case FOTA_ERROR_CONNECTION:
263+
return "Connection failed";
264+
case FOTA_ERROR_BINARY_TOO_LARGE:
265+
return "Binary too large";
266+
case FOTA_ERROR_BINARY_EMPTY:
267+
return "Binary is empty";
268+
case FOTA_ERROR_BINARY_INCOMPLETE:
269+
return "Binary download incomplete";
270+
case FOTA_ERROR_FLASH_OP:
271+
return "Flash write failed";
272+
default:
273+
return "Unknown";
274+
}
275+
}

0 commit comments

Comments
 (0)