Skip to content

Commit 74c1501

Browse files
committed
MCU8MASS-1842 Catch SQNHTTPSH URC for better error logging
1 parent de0eb1e commit 74c1501

File tree

2 files changed

+130
-97
lines changed

2 files changed

+130
-97
lines changed

src/http_client.cpp

Lines changed: 129 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
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";
4143
const char HTTP_CONTENT_TYPE_APPLICATION_MULTIPART_FORM_DATA[] PROGMEM = "3";
4244
const 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

4649
HttpClientClass 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;

src/http_client.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
typedef struct {
1414
uint16_t status_code;
1515
uint32_t data_size;
16+
uint16_t curl_error_code;
1617
} HttpResponse;
1718

1819
class HttpClientClass {

0 commit comments

Comments
 (0)