Skip to content

Commit 9bbecdb

Browse files
committed
MCU8MASS-1249 Fix buffer size checking in readResponse(), report buffer overflow explicitely and add example for custom AT commands with SequansController
1 parent 13e451d commit 9bbecdb

File tree

3 files changed

+178
-2
lines changed

3 files changed

+178
-2
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/**
2+
* @brief This example demonstrates how to use the SequansController to send AT
3+
* commands to the Sequans GM02S modem.
4+
*/
5+
6+
#include <log.h>
7+
#include <lte.h>
8+
#include <sequans_controller.h>
9+
10+
#include <string.h>
11+
12+
static char ping_response[512] = "";
13+
14+
static volatile size_t ping_response_index = 0;
15+
16+
static volatile size_t ping_messages_received = 0;
17+
18+
static void ping_callback(char* message) {
19+
// We don't want to include a whitespace at the start of the string, so we
20+
// remove one from the length and move the pointer by one
21+
const size_t message_length = strlen(message) - 1;
22+
message = message + 1;
23+
24+
// We store all the notification messsage data in a buffer where we adjust
25+
// the index of the data according to the length of the message
26+
//
27+
// We do messsage + 1 here to move the pointer so we don't include a
28+
// whitespace.
29+
memcpy(ping_response + ping_response_index, message, message_length);
30+
31+
ping_response_index += message_length;
32+
33+
// Append new line for every retrieved message
34+
ping_response[ping_response_index++] = '\r';
35+
ping_response[ping_response_index++] = '\n';
36+
37+
ping_messages_received++;
38+
}
39+
40+
void setup() {
41+
Log.begin(115200);
42+
43+
Log.info("Starting up example for custom AT commands");
44+
45+
// If we didn't want to connect to the network, we could start the
46+
// SequansController directly by: SequansController.begin();
47+
// Lte.begin() will start the SequansController in its begin()
48+
Lte.begin();
49+
50+
// Here we enable verbose error messages
51+
SequansController.writeCommand("AT+CMEE=2");
52+
53+
// Here we perform a ping with incorrect parameter in order to trigger the
54+
// error message. Note that if the modem returns an error with the command,
55+
// the SequansController will retry 5 times with an interval of 2 seconds,
56+
// so this will take 10 seconds
57+
//
58+
// Here we also pass an optional response buffer to the function which will
59+
// be filled with the response from the command
60+
char response[128] = "";
61+
ResponseResult response_result =
62+
SequansController.writeCommand("AT+PING=0", response, sizeof(response));
63+
64+
if (response_result == ResponseResult::OK) {
65+
Log.infof("Command written successfully, this should not happen");
66+
} else {
67+
Log.errorf("Error writing command, the response was: %s\r\n", response);
68+
}
69+
70+
// --------------------- Notifications & Commands -------------------------
71+
72+
// Now we're going to perform a ping to google in a slighly different way
73+
// and set up a notification so that we can inspect the result
74+
75+
// First we set up a callback when the modem sends back an URC (unsolicited
76+
// response code), which can be though of as a notification.
77+
//
78+
// The different URCs are documented in Sequans' AT command reference.
79+
SequansController.registerCallback("PING", ping_callback);
80+
81+
// Instead of writing a command, we use the writeBytes function here. It
82+
// will simply write the bytes we provide and not check whether the command
83+
// was written successfully. We do it this way for this example as we want
84+
// to utilise notifications and the ping command is blocking. This is thus
85+
// purely an example.
86+
const char* command = "AT+PING=\"www.google.com\"";
87+
SequansController.writeBytes((uint8_t*)command, strlen(command), true);
88+
89+
// The default ping will retrieve four responses, so wait for them
90+
while (ping_messages_received < 4) {}
91+
92+
Log.infof("Received the following ping response:\r\n%s\r\n", ping_response);
93+
94+
// -------------- Extracting Parameters from Responses --------------------
95+
96+
// Here we will utilise AT+CEREG?, which returns data about the current
97+
// connection. We can use it to check if we are connected to the network.
98+
response_result =
99+
SequansController.writeCommand("AT+CEREG?", response, sizeof(response));
100+
101+
if (response_result == ResponseResult::OK) {
102+
103+
Log.infof("Command written successfully, the response was: %s\r\n",
104+
response);
105+
106+
char value_buffer[8] = "";
107+
108+
// Extract the 1st index (zero indexed), which tells us about the
109+
// connection status
110+
if (SequansController.extractValueFromCommandResponse(
111+
response,
112+
1,
113+
value_buffer,
114+
sizeof(value_buffer))) {
115+
Log.infof("The value was: %s\r\n", value_buffer);
116+
} else {
117+
Log.error("Failed to extract value");
118+
}
119+
} else {
120+
Log.errorf("Error writing command, the response was: %s\r\n", response);
121+
}
122+
123+
Lte.end();
124+
}
125+
126+
void loop() {}

src/sequans_controller.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,17 @@ SequansControllerClass::writeCommand(const char* command,
639639
writeBytes((const uint8_t*)command, strlen(command), true);
640640
response = readResponse(result_buffer, result_buffer_size);
641641

642+
if (response == ResponseResult::BUFFER_OVERFLOW &&
643+
result_buffer != NULL) {
644+
645+
strcpy(result_buffer, "");
646+
Log.error(
647+
"SequansController.writeCommand() called with buffer which "
648+
"is too small for the response. Increase response buffer "
649+
"size.");
650+
return response;
651+
}
652+
642653
if (response != ResponseResult::OK) {
643654
delay(COMMAND_RETRY_SLEEP_MS);
644655
}
@@ -694,11 +705,11 @@ SequansControllerClass::readResponse(char* out_buffer,
694705
// Reset timeout timer
695706
start = millis();
696707

697-
buffer[i++] = (uint8_t)readByte();
708+
buffer[i++] = (char)readByte();
698709

699710
// We won't check for the buffer having a termination until at least
700711
// 2 bytes are in it
701-
if (i == 0) {
712+
if (i < 2) {
702713
continue;
703714
}
704715

test/test_examples.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,45 @@ def example_test_data():
7676
test_data(dict): Dictionary with the test for each example
7777
"""
7878
return {
79+
"custom_at_commands": [
80+
{
81+
"expectation": "\\[INFO\\] Starting up example for custom AT commands"
82+
},
83+
{
84+
"expectation": "\\[INFO\\] Connecting to operator.{0,}OK!"
85+
},
86+
{
87+
"expectation": "\\[ERROR\\] Error writing command, the response was:"
88+
},
89+
{
90+
"expectation": "+CME ERROR: invalid characters in text string"
91+
},
92+
{
93+
"expectation": ""
94+
},
95+
{
96+
"expectation": "\\[INFO\\] Received the following ping response:"
97+
},
98+
{
99+
"expectation": "\\d{1},\\d{1,3}.\\d{1,3}.\\d{1,3}.\\d{1,3},\\d{1,},\\d{1,}",
100+
"repeat": 4
101+
},
102+
{
103+
"expectation": ""
104+
},
105+
{
106+
"expectation": "\\[INFO\\] Command written successfully, the response was:"
107+
},
108+
{
109+
"expectation": "\\+CEREG: 5,5,\"[a-zA-Z0-9]{1,}\",\"[a-zA-Z0-9]{1,}\",\\d{1,},,,\"[a-zA-Z0-9]{1,}\",\"[a-zA-Z0-9]{1,}\""
110+
},
111+
{
112+
"expectation": ""
113+
},
114+
{
115+
"expectation": "The value was: 5"
116+
},
117+
],
79118
"debug_modem": [
80119
{
81120
"command": "AT\r",

0 commit comments

Comments
 (0)