Skip to content

Commit 31d4323

Browse files
committed
fix(modem): AT-only example: support MQTT over TLS on BG96
1 parent 97831c6 commit 31d4323

File tree

5 files changed

+294
-210
lines changed

5 files changed

+294
-210
lines changed

components/esp_modem/examples/modem_tcp_client/main/modem_client.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ extern "C" void app_main(void)
9797
assert(dte);
9898

9999
/* Configure the DCE */
100-
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("lpwa.vodafone.com");
100+
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_APN);
101101

102102
/* create the DCE and initialize network manually (using AT commands) */
103103
auto dce = sock_dce::create(&dce_config, std::move(dte));
@@ -106,10 +106,10 @@ extern "C" void app_main(void)
106106
return;
107107
}
108108

109-
dce->init(1883);
109+
dce->init_sock(8883);
110110
esp_mqtt_client_config_t mqtt_config = {};
111111
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
112-
mqtt_config.broker.address.uri = "mqtt://127.0.0.1";
112+
mqtt_config.broker.address.uri = "mqtts://127.0.0.1";
113113
mqtt_config.session.message_retransmit_timeout = 10000;
114114
#else
115115
mqtt_config.uri = "mqtt://127.0.0.1";
@@ -118,13 +118,13 @@ extern "C" void app_main(void)
118118
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
119119
esp_mqtt_client_register_event(mqtt_client, static_cast<esp_mqtt_event_id_t>(ESP_EVENT_ANY_ID), mqtt_event_handler, NULL);
120120
esp_mqtt_client_start(mqtt_client);
121-
if (!dce->start(BROKER_URL, 1883)) {
121+
if (!dce->start(BROKER_URL, 8883)) {
122122
ESP_LOGE(TAG, "Failed to start DCE");
123123
return;
124124
}
125125
while (1) {
126-
while (dce->perform()) {
127-
ESP_LOGD(TAG, "...performing");
126+
while (dce->perform_sock()) {
127+
ESP_LOGV(TAG, "...performing");
128128
}
129129
ESP_LOGE(TAG, "Loop exit.. retrying");
130130
// handle disconnections errors

components/esp_modem/examples/modem_tcp_client/main/sock_commands_bg96.cpp

Lines changed: 136 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ command_result net_open(CommandableIf *t)
2828
if (out.find("+QISTATE: 0") != std::string::npos) {
2929
ESP_LOGV(TAG, "%s", out.data() );
3030
ESP_LOGD(TAG, "Already there");
31-
return command_result::OK;
31+
return command_result::FAIL;
3232
} else if (out.empty()) {
3333
return dce_commands::generic_command(t, "AT+QIACT=1\r", "OK", "ERROR", 150000);
3434
}
@@ -38,6 +38,8 @@ command_result net_open(CommandableIf *t)
3838
command_result net_close(CommandableIf *t)
3939
{
4040
ESP_LOGV(TAG, "%s", __func__ );
41+
dce_commands::generic_command(t, "AT+QICLOSE=0\r", "OK", "ERROR", 10000);
42+
esp_modem::Task::Delay(1000);
4143
return dce_commands::generic_command(t, "AT+QIDEACT=1\r", "OK", "ERROR", 40000);
4244
}
4345

@@ -104,103 +106,131 @@ command_result get_ip(CommandableIf *t, std::string &ip)
104106

105107
namespace sock_dce {
106108

107-
void Listener::start_sending(size_t len)
109+
void Responder::start_sending(size_t len)
108110
{
109111
data_to_send = len;
110112
send_stat = 0;
111113
send_cmd("AT+QISEND=0," + std::to_string(len) + "\r");
112114
}
113115

114-
void Listener::start_receiving(size_t len)
116+
void Responder::start_receiving(size_t len)
115117
{
116-
send_cmd("AT+QIRD=0," + std::to_string(size) + "\r");
118+
send_cmd("AT+QIRD=0," + std::to_string(len) + "\r");
117119
}
118120

119-
bool Listener::start_connecting(std::string host, int port)
121+
bool Responder::start_connecting(std::string host, int port)
120122
{
121123
send_cmd(R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
122124
return true;
123125
}
124126

125-
Listener::state Listener::recv(uint8_t *data, size_t len)
127+
Responder::ret Responder::recv(uint8_t *data, size_t len)
126128
{
127-
const size_t MIN_MESSAGE = 6;
128-
const std::string_view head = "+QIRD: ";
129-
auto head_pos = (char *)std::search(data, data + len, head.begin(), head.end());
130-
if (head_pos == nullptr) {
131-
return state::FAIL;
132-
}
129+
const int MIN_MESSAGE = 6;
130+
size_t actual_len = 0;
131+
auto *recv_data = (char *)data;
132+
if (data_to_recv == 0) {
133+
const std::string_view head = "+QIRD: ";
134+
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
135+
if (head_pos == nullptr) {
136+
return ret::FAIL;
137+
}
133138

134-
auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE);
135-
if (next_nl == nullptr) {
136-
return state::FAIL;
137-
}
139+
auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE);
140+
if (next_nl == nullptr) {
141+
return ret::FAIL;
142+
}
138143

139-
size_t actual_len;
140-
if (std::from_chars(head_pos + head.size(), next_nl, actual_len).ec == std::errc::invalid_argument) {
141-
ESP_LOGE(TAG, "cannot convert");
142-
return state::FAIL;
143-
}
144+
if (std::from_chars(head_pos + head.size(), next_nl, actual_len).ec == std::errc::invalid_argument) {
145+
ESP_LOGE(TAG, "cannot convert");
146+
return ret::FAIL;
147+
}
144148

145-
ESP_LOGD(TAG, "Received: actual len=%d", actual_len);
146-
if (actual_len == 0) {
147-
ESP_LOGD(TAG, "no data received");
148-
return state::FAIL;
149-
}
149+
ESP_LOGD(TAG, "Received: actual len=%d", actual_len);
150+
if (actual_len == 0) {
151+
ESP_LOGD(TAG, "no data received");
152+
return ret::FAIL;
153+
}
154+
155+
if (actual_len > buffer_size) {
156+
ESP_LOGE(TAG, "TOO BIG");
157+
return ret::FAIL;
158+
}
150159

151-
// TODO improve : compare *actual_len* & data size (to be sure that received data is equal to *actual_len*)
152-
if (actual_len > size) {
153-
ESP_LOGE(TAG, "TOO BIG");
154-
return state::FAIL;
160+
recv_data = next_nl + 1;
161+
auto first_data_len = len - (recv_data - (char *)data) /* minus size of the command marker */;
162+
if (actual_len > first_data_len) {
163+
::send(sock, recv_data, first_data_len, 0);
164+
data_to_recv = actual_len - first_data_len;
165+
return ret::NEED_MORE_DATA;
166+
}
167+
::send(sock, recv_data, actual_len, 0);
168+
} else if (data_to_recv > len) { // continue sending
169+
::send(sock, recv_data, len, 0);
170+
data_to_recv -= len;
171+
return ret::NEED_MORE_DATA;
172+
} else if (data_to_recv <= len) { // last read -> looking for "OK" marker
173+
::send(sock, recv_data, data_to_recv, 0);
174+
actual_len = data_to_recv;
155175
}
156-
::send(sock, next_nl + 1, actual_len, 0);
157176

158177
// "OK" after the data
159-
auto last_pos = (char *)memchr(next_nl + 1 + actual_len, 'O', MIN_MESSAGE);
160-
if (last_pos == nullptr || last_pos[1] != 'K') {
161-
return state::FAIL;
178+
char *last_pos = nullptr;
179+
if (actual_len + 1 + 2 /* OK */ > len) {
180+
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
181+
if (last_pos == nullptr || last_pos[1] != 'K') {
182+
data_to_recv = 0;
183+
return ret::FAIL;
184+
}
162185
}
163-
if ((char *)data + len - last_pos > MIN_MESSAGE) {
186+
if (last_pos != nullptr && (char *)data + len - last_pos - 2 > MIN_MESSAGE) {
164187
// check for async replies after the Recv header
165188
std::string_view response((char *)last_pos + 2 /* OK */, (char *)data + len - last_pos);
166-
check_async_replies(response);
189+
check_async_replies(status::RECEIVING, response);
167190
}
168-
return state::OK;
191+
// check if some other data?
192+
start_receiving(0);
193+
data_to_recv = 0;
194+
return ret::OK;
169195
}
170196

171197

172-
Listener::state Listener::send(uint8_t *data, size_t len)
198+
Responder::ret Responder::send(uint8_t *data, size_t len)
173199
{
174-
if (send_stat == 0) {
200+
if (send_stat < 3) {
175201
if (memchr(data, '>', len) == NULL) {
202+
if (send_stat++ < 2) {
203+
return Responder::ret::NEED_MORE_DATA;
204+
}
176205
ESP_LOGE(TAG, "Missed >");
177-
return state::FAIL;
206+
return ret::FAIL;
178207
}
179208
auto written = dte->write(&buffer[0], data_to_send);
180209
if (written != data_to_send) {
181210
ESP_LOGE(TAG, "written %d (%d)...", written, len);
182-
return state::FAIL;
211+
return ret::FAIL;
183212
}
184213
data_to_send = 0;
185-
send_stat++;
214+
send_stat = 3;
186215
}
187-
return Listener::state::IN_PROGRESS;
216+
return Responder::ret::IN_PROGRESS;
188217
}
189218

190-
Listener::state Listener::send(std::string_view response)
219+
Responder::ret Responder::send(std::string_view response)
191220
{
192-
if (send_stat == 1) {
221+
if (send_stat == 3) {
193222
if (response.find("SEND OK") != std::string::npos) {
194223
send_cmd("AT+QISEND=0,0\r");
195224
send_stat++;
225+
return ret::IN_PROGRESS;
196226
} else if (response.find("SEND FAIL") != std::string::npos) {
197227
ESP_LOGE(TAG, "Sending buffer full");
198-
return state::FAIL;
228+
return ret::FAIL;
199229
} else if (response.find("ERROR") != std::string::npos) {
200230
ESP_LOGE(TAG, "Failed to sent");
201-
return state::FAIL;
231+
return ret::FAIL;
202232
}
203-
} else if (send_stat == 2) {
233+
} else if (send_stat == 4) {
204234
constexpr std::string_view head = "+QISEND: ";
205235
if (response.find(head) != std::string::npos) {
206236
// Parsing +QISEND: <total_send_length>,<ackedbytes>,<unackedbytes>
@@ -215,7 +245,7 @@ Listener::state Listener::send(std::string_view response)
215245
size_t value;
216246
if (std::from_chars(response.data(), next_comma, value).ec == std::errc::invalid_argument) {
217247
ESP_LOGE(TAG, "cannot convert");
218-
return state::FAIL;
248+
return ret::FAIL;
219249
}
220250

221251
switch (property++) {
@@ -224,49 +254,94 @@ Listener::state Listener::send(std::string_view response)
224254
case 1: ack = value;
225255
break;
226256
default:
227-
return state::FAIL;
257+
return ret::FAIL;
228258
}
229259
response = response.substr(pos + 1);
230260
}
231261
if (std::from_chars(response.data(), response.data() + pos, unack).ec == std::errc::invalid_argument) {
232-
return state::FAIL;
262+
return ret::FAIL;
233263
}
234264

235-
// TODO improve : need check *total* & *ack* values, or loop (every 5 sec) with 90s or 120s timeout
236265
if (ack < total) {
237-
ESP_LOGE(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack));
266+
ESP_LOGD(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack));
267+
if (total - ack > 64) {
268+
ESP_LOGW(TAG, "Need a pause: missing %d bytes acked", (total - ack));
269+
return ret::NEED_MORE_TIME;
270+
}
238271
}
239-
return state::OK;
272+
send_stat = 0;
273+
return ret::OK;
240274
} else if (response.find("ERROR") != std::string::npos) {
241275
ESP_LOGE(TAG, "Failed to check sending");
242-
return state::FAIL;
276+
return ret::FAIL;
243277
}
244278

245279
}
246-
return Listener::state::IN_PROGRESS;
280+
return Responder::ret::IN_PROGRESS;
247281
}
248282

249-
Listener::state Listener::connect(std::string_view response)
283+
Responder::ret Responder::connect(std::string_view response)
250284
{
251285
if (response.find("+QIOPEN: 0,0") != std::string::npos) {
252286
ESP_LOGI(TAG, "Connected!");
253-
return state::OK;
287+
return ret::OK;
254288
}
255289
if (response.find("ERROR") != std::string::npos) {
256290
ESP_LOGE(TAG, "Failed to open");
257-
return state::FAIL;
291+
return ret::FAIL;
258292
}
259-
return Listener::state::IN_PROGRESS;
293+
return Responder::ret::IN_PROGRESS;
260294
}
261295

262-
void Listener::check_async_replies(std::string_view &response) const
296+
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
263297
{
264298
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
265299
if (response.find("+QIURC: \"recv\",0") != std::string::npos) {
266300
uint64_t data_ready = 1;
267301
write(data_ready_fd, &data_ready, sizeof(data_ready));
268302
ESP_LOGD(TAG, "Got data on modem!");
303+
} else if (response.find("+QIRD: ") != std::string::npos) {
304+
static constexpr std::string_view head = "+QIRD: ";
305+
size_t head_pos = response.find(head);
306+
// Parsing +QIURC: <total_receive_length>,<have_read_length>,<unread_length>
307+
response = response.substr(head_pos + head.size());
308+
int next_cr = response.find('\r');
309+
if (next_cr != std::string::npos) {
310+
response = response.substr(next_cr - 2, next_cr);
311+
if (response.find(",0") != std::string::npos) {
312+
ESP_LOGV(TAG, "Receiving done");
313+
} else {
314+
uint64_t data_ready = 1;
315+
write(data_ready_fd, &data_ready, sizeof(data_ready));
316+
ESP_LOGD(TAG, "Got data on modem!");
317+
}
318+
}
319+
} else if (response.find("+QIURC: \"closed\",0") != std::string::npos) {
320+
return ret::FAIL;
321+
}
322+
if (state == status::SENDING) {
323+
return send(response);
324+
} else if (state == status::CONNECTING) {
325+
return connect(response);
326+
}
327+
return ret::IN_PROGRESS;
328+
}
329+
330+
Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
331+
{
332+
if (state == status::SENDING) {
333+
return send(data, len);
334+
}
335+
if (state == status::RECEIVING) {
336+
return recv(data, len);
269337
}
338+
return Responder::ret::IN_PROGRESS;
339+
}
340+
341+
status Responder::pending()
342+
{
343+
send_cmd("AT+QISEND=0,0\r");
344+
return status::SENDING;
270345
}
271346

272347

0 commit comments

Comments
 (0)