Понадобилось мне слать SMS из ардуинины себе на телефон. Да так, что бы без заморочек с GSM, сим картами и оплатой. Под катом то, что из этого вышло.
Раз GSM не подходит, то слать придется через интернет. В закромах имелся сверх дешевый Wi-Fi модуль ESP8266. Про его предварительную настройку можно почитать в этой отличной статье ссылка.
Перво-наперво полез гуглить различные интернет-сервисы для отправки SMS. Сервисов оказалось много, но вот подходящего среди них не оказалось. Либо 10 SMS в день, либо хотят денег. Да и капчи ардуинине не по зубам. Но тут совершенно случайно наткнулся на на интересный сервис своего мобильного оператора (MTS BY) — всего за 1$ можно подключить услугу приема email в виде SMS ссылка. Что же, полностью бесплатно не получилось, и после оплаты я получил почтовый адрес вида 375XXYYYYYYY@sms.mts.by ( где XX и YYYYYYY — код сети и номер). То есть все свелось к банальной отправке email из arduino.
Но не тут-то было. Оказывается в последнее время все уважающие себя почтовые сервера отказываются принимать почту по 25 порту и без шифрования. А с другими связываться не хотелось, как и привязываться к своему домашнему серверу. Нагуглил вот такой сервис ссылка. В базовом бесплатном варианте дают 6к писем в месяц, что для моего домашнего использования хватает с головой. После регистрации идем в настройки и видим адрес smtp сервера, порт, логин и сгенерированный нам пароль.
Быстро накатал скетч, отправляю email 375XXYYYYYYY@sms.mts.by и… Облом. Отправляю на обычную почту — приходит. Пытался так и эдак переделывать заголовок — все равно письма исчезают в безднах серверов МТС, явно попадая в котел спам фильтра.
Значит нужно «отмыть» почту. В качестве сервиса отмывки выбрал (тихо ненавидимый мною за рекламу) mail.ru. Для отмывки в настройках почты, в разделе «Фильтры и пересылка», создал новое правило:
Настройки
После создания фильтра его понадобилось активировать введя код, пришедший в виде email-SMS на телефон.Вот и момент триумфа — отправляю email из ардуинины, и через минуту телефон раздается радостным сигналом оповещения, при этом пугая проходящего рядом кота.
Далее скучные технические подробности.
В качестве платы взял Arduino Mega 2560, так как в нем целых три дополнительных последовательных порта (хотя можно и обычный UNO, только дебажить будет сложнее).
ESP8266 подключен: GND -> GND, VCC и CH_PD -> +3.3V, RX -> TX3, TX -> RX3. Скорость ESP8266 настроена на 115200 бод.
Для общения с smtp сервером нужно закодировать свои логин и пароль в Base64.
Можно воспользоваться консолью Linux:
openssl enc -base64 <<< 'email@gmail.com'
openssl enc -base64 <<< 'password'
Или же каким-либо онлайн сервисом, например ссылка.
Ну и собственно код скетча. После включения отправляется СМС и код уходит в вечный цикл. Дополнительные библиотеки не использовал.
Код
#define SSID "wi-fi_login" // ваш SSID
#define PASS "wi-fi_password" // ваш пароль Wi-Fi
#define SMTPServer "s02.atomsmtp.com" //smtp сервер
#define SMTPPort "2525" // smtp порт
#define MailLogin "smtp_example@gmail.com" // логин для smtp
#define MailLoginBase64 "dWd1LCBrb25lNG5vCg==" //логин для smtp в Base64
#define MailPasswordBase64 "aHJlbiB0YW0K" // пароль для smtp в Base64
#define MailRelay "example@mail.ru" // промежуточная почта для "отмывания" email
#define PhoneNumber "375290000000" // номер телефона
#define Message "Hello from Arduino!" //сообщение
#define SERIAL_RX_BUFFER_SIZE 256
#define SERIAL_TX_BUFFER_SIZE 256
void setup()
{
delay(2000);
Serial3.begin(115200);
Serial3.setTimeout(5000);
Serial.begin(115200); // для отладки
Serial.println("Init");
Serial3.println("AT+RST"); // сброс и проверка, что модуль готов
if(WaiteString("Ready", 5000)) {
while(Serial3.available()) { Serial3.read();}
Serial.println("WiFi - Module is ready");
}else{
Serial.println("Module dosn't respond.");
while(1);
}
delay(100);
Serial3.println(" AT+CIPMODE=0");
WaiteString("OK");
while(Serial3.available()) { Serial3.read();}
Serial3.println("AT+CIPMUX=1");
WaiteString("OK");
while(Serial3.available()) { Serial3.read();}
// try to connect to wifi
boolean connected = false;
for(int i=0;i<5;i++) {
if(connectWiFi()) {
connected = true;
break;
}
}
if (!connected) {
while(1);
}
}
void loop()
{
String cmd = "AT+CIPSTART=0,\"TCP\",\"";
cmd += String(SMTPServer);
cmd += "\"," + String(SMTPPort);
Serial3.println(cmd);
if(WaiteString("Linked", 5000)) {
while(Serial3.available()) { Serial3.read();}
Serial.println("Link");
}
else {
Serial.println("Link fail");
while (1);
}
if (WaiteString("OK", 2000)) {
while(Serial3.available()) { Serial3.read();}
}
else {
while (1);
}
Send("HELO 1.2.3.4", true);
Send("AUTH LOGIN", true);
Send(MailLoginBase64, true);
Send(MailPasswordBase64, true);
Send("MAIL FROM:<" + String(MailLogin) + ">", true);
Send("RCPT TO:<" + String(MailRelay) + ">", true);
Send("DATA", true);
Send("Subject:SMS", false);
Send("To:\"" + String(PhoneNumber) + "\" <" + String(PhoneNumber) + "@sms.mts.by>", false);
Send("From: <" + String(MailLogin) + ">", false);
Send("", false);
Send(Message, false);
Send(".", true);
Send("QUIT", true);
while(1) {};
}
boolean connectWiFi()
{
Serial3.println("AT+CWMODE=1");
while (!Serial3.available()) { delay(10);}
while (Serial3.available()) {Serial3.read();}
String cmd="AT+CWJAP=\"";
cmd+=SSID;
cmd+="\",\"";
cmd+=PASS;
cmd+="\"";
Serial3.println(cmd);
if(WaiteString("OK", 8000)){
Serial.println("Connected to WiFi.");
return true;
}else{
Serial.println("Can not connect to the WiFi.");
return false;
}
}
bool Send(String S, bool wait) {
Serial3.print("AT+CIPSEND=0,");
Serial3.println(S.length()+2);
while (!Serial3.available()) { delay(10);}
if(Serial3.find(">")){
}else{
Serial3.println("AT+CIPCLOSE=0");
delay(1000);
return false;
}
Serial3.print(S + "\r\n");//добаяляем перевод строки
if (WaitString("OK", 15000)) {
if (wait) {
WaitString("+IPD", 15000);
while(Serial3.available()) {
Serial3.read();}}
return true;}
else {
return false;}
}
void WaiteString(String S) {
int L = S.length();
String T = String(" ");
while(1) {
if (Serial3.available()) {
char c = Serial3.read();
T = T + String(c);
if (T.length() > L) T = T.substring(1);
if (S.charAt(0) == T.charAt(0))
if (S.compareTo(T) == 0) return;
}
else {
delay(1);
}
}
}
bool WaiteString(String S, int Time) {
int L = S.length();
String T = String(" ");
while(Time>0) {
if (Serial3.available()) {
char c = Serial3.read();
T = T + String(c);
if (T.length() > L) T = T.substring(1);
if (S.charAt(0) == T.charAt(0))
if (S.compareTo(T) == 0) return true;
}
else {
delay(1);
Time--;
}
}
return false;
}
String WaiteString(int Time) {
String T = String("");
while(Time>0) {
if (Serial3.available()) {
char c = Serial3.read();
T = T + String(c);
}
else {
delay(1);
Time--;
}
}
return T;
}