ESPHome  2022.12.8
sim800l.cpp
Go to the documentation of this file.
1 #include "sim800l.h"
2 #include "esphome/core/log.h"
3 #include <cstring>
4 
5 namespace esphome {
6 namespace sim800l {
7 
8 static const char *const TAG = "sim800l";
9 
10 const char ASCII_CR = 0x0D;
11 const char ASCII_LF = 0x0A;
12 
14  if (this->watch_dog_++ == 2) {
15  this->state_ = STATE_INIT;
16  this->write(26);
17  }
18 
19  if (this->expect_ack_)
20  return;
21 
22  if (state_ == STATE_INIT) {
23  if (this->registered_ && this->send_pending_) {
24  this->send_cmd_("AT+CSCS=\"GSM\"");
26  } else if (this->registered_ && this->dial_pending_) {
27  this->send_cmd_("AT+CSCS=\"GSM\"");
28  this->state_ = STATE_DIALING1;
29  } else if (this->registered_ && this->connect_pending_) {
30  this->connect_pending_ = false;
31  ESP_LOGI(TAG, "Connecting...");
32  this->send_cmd_("ATA");
33  this->state_ = STATE_ATA_SENT;
34  } else if (this->registered_ && this->send_ussd_pending_) {
35  this->send_cmd_("AT+CSCS=\"GSM\"");
36  this->state_ = STATE_SEND_USSD1;
37  } else if (this->registered_ && this->disconnect_pending_) {
38  this->disconnect_pending_ = false;
39  ESP_LOGI(TAG, "Disconnecting...");
40  this->send_cmd_("ATH");
41  } else if (this->registered_ && this->call_state_ != 6) {
42  send_cmd_("AT+CLCC");
43  this->state_ = STATE_CHECK_CALL;
44  return;
45  } else {
46  this->send_cmd_("AT");
47  this->state_ = STATE_SETUP_CMGF;
48  }
49  this->expect_ack_ = true;
50  } else if (state_ == STATE_RECEIVED_SMS) {
51  // Serial Buffer should have flushed.
52  // Send cmd to delete received sms
53  char delete_cmd[20];
54  sprintf(delete_cmd, "AT+CMGD=%d", this->parse_index_);
55  this->send_cmd_(delete_cmd);
56  this->state_ = STATE_CHECK_SMS;
57  this->expect_ack_ = true;
58  }
59 }
60 
61 void Sim800LComponent::send_cmd_(const std::string &message) {
62  ESP_LOGV(TAG, "S: %s - %d", message.c_str(), this->state_);
63  this->watch_dog_ = 0;
64  this->write_str(message.c_str());
65  this->write_byte(ASCII_CR);
66  this->write_byte(ASCII_LF);
67 }
68 
69 void Sim800LComponent::parse_cmd_(std::string message) {
70  if (message.empty())
71  return;
72 
73  ESP_LOGV(TAG, "R: %s - %d", message.c_str(), this->state_);
74 
75  if (this->state_ != STATE_RECEIVE_SMS) {
76  if (message == "RING") {
77  // Incoming call...
78  this->state_ = STATE_PARSE_CLIP;
79  this->expect_ack_ = false;
80  } else if (message == "NO CARRIER") {
81  if (this->call_state_ != 6) {
82  this->call_state_ = 6;
83  this->call_disconnected_callback_.call();
84  }
85  }
86  }
87 
88  bool ok = message == "OK";
89  if (this->expect_ack_) {
90  this->expect_ack_ = false;
91  if (!ok) {
92  if (this->state_ == STATE_SETUP_CMGF && message == "AT") {
93  // Expected ack but AT echo received
94  this->state_ = STATE_DISABLE_ECHO;
95  this->expect_ack_ = true;
96  } else {
97  ESP_LOGW(TAG, "Not ack. %d %s", this->state_, message.c_str());
98  this->state_ = STATE_IDLE; // Let it timeout
99  return;
100  }
101  }
102  } else if (ok && (this->state_ != STATE_PARSE_SMS_RESPONSE && this->state_ != STATE_CHECK_CALL &&
103  this->state_ != STATE_RECEIVE_SMS && this->state_ != STATE_DIALING2)) {
104  ESP_LOGW(TAG, "Received unexpected OK. Ignoring");
105  return;
106  }
107 
108  switch (this->state_) {
109  case STATE_INIT: {
110  // While we were waiting for update to check for messages, this notifies a message
111  // is available.
112  bool message_available = message.compare(0, 6, "+CMTI:") == 0;
113  if (!message_available) {
114  if (message == "RING") {
115  // Incoming call...
116  this->state_ = STATE_PARSE_CLIP;
117  } else if (message == "NO CARRIER") {
118  if (this->call_state_ != 6) {
119  this->call_state_ = 6;
120  this->call_disconnected_callback_.call();
121  }
122  } else if (message.compare(0, 6, "+CUSD:") == 0) {
123  // Incoming USSD MESSAGE
124  this->state_ = STATE_CHECK_USSD;
125  }
126  break;
127  }
128 
129  // Else fall thru ...
130  }
131  case STATE_CHECK_SMS:
132  send_cmd_("AT+CMGL=\"ALL\"");
134  this->parse_index_ = 0;
135  break;
136  case STATE_DISABLE_ECHO:
137  send_cmd_("ATE0");
138  this->state_ = STATE_SETUP_CMGF;
139  this->expect_ack_ = true;
140  break;
141  case STATE_SETUP_CMGF:
142  send_cmd_("AT+CMGF=1");
143  this->state_ = STATE_SETUP_CLIP;
144  this->expect_ack_ = true;
145  break;
146  case STATE_SETUP_CLIP:
147  send_cmd_("AT+CLIP=1");
148  this->state_ = STATE_CREG;
149  this->expect_ack_ = true;
150  break;
151  case STATE_SETUP_USSD:
152  send_cmd_("AT+CUSD=1");
153  this->state_ = STATE_CREG;
154  this->expect_ack_ = true;
155  break;
156  case STATE_SEND_USSD1:
157  this->send_cmd_("AT+CUSD=1, \"" + this->ussd_ + "\"");
158  this->state_ = STATE_SEND_USSD2;
159  break;
160  case STATE_SEND_USSD2:
161  ESP_LOGD(TAG, "SendUssd2: '%s'", message.c_str());
162  if (message == "OK") {
163  // Dialing
164  ESP_LOGD(TAG, "Dialing ussd code: '%s' done.", this->ussd_.c_str());
165  this->state_ = STATE_CHECK_USSD;
166  this->send_ussd_pending_ = false;
167  } else {
168  this->set_registered_(false);
169  this->state_ = STATE_INIT;
170  this->send_cmd_("AT+CMEE=2");
171  this->write(26);
172  }
173  break;
174  case STATE_CHECK_USSD:
175  ESP_LOGD(TAG, "Check ussd code: '%s'", message.c_str());
176  if (message.compare(0, 6, "+CUSD:") == 0) {
177  this->state_ = STATE_RECEIVED_USSD;
178  this->ussd_ = "";
179  size_t start = 10;
180  size_t end = message.find_last_of(',');
181  if (end > start) {
182  this->ussd_ = message.substr(start + 1, end - start - 2);
183  this->ussd_received_callback_.call(this->ussd_);
184  }
185  }
186  // Otherwise we receive another OK, we do nothing just wait polling to continuously check for SMS
187  if (message == "OK")
188  this->state_ = STATE_INIT;
189  break;
190  case STATE_CREG:
191  send_cmd_("AT+CREG?");
192  this->state_ = STATE_CREG_WAIT;
193  break;
194  case STATE_CREG_WAIT: {
195  // Response: "+CREG: 0,1" -- the one there means registered ok
196  // "+CREG: -,-" means not registered ok
197  bool registered = message.compare(0, 6, "+CREG:") == 0 && (message[9] == '1' || message[9] == '5');
198  if (registered) {
199  if (!this->registered_) {
200  ESP_LOGD(TAG, "Registered OK");
201  }
202  this->state_ = STATE_CSQ;
203  this->expect_ack_ = true;
204  } else {
205  ESP_LOGW(TAG, "Registration Fail");
206  if (message[7] == '0') { // Network registration is disable, enable it
207  send_cmd_("AT+CREG=1");
208  this->expect_ack_ = true;
209  this->state_ = STATE_SETUP_CMGF;
210  } else {
211  // Keep waiting registration
212  this->state_ = STATE_INIT;
213  }
214  }
215  set_registered_(registered);
216  break;
217  }
218  case STATE_CSQ:
219  send_cmd_("AT+CSQ");
220  this->state_ = STATE_CSQ_RESPONSE;
221  break;
222  case STATE_CSQ_RESPONSE:
223  if (message.compare(0, 5, "+CSQ:") == 0) {
224  size_t comma = message.find(',', 6);
225  if (comma != 6) {
226  int rssi = parse_number<int>(message.substr(6, comma - 6)).value_or(0);
227 
228 #ifdef USE_SENSOR
229  if (this->rssi_sensor_ != nullptr) {
230  this->rssi_sensor_->publish_state(rssi);
231  } else {
232  ESP_LOGD(TAG, "RSSI: %d", rssi);
233  }
234 #else
235  ESP_LOGD(TAG, "RSSI: %d", rssi);
236 #endif
237  }
238  }
239  this->expect_ack_ = true;
240  this->state_ = STATE_CHECK_SMS;
241  break;
243  if (message.compare(0, 6, "+CMGL:") == 0 && this->parse_index_ == 0) {
244  size_t start = 7;
245  size_t end = message.find(',', start);
246  uint8_t item = 0;
247  while (end != start) {
248  item++;
249  if (item == 1) { // Slot Index
250  this->parse_index_ = parse_number<uint8_t>(message.substr(start, end - start)).value_or(0);
251  }
252  // item 2 = STATUS, usually "REC UNREAD"
253  if (item == 3) { // recipient
254  // Add 1 and remove 2 from substring to get rid of "quotes"
255  this->sender_ = message.substr(start + 1, end - start - 2);
256  this->message_.clear();
257  break;
258  }
259  // item 4 = ""
260  // item 5 = Received timestamp
261  start = end + 1;
262  end = message.find(',', start);
263  }
264 
265  if (item < 2) {
266  ESP_LOGD(TAG, "Invalid message %d %s", this->state_, message.c_str());
267  return;
268  }
269  this->state_ = STATE_RECEIVE_SMS;
270  }
271  // Otherwise we receive another OK
272  if (ok) {
273  send_cmd_("AT+CLCC");
274  this->state_ = STATE_CHECK_CALL;
275  }
276  break;
277  case STATE_CHECK_CALL:
278  if (message.compare(0, 6, "+CLCC:") == 0 && this->parse_index_ == 0) {
279  this->expect_ack_ = true;
280  size_t start = 7;
281  size_t end = message.find(',', start);
282  uint8_t item = 0;
283  while (end != start) {
284  item++;
285  // item 1 call index for +CHLD
286  // item 2 dir 0 Mobile originated; 1 Mobile terminated
287  if (item == 3) { // stat
288  uint8_t current_call_state = parse_number<uint8_t>(message.substr(start, end - start)).value_or(6);
289  if (current_call_state != this->call_state_) {
290  ESP_LOGD(TAG, "Call state is now: %d", current_call_state);
291  if (current_call_state == 0)
292  this->call_connected_callback_.call();
293  }
294  this->call_state_ = current_call_state;
295  break;
296  }
297  // item 4 = ""
298  // item 5 = Received timestamp
299  start = end + 1;
300  end = message.find(',', start);
301  }
302 
303  if (item < 2) {
304  ESP_LOGD(TAG, "Invalid message %d %s", this->state_, message.c_str());
305  return;
306  }
307  } else if (ok) {
308  if (this->call_state_ != 6) {
309  // no call in progress
310  this->call_state_ = 6; // Disconnect
311  this->call_disconnected_callback_.call();
312  }
313  }
314  this->state_ = STATE_INIT;
315  break;
316  case STATE_RECEIVE_SMS:
317  /* Our recipient is set and the message body is in message
318  kick ESPHome callback now
319  */
320  if (ok || message.compare(0, 6, "+CMGL:") == 0) {
321  ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str());
322  ESP_LOGD(TAG, "%s", this->message_.c_str());
323  this->sms_received_callback_.call(this->message_, this->sender_);
324  this->state_ = STATE_RECEIVED_SMS;
325  } else {
326  if (this->message_.length() > 0)
327  this->message_ += "\n";
328  this->message_ += message;
329  }
330  break;
331  case STATE_RECEIVED_SMS:
332  case STATE_RECEIVED_USSD:
333  // Let the buffer flush. Next poll will request to delete the parsed index message.
334  break;
335  case STATE_SENDING_SMS_1:
336  this->send_cmd_("AT+CMGS=\"" + this->recipient_ + "\"");
337  this->state_ = STATE_SENDING_SMS_2;
338  break;
339  case STATE_SENDING_SMS_2:
340  if (message == ">") {
341  // Send sms body
342  ESP_LOGI(TAG, "Sending to %s message: '%s'", this->recipient_.c_str(), this->outgoing_message_.c_str());
343  this->write_str(this->outgoing_message_.c_str());
344  this->write(26);
345  this->state_ = STATE_SENDING_SMS_3;
346  } else {
347  set_registered_(false);
348  this->state_ = STATE_INIT;
349  this->send_cmd_("AT+CMEE=2");
350  this->write(26);
351  }
352  break;
353  case STATE_SENDING_SMS_3:
354  if (message.compare(0, 6, "+CMGS:") == 0) {
355  ESP_LOGD(TAG, "SMS Sent OK: %s", message.c_str());
356  this->send_pending_ = false;
357  this->state_ = STATE_CHECK_SMS;
358  this->expect_ack_ = true;
359  }
360  break;
361  case STATE_DIALING1:
362  this->send_cmd_("ATD" + this->recipient_ + ';');
363  this->state_ = STATE_DIALING2;
364  break;
365  case STATE_DIALING2:
366  if (ok) {
367  ESP_LOGI(TAG, "Dialing: '%s'", this->recipient_.c_str());
368  this->dial_pending_ = false;
369  } else {
370  this->set_registered_(false);
371  this->send_cmd_("AT+CMEE=2");
372  this->write(26);
373  }
374  this->state_ = STATE_INIT;
375  break;
376  case STATE_PARSE_CLIP:
377  if (message.compare(0, 6, "+CLIP:") == 0) {
378  std::string caller_id;
379  size_t start = 7;
380  size_t end = message.find(',', start);
381  uint8_t item = 0;
382  while (end != start) {
383  item++;
384  if (item == 1) { // Slot Index
385  // Add 1 and remove 2 from substring to get rid of "quotes"
386  caller_id = message.substr(start + 1, end - start - 2);
387  break;
388  }
389  // item 4 = ""
390  // item 5 = Received timestamp
391  start = end + 1;
392  end = message.find(',', start);
393  }
394  if (this->call_state_ != 4) {
395  this->call_state_ = 4;
396  ESP_LOGI(TAG, "Incoming call from %s", caller_id.c_str());
397  incoming_call_callback_.call(caller_id);
398  }
399  this->state_ = STATE_INIT;
400  }
401  break;
402  case STATE_ATA_SENT:
403  ESP_LOGI(TAG, "Call connected");
404  if (this->call_state_ != 0) {
405  this->call_state_ = 0;
406  this->call_connected_callback_.call();
407  }
408  this->state_ = STATE_INIT;
409  break;
410  default:
411  ESP_LOGW(TAG, "Unhandled: %s - %d", message.c_str(), this->state_);
412  break;
413  }
414 } // namespace sim800l
415 
417  // Read message
418  while (this->available()) {
419  uint8_t byte;
420  this->read_byte(&byte);
421 
423  this->read_pos_ = 0;
424 
425  ESP_LOGVV(TAG, "Buffer pos: %u %d", this->read_pos_, byte); // NOLINT
426 
427  if (byte == ASCII_CR)
428  continue;
429  if (byte >= 0x7F)
430  byte = '?'; // need to be valid utf8 string for log functions.
431  this->read_buffer_[this->read_pos_] = byte;
432 
433  if (this->state_ == STATE_SENDING_SMS_2 && this->read_pos_ == 0 && byte == '>')
434  this->read_buffer_[++this->read_pos_] = ASCII_LF;
435 
436  if (this->read_buffer_[this->read_pos_] == ASCII_LF) {
437  this->read_buffer_[this->read_pos_] = 0;
438  this->read_pos_ = 0;
439  this->parse_cmd_(this->read_buffer_);
440  } else {
441  this->read_pos_++;
442  }
443  }
444  if (state_ == STATE_INIT && this->registered_ &&
445  (this->call_state_ != 6 // A call is in progress
446  || this->send_pending_ || this->dial_pending_ || this->connect_pending_ || this->disconnect_pending_)) {
447  this->update();
448  }
449 }
450 
451 void Sim800LComponent::send_sms(const std::string &recipient, const std::string &message) {
452  this->recipient_ = recipient;
453  this->outgoing_message_ = message;
454  this->send_pending_ = true;
455 }
456 
457 void Sim800LComponent::send_ussd(const std::string &ussd_code) {
458  ESP_LOGD(TAG, "Sending USSD code: %s", ussd_code.c_str());
459  this->ussd_ = ussd_code;
460  this->send_ussd_pending_ = true;
461  this->update();
462 }
464  ESP_LOGCONFIG(TAG, "SIM800L:");
465 #ifdef USE_BINARY_SENSOR
466  LOG_BINARY_SENSOR(" ", "Registered", this->registered_binary_sensor_);
467 #endif
468 #ifdef USE_SENSOR
469  LOG_SENSOR(" ", "Rssi", this->rssi_sensor_);
470 #endif
471 }
472 void Sim800LComponent::dial(const std::string &recipient) {
473  this->recipient_ = recipient;
474  this->dial_pending_ = true;
475 }
478 
479 void Sim800LComponent::set_registered_(bool registered) {
480  this->registered_ = registered;
481 #ifdef USE_BINARY_SENSOR
482  if (this->registered_binary_sensor_ != nullptr)
483  this->registered_binary_sensor_->publish_state(registered);
484 #endif
485 }
486 
487 } // namespace sim800l
488 } // namespace esphome
void write_str(const char *str)
Definition: uart.h:27
CallbackManager< void(std::string)> ussd_received_callback_
Definition: sim800l.h:121
CallbackManager< void(std::string, std::string)> sms_received_callback_
Definition: sim800l.h:117
void write_byte(uint8_t data)
Definition: uart.h:19
void send_cmd_(const std::string &message)
Definition: sim800l.cpp:61
void send_sms(const std::string &recipient, const std::string &message)
Definition: sim800l.cpp:451
CallbackManager< void()> call_disconnected_callback_
Definition: sim800l.h:120
const char ASCII_CR
Definition: sim800l.cpp:10
void update() override
Retrieve the latest sensor values. This operation takes approximately 16ms.
Definition: sim800l.cpp:13
bool read_byte(uint8_t *data)
Definition: uart.h:29
void parse_cmd_(std::string message)
Definition: sim800l.cpp:69
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:72
char read_buffer_[SIM800L_READ_BUFFER_LENGTH]
Definition: sim800l.h:99
void publish_state(bool state)
Publish a new state to the front-end.
binary_sensor::BinarySensor * registered_binary_sensor_
Definition: sim800l.h:91
void send_ussd(const std::string &ussd_code)
Definition: sim800l.cpp:457
sensor::Sensor * rssi_sensor_
Definition: sim800l.h:95
CallbackManager< void(std::string)> incoming_call_callback_
Definition: sim800l.h:118
void dial(const std::string &recipient)
Definition: sim800l.cpp:472
CallbackManager< void()> call_connected_callback_
Definition: sim800l.h:119
void set_registered_(bool registered)
Definition: sim800l.cpp:479
Definition: a4988.cpp:4
const uint16_t SIM800L_READ_BUFFER_LENGTH
Definition: sim800l.h:19
size_t write(uint8_t data)
Definition: uart.h:52
const char ASCII_LF
Definition: sim800l.cpp:11