44#include " log.h"
55#include " security_profile.h"
66#include " sequans_controller.h"
7+ #include " timeout_timer.h"
78
89#include < Arduino.h>
910#include < math.h>
1011#include < stdio.h>
1112#include < stdlib.h>
1213#include < string.h>
14+ #include < util/delay.h>
1315
1416#define HTTPS_SECURITY_PROFILE_NUMBER (3 )
1517
@@ -41,10 +43,132 @@ const char HTTP_CONTENT_TYPE_APPLICATION_OCTET_STREAM[] PROGMEM = "2";
4143const char HTTP_CONTENT_TYPE_APPLICATION_MULTIPART_FORM_DATA[] PROGMEM = " 3" ;
4244const char HTTP_CONTENT_TYPE_APPLICATION_APPLICATION_JSON[] PROGMEM = " 4" ;
4345
44- const char HTTP_RING_URC[] PROGMEM = " SQNHTTPRING" ;
46+ const char HTTP_RING_URC[] PROGMEM = " SQNHTTPRING" ;
47+ const char HTTP_SHUTDOWN_URC[] PROGMEM = " SQNHTTPSH" ;
4548
4649HttpClientClass HttpClient = HttpClientClass::instance();
4750
51+ static volatile bool got_shutdown_callback = false ;
52+ static volatile uint16_t shutdown_error_code = 0 ;
53+
54+ /* *
55+ * @brief Registered as a callback for the HTTP shutdown URC.
56+ */
57+ static void httpShutdownCallback (char * urc) {
58+ char error_code_buffer[8 ] = " " ;
59+
60+ const bool got_error_code =
61+ SequansController.extractValueFromCommandResponse (
62+ urc,
63+ 1 ,
64+ error_code_buffer,
65+ sizeof (error_code_buffer) - 1 ,
66+ 0 );
67+
68+ if (!got_error_code) {
69+ return ;
70+ }
71+
72+ got_shutdown_callback = true ;
73+ shutdown_error_code = atoi (error_code_buffer);
74+ }
75+
76+ /* *
77+ * @brief Waits for the HTTP response URC from the modem and returns the HTTP
78+ * response codes. This function also checks for an abrupt shutdown of the HTTP
79+ * connection, where the shutdown URC is sent with a cURL status code.
80+ */
81+ static HttpResponse waitForResponse (const uint32_t timeout_ms) {
82+
83+ HttpResponse http_response = {0 , 0 , 0 };
84+
85+ char http_response_buffer[HTTP_RESPONSE_MAX_LENGTH] = " " ;
86+ char http_status_code_buffer[HTTP_RESPONSE_STATUS_CODE_LENGTH + 1 ] = " " ;
87+ char data_size_buffer[HTTP_RESPONSE_DATA_SIZE_LENGTH] = " " ;
88+
89+ const auto toggle_led_whilst_waiting = [] {
90+ LedCtrl.toggle (Led::DATA, true );
91+ };
92+
93+ // If the request fails for some reason, we will retrieve the SQNHTTPSH
94+ // (shutdown) URC which has the reason for failure, so we want to listen for
95+ // that as well
96+ got_shutdown_callback = false ;
97+ SequansController.registerCallback (FV (HTTP_SHUTDOWN_URC),
98+ httpShutdownCallback);
99+
100+ if (!SequansController.waitForURC (FV (HTTP_RING_URC),
101+ http_response_buffer,
102+ sizeof (http_response_buffer),
103+ timeout_ms,
104+ toggle_led_whilst_waiting,
105+ 500 )) {
106+ LedCtrl.off (Led::DATA, true );
107+
108+ SequansController.unregisterCallback (FV (HTTP_SHUTDOWN_URC));
109+
110+ Log.warnf (F (" Did not get HTTP response before timeout on %d ms. "
111+ " Consider increasing the timeout.\r\n " ),
112+ timeout_ms);
113+
114+ return http_response;
115+ }
116+
117+ // We pass 0 as the start character here as the URC data will only
118+ // contain the payload, not the URC identifier
119+ const bool got_response_code =
120+ SequansController.extractValueFromCommandResponse (
121+ http_response_buffer,
122+ HTTP_RESPONSE_STATUS_CODE_INDEX,
123+ http_status_code_buffer,
124+ HTTP_RESPONSE_STATUS_CODE_LENGTH + 1 ,
125+ 0 );
126+
127+ const bool got_data_size =
128+ SequansController.extractValueFromCommandResponse (
129+ http_response_buffer,
130+ HTTP_RESPONSE_DATA_SIZE_INDEX,
131+ data_size_buffer,
132+ HTTP_RESPONSE_DATA_SIZE_LENGTH,
133+ 0 );
134+
135+ if (got_response_code) {
136+ http_response.status_code = atoi (http_status_code_buffer);
137+
138+ // The modem reports 0 as the status code if the connection has been
139+ // shut down with an error
140+ if (http_response.status_code == 0 ) {
141+ // The request failed, check if we got a shutdown URC. We give the
142+ // URC some time to arrive here.
143+ TimeoutTimer timer (1000 );
144+
145+ while (!got_shutdown_callback && !timer.hasTimedOut ()) {
146+ _delay_ms (1 );
147+ }
148+
149+ if (got_shutdown_callback) {
150+ if (shutdown_error_code != 0 ) {
151+ Log.errorf (
152+ F (" HTTP request failed with cURL error code: %d\r\n " ),
153+ shutdown_error_code);
154+ }
155+
156+ http_response.curl_error_code = shutdown_error_code;
157+ }
158+ }
159+ }
160+
161+ if (got_data_size) {
162+ http_response.data_size = atoi (data_size_buffer);
163+ }
164+
165+ SequansController.unregisterCallback (FV (HTTP_SHUTDOWN_URC));
166+
167+ LedCtrl.off (Led::DATA, true );
168+
169+ return http_response;
170+ }
171+
48172/* *
49173 * @brief Generic method for sending data via HTTP, either with POST or PUT.
50174 *
@@ -72,7 +196,7 @@ sendData(const char* endpoint,
72196 // other, this alleviates this
73197 SequansController.writeCommand (F (" AT" ));
74198
75- HttpResponse http_response = {0 , 0 };
199+ HttpResponse http_response = {0 , 0 , 0 };
76200
77201 if (!SequansController.writeString (
78202 F (" AT+SQNHTTPSND=0,%u,\" %s\" ,%lu,\" %s\" ,\" %s\" " ),
@@ -105,56 +229,8 @@ sendData(const char* endpoint,
105229 SequansController.writeBytes (data, data_length, true );
106230 }
107231
108- char http_response_buffer[HTTP_RESPONSE_MAX_LENGTH] = " " ;
109- char http_status_code_buffer[HTTP_RESPONSE_STATUS_CODE_LENGTH + 1 ] = " " ;
110- char data_size_buffer[HTTP_RESPONSE_DATA_SIZE_LENGTH] = " " ;
232+ http_response = waitForResponse (timeout_ms);
111233
112- const auto toggle_led_whilst_waiting = [] {
113- LedCtrl.toggle (Led::DATA, true );
114- };
115-
116- // Now we wait for the URC
117- if (!SequansController.waitForURC (FV (HTTP_RING_URC),
118- http_response_buffer,
119- sizeof (http_response_buffer),
120- timeout_ms,
121- toggle_led_whilst_waiting,
122- 500 )) {
123- LedCtrl.off (Led::DATA, true );
124- LedCtrl.off (Led::CON, true );
125-
126- Log.warnf (F (" Did not get HTTP response before timeout on %d ms. "
127- " Consider increasing the timeout.\r\n " ),
128- timeout_ms);
129-
130- return http_response;
131- }
132-
133- // We pass in NULL as the start character here as the URC data will only
134- // contain the payload, not the URC identifier
135- bool got_response_code = SequansController.extractValueFromCommandResponse (
136- http_response_buffer,
137- HTTP_RESPONSE_STATUS_CODE_INDEX,
138- http_status_code_buffer,
139- HTTP_RESPONSE_STATUS_CODE_LENGTH + 1 ,
140- (char )NULL );
141-
142- bool got_data_size = SequansController.extractValueFromCommandResponse (
143- http_response_buffer,
144- HTTP_RESPONSE_DATA_SIZE_INDEX,
145- data_size_buffer,
146- HTTP_RESPONSE_DATA_SIZE_LENGTH,
147- (char )NULL );
148-
149- if (got_response_code) {
150- http_response.status_code = atoi (http_status_code_buffer);
151- }
152-
153- if (got_data_size) {
154- http_response.data_size = atoi (data_size_buffer);
155- }
156-
157- LedCtrl.off (Led::DATA, true );
158234 LedCtrl.off (Led::CON, true );
159235
160236 return http_response;
@@ -182,7 +258,7 @@ static HttpResponse queryData(const char* endpoint,
182258 // other, this alleviates this
183259 SequansController.writeCommand (F (" AT" ));
184260
185- HttpResponse http_response = {0 , 0 };
261+ HttpResponse http_response = {0 , 0 , 0 };
186262
187263 const ResponseResult response = SequansController.writeCommand (
188264 F (" AT+SQNHTTPQRY=0,%u,\" %s\" ,\" %s\" " ),
@@ -198,52 +274,8 @@ static HttpResponse queryData(const char* endpoint,
198274 return http_response;
199275 }
200276
201- char http_response_buffer[HTTP_RESPONSE_MAX_LENGTH] = " " ;
202- char http_status_code_buffer[HTTP_RESPONSE_STATUS_CODE_LENGTH + 1 ] = " " ;
203- char data_size_buffer[HTTP_RESPONSE_DATA_SIZE_LENGTH] = " " ;
277+ http_response = waitForResponse (timeout_ms);
204278
205- const auto toggle_led_whilst_waiting = [] {
206- LedCtrl.toggle (Led::DATA, true );
207- };
208-
209- if (!SequansController.waitForURC (FV (HTTP_RING_URC),
210- http_response_buffer,
211- sizeof (http_response_buffer),
212- timeout_ms,
213- toggle_led_whilst_waiting,
214- 500 )) {
215- Log.warnf (F (" Did not get HTTP response before timeout on %d ms. "
216- " Consider increasing the timeout.\r\n " ),
217- timeout_ms);
218-
219- LedCtrl.off (Led::DATA, true );
220- LedCtrl.off (Led::CON, true );
221- return http_response;
222- }
223-
224- bool got_response_code = SequansController.extractValueFromCommandResponse (
225- http_response_buffer,
226- HTTP_RESPONSE_STATUS_CODE_INDEX,
227- http_status_code_buffer,
228- HTTP_RESPONSE_STATUS_CODE_LENGTH + 1 ,
229- (char )NULL );
230-
231- bool got_data_size = SequansController.extractValueFromCommandResponse (
232- http_response_buffer,
233- HTTP_RESPONSE_DATA_SIZE_INDEX,
234- data_size_buffer,
235- HTTP_RESPONSE_DATA_SIZE_LENGTH,
236- (char )NULL );
237-
238- if (got_response_code) {
239- http_response.status_code = atoi (http_status_code_buffer);
240- }
241-
242- if (got_data_size) {
243- http_response.data_size = atoi (data_size_buffer);
244- }
245-
246- LedCtrl.off (Led::DATA, true );
247279 LedCtrl.off (Led::CON, true );
248280
249281 return http_response;
0 commit comments