Files
esphome-dev/esphome/components/sim800l/sim800l.cpp
T
Guillermo Ruffino fc465d6d93 SMS Sender / Receiver (#522)
* add sim800l

* Increse SoftwareSerial Buffer Size

* use auto id on action

* lint

* lint

* add to test3.yaml

* lint


Co-authored-by: Guillermo Ruffino <guillermo.ruffino@pampatech.net>
2019-06-17 20:13:52 +02:00

260 lines
8.0 KiB
C++

#include "sim800l.h"
#include "esphome/core/log.h"
#include <string.h>
namespace esphome {
namespace sim800l {
static const char* TAG = "sim800l";
const char ASCII_CR = 0x0D;
const char ASCII_LF = 0x0A;
void Sim800LComponent::update() {
if (this->watch_dog_++ == 2) {
this->state_ = STATE_INIT;
this->write(26);
}
if (state_ == STATE_INIT) {
if (this->registered_ && this->send_pending_) {
this->send_cmd_("AT+CSCS=\"GSM\"");
this->state_ = STATE_SENDINGSMS1;
} else {
this->send_cmd_("AT");
this->state_ = STATE_CHECK_AT;
}
this->expect_ack_ = true;
}
if (state_ == STATE_RECEIVEDSMS) {
// Serial Buffer should have flushed.
// Send cmd to delete received sms
char delete_cmd[20];
sprintf(delete_cmd, "AT+CMGD=%d", this->parse_index_);
this->send_cmd_(delete_cmd);
this->state_ = STATE_CHECK_SMS;
this->expect_ack_ = true;
}
}
void Sim800LComponent::send_cmd_(std::string message) {
ESP_LOGV(TAG, "S: %s - %d", message.c_str(), this->state_);
this->watch_dog_ = 0;
this->write_str(message.c_str());
this->write_byte(ASCII_LF);
}
void Sim800LComponent::parse_cmd_(std::string message) {
ESP_LOGV(TAG, "R: %s - %d", message.c_str(), this->state_);
if (message.empty())
return;
if (this->expect_ack_) {
bool ok = message == "OK";
this->expect_ack_ = false;
if (!ok) {
if (this->state_ == STATE_CHECK_AT && message == "AT") {
// Expected ack but AT echo received
this->state_ = STATE_DISABLE_ECHO;
this->expect_ack_ = true;
} else {
ESP_LOGW(TAG, "Not ack. %d %s", this->state_, message.c_str());
this->state_ = STATE_IDLE; // Let it timeout
return;
}
}
}
switch (this->state_) {
case STATE_INIT:
if (message.compare(0, 6, "+CMTI:") == 0) {
// While we were waiting for update to check for messages, this notifies a message
// is available. Grab it quickly
this->state_ = STATE_CHECK_SMS;
}
break;
case STATE_DISABLE_ECHO:
send_cmd_("ATE0");
this->state_ = STATE_CHECK_AT;
this->expect_ack_ = true;
break;
case STATE_CHECK_AT:
send_cmd_("AT+CMGF=1");
this->state_ = STATE_CREG;
this->expect_ack_ = true;
break;
case STATE_CREG:
send_cmd_("AT+CREG?");
this->state_ = STATE_CREGWAIT;
break;
case STATE_CREGWAIT: {
// Response: "+CREG: 0,1" -- the one there means registered ok
// "+CREG: -,-" means not registered ok
bool registered = message.compare(0, 6, "+CREG:") == 0 && message[9] == '1';
if (registered) {
if (!this->registered_)
ESP_LOGD(TAG, "Registered OK");
send_cmd_("AT+CSQ");
this->state_ = STATE_CSQ;
this->expect_ack_ = true;
} else {
ESP_LOGW(TAG, "Registration Fail");
if (message[7] == '0') { // Network registration is disable, enable it
send_cmd_("AT+CREG=1");
this->expect_ack_ = true;
this->state_ = STATE_CHECK_AT;
} else {
// Keep waiting registration
this->state_ = STATE_CREG;
}
}
this->registered_ = registered;
break;
}
case STATE_CSQ:
this->state_ = STATE_CSQ_RESPONSE;
break;
case STATE_CSQ_RESPONSE:
if (message.compare(0, 5, "+CSQ:") == 0) {
size_t comma = message.find(',', 6);
if (comma != 6) {
this->rssi_ = strtol(message.substr(6, comma - 6).c_str(), nullptr, 10);
ESP_LOGD(TAG, "RSSI: %d", this->rssi_);
}
}
this->state_ = STATE_CHECK_SMS;
break;
case STATE_PARSE_SMS:
this->state_ = STATE_PARSE_SMS_RESPONSE;
break;
case STATE_PARSE_SMS_RESPONSE:
if (message.compare(0, 6, "+CMGL:") == 0 && this->parse_index_ == 0) {
size_t start = 7;
size_t end = message.find(',', start);
uint8_t item = 0;
while (end != start) {
item++;
if (item == 1) { // Slot Index
this->parse_index_ = strtol(message.substr(start, end - start).c_str(), nullptr, 10);
}
// item 2 = STATUS, usually "REC UNERAD"
if (item == 3) { // recipient
// Add 1 and remove 2 from substring to get rid of "quotes"
this->sender_ = message.substr(start + 1, end - start - 2);
break;
}
// item 4 = ""
// item 5 = Received timestamp
start = end + 1;
end = message.find(',', start);
}
if (item < 2) {
ESP_LOGD(TAG, "Invalid message %d %s", this->state_, message.c_str());
return;
}
this->state_ = STATE_RECEIVESMS;
}
// Otherwise we receive another OK, we do nothing just wait polling to continuously check for SMS
if (message == "OK")
this->state_ = STATE_INIT;
break;
case STATE_RECEIVESMS:
/* Our recipient is set and the message body is in message
kick ESPHome callback now
*/
ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str());
ESP_LOGD(TAG, "%s", message.c_str());
this->callback_.call(message, this->sender_);
/* If the message is multiline, next lines will contain message data.
If there were other messages in the list, next line will be +CMGL: ...
At the end of the list the new line and the OK should be received.
To keep this simple just first line of message if considered, then
the next state will swallow all received data and in next poll event
this message index is marked for deletion.
*/
this->state_ = STATE_RECEIVEDSMS;
break;
case STATE_RECEIVEDSMS:
// Let the buffer flush. Next poll will request to delete the parsed index message.
break;
case STATE_SENDINGSMS1:
this->send_cmd_("AT+CMGS=\"" + this->recipient_ + "\"");
this->state_ = STATE_SENDINGSMS2;
break;
case STATE_SENDINGSMS2:
if (message == ">") {
// Send sms body
ESP_LOGD(TAG, "Sending message: '%s'", this->outgoing_message_.c_str());
this->write_str(this->outgoing_message_.c_str());
this->write(26);
this->state_ = STATE_SENDINGSMS3;
} else {
this->registered_ = false;
this->state_ = STATE_INIT;
this->send_cmd_("AT+CMEE=2");
this->write(26);
}
break;
case STATE_SENDINGSMS3:
if (message.compare(0, 6, "+CMGS:") == 0) {
ESP_LOGD(TAG, "SMS Sent OK: %s", message.c_str());
this->send_pending_ = false;
this->state_ = STATE_CHECK_SMS;
this->expect_ack_ = true;
}
break;
default:
ESP_LOGD(TAG, "Unhandled: %s - %d", message.c_str(), this->state_);
break;
}
if (this->state_ == STATE_CHECK_SMS) {
send_cmd_("AT+CMGL=\"ALL\"");
this->state_ = STATE_PARSE_SMS;
this->parse_index_ = 0;
this->expect_ack_ = true;
}
}
void Sim800LComponent::loop() {
// Read message
while (this->available()) {
uint8_t byte;
this->read_byte(&byte);
if (this->read_pos_ == SIM800L_READ_BUFFER_LENGTH)
this->read_pos_ = 0;
ESP_LOGVV(TAG, "Buffer pos: %u %d", this->read_pos_, byte); // NOLINT
if (byte == ASCII_CR)
continue;
if (byte >= 0x7F)
byte = '?'; // need to be valid utf8 string for log functions.
this->read_buffer_[this->read_pos_] = byte;
if (this->state_ == STATE_SENDINGSMS2 && this->read_pos_ == 0 && byte == '>')
this->read_buffer_[++this->read_pos_] = ASCII_LF;
if (this->read_buffer_[this->read_pos_] == ASCII_LF) {
this->read_buffer_[this->read_pos_] = 0;
this->read_pos_ = 0;
this->parse_cmd_(this->read_buffer_);
} else {
this->read_pos_++;
}
}
}
void Sim800LComponent::send_sms(std::string recipient, std::string message) {
ESP_LOGD(TAG, "Sending to %s: %s", recipient.c_str(), message.c_str());
this->recipient_ = recipient;
this->outgoing_message_ = message;
this->send_pending_ = true;
this->update();
}
} // namespace sim800l
} // namespace esphome