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