Tworzenie Bramki SMS za pomocą ESP32-PoE i SIM800L

Projekt bramki SMS, który umożliwia wysyłanie i odbieranie wiadomości za pomocą modułu ESP32-PoE i SIM800L, z naciskiem na niezawodność i odporność na awarie.

Wysyłanie i Odbieranie SMS

ESP32 wykorzystuje moduł SIM800L, który jest regularnie sprawdzany, aby upewnić się, że jest zawsze gotowy do wysyłania i odbierania wiadomości. Dzięki temu możemy mieć pewność, że każda wiadomość zostanie prawidłowo obsłużona.

Moduł SIM800L jest podłączony do ESP32-PoE za pomocą komunikacji TX/RX, co umożliwia płynne przesyłanie danych między nimi.

Zasilanie ESP32-PoE

uwaga ESP32-PoE może być zasilany zarówno poprzez PoE, jak i przez USB. Ważne jest jednak, aby pamiętać, że jednoczesne podłączenie zasilania PoE i USB może spowodować uszkodzenie układu. Dzieje się tak, ponieważ ESP32-PoE nie posiada galwanicznej izolacji między zasilaniem PoE a USB.

Podłączenie modułów


SIM800L TX do ESP32 RX (GPIO16)
SIM800L RX do ESP32 TX (GPIO4)
Zasilanie: Upewnij się, że moduł SIM800L jest zasilany odpowiednim napięciem (3.7V – 4.2V) Zasilanie z ESP32 3.3V jest za małe. W moim projekcie użyłem regulowany stabilizator napięcia LM2595S

Dokumentacje

W trakcie budowania projektu korzystałem z następujących dokumetacji:

Przykłady komend AT

AT+CSQ -  Sprawdzenie siły sygnału 
AT+COPS=0 - automatyczny wybór operatorów
AT+CFUN=1,1   - restart modułu 
AT+CBAND? - sprawdzenie aktualnego zakresu częstotliwości 
AT+CBAND="DCS_MODE" - ustawienie zakresu częstotliwości DCS
AT+CBAND="ALL_BAND" - wszystkie częstotliwości 

Przykłady zakresów częstotliwości:

GSM 850: AT+CBAND="GSM850"
E-GSM 900: AT+CBAND="EGSM_MODE"
DCS 1800: AT+CBAND="DCS_MODE"
PCS 1900: AT+CBAND="PCS_MODE"

Wszystkie zakresy: AT+CBAND="ALL_BAND"

Kod projektu

main.cpp – główny plik projektu

#include <Arduino.h>
#include "Network.h"
#include "HTTPServer.h"
#include "SIM800L.h"

void setup() {
Serial.begin(115200);
sim800lSetup();

setupEthernet();
delay(3000);

if (!ETH.linkUp()) {
setupWiFi();
}

setupServer();
}

void loop() {
server.handleClient();
monitorConnection();
delay(10000);
}

SIM800L.h SIM800L.cpp – pliki do obsługi modułu SIM800L.

#include "SIM800L.h"

const char SIM_PIN[] = "1111";
HardwareSerial sim800l(2);
bool isConnected = false;

std::vector<String> receivedSMS; // Zmienna globalna do przechowywania odebranych SMS-ów


void sim800lSetup() {
sim800l.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);
sendATCommand("AT", 2000);
sendATCommand("AT+CPIN?", 2000);
sendATCommand(String("AT+CPIN=") + SIM_PIN, 2000);
configureBand();
resetAndConfigureModem();
setupGPRS();
checkNetworkConnection();
}

String sendATCommand(String command, int timeout) {
sim800l.println(command);
long int time = millis();
String response = "";

while ((millis() - time) < timeout) {
while (sim800l.available()) {
char c = sim800l.read();
response += c;
}
}
Serial.println("Odpowiedź na komendę " + command + ": " + response);
return response;
}

bool isRegistered(String response) {
if (response.indexOf("+CREG: 0,1") >= 0 || response.indexOf("+CREG: 0,5") >= 0) {
return true;
}
return false;
}

// Check network z uzyciem isRegistered
void checkNetworkConnection() {
String response = sendATCommand("AT+CREG?", 5000);
delay(3000);

Serial.println("Odpowiedź CREG: " + response);

if (isRegistered(response)) {
Serial.println("Zarejestrowano w sieci.");
isConnected = true;
} else {
Serial.println("Brak rejestracji w sieci. Wybieranie operatora...");
selectOperator();
checkNetworkConnection();
}
}


void checkSignalStrength() {
sendATCommand("AT+CSQ", 2000);
}

void checkSIMStatus() {
sendATCommand("AT+CPIN?", 2000);
}

void restartModem() {
sendATCommand("AT+CFUN=1,1", 5000);
delay(10000);
}

void resetAndConfigureModem() {
sendATCommand("AT+CFUN=1,1", 10000);
delay(10000);
sendATCommand("AT+CNMP=2", 2000);
sendATCommand("AT+CMNB=1", 2000);
}

void selectOperator() {
sendATCommand("AT+COPS=?", 2000);
sendATCommand("AT+COPS=1,2,\"26003\"", 2000);
delay(10000);
}

void configureBand() {
sendATCommand("AT+CBAND?", 2000);
sendATCommand("AT+CBAND=\"ALL_BAND\"", 2000);
}

void setupGPRS() {
sendATCommand("AT+CGATT=1", 10000);
configureAPN();
}

void configureAPN() {
sendATCommand("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"", 5000);
sendATCommand("AT+SAPBR=3,1,\"APN\",\"internet\"", 5000);
sendATCommand("AT+SAPBR=1,1", 5000);
sendATCommand("AT+SAPBR=2,1", 5000);
}

void sendToZabbix(String url) {
sendATCommand("AT+HTTPINIT", 5000);
sendATCommand("AT+HTTPPARA=\"CID\",1", 5000);
sendATCommand("AT+HTTPPARA=\"URL\",\"" + url + "\"", 5000);
sendATCommand("AT+HTTPACTION=0", 10000);
sendATCommand("AT+HTTPTERM", 5000);
}


void checkSMS() {
sendATCommand("AT+CMGF=1", 2000);
sendATCommand("AT+CPMS=\"SM\",\"SM\",\"SM\"", 2000);
String response = sendATCommand("AT+CMGL=\"REC UNREAD\"", 5000);

// Logowanie odpowiedzi
Serial.println("Odpowiedź AT+CMGL: " + response);

if (response.indexOf("+CMGL") >= 0) {
// Przechowywanie odebranych SMS-ów
receivedSMS.push_back(response);
} else {
Serial.println("Brak nowych SMS-ów.");
}

// Przykładowa analiza odebranych SMS
while (sim800l.available()) {
String smsContent = "";
while (sim800l.available()) {
char c = sim800l.read();
smsContent += c;
}

String senderNumber;
parseSenderNumber(smsContent, senderNumber);

}
}


void parseSenderNumber(String &smsContent, String &senderNumber) {
int index = smsContent.indexOf("+CMGR:");
if (index >= 0) {
int start = smsContent.indexOf("\"", index + 1) + 1;
int end = smsContent.indexOf("\"", start);
senderNumber = smsContent.substring(start, end);
}
}


bool sendSMS(String number, String message) {
sendATCommand("AT+CMGS=\"" + number + "\"", 2000);
sim800l.print(message);
sim800l.write(26);

long int time = millis();
String response = "";
while ((millis() - time) < 10000) {
while (sim800l.available()) {
char c = sim800l.read();
response += c;
}
}
Serial.println("Odpowiedź: " + response);

if (response.indexOf("OK") != -1) {
return true;
} else {
return false;
}
}

void monitorConnection() {
checkNetworkConnection();
checkSignalStrength();
checkSIMStatus();

if (!isConnected) {
setupGPRS();
}
}
#ifndef SIM800L_H
#define SIM800L_H

#include <HardwareSerial.h>
#include <vector>


extern const char SIM_PIN[];
extern HardwareSerial sim800l;
extern bool isConnected;

extern std::vector<String> receivedSMS;

#define RX_PIN 16
#define TX_PIN 4

void sim800lSetup();
String sendATCommand(String command, int timeout);
bool isRegistered(String response);
void checkNetworkConnection();
void checkSignalStrength();
void checkSIMStatus();
void restartModem();
void configureNetworkMode();
void resetAndConfigureModem();
void selectOperator();
void configureBand();
void setupGPRS();
void configureAPN();
void sendToZabbix(String url);
void checkSMS();
// void processSMS(String smsContent, String senderNumber);
bool sendSMS(String number, String message);
void monitorConnection();
void parseSenderNumber(String &smsContent, String &senderNumber);

#endif

Network.h i Network.cpp – obsługa sieci ETH / WIFI

#include "Network.h"

const char* ssid = "your_ssid_wifi";
const char* password = "your_pass_wifi";

void WiFiEvent(WiFiEvent_t event) {
switch (event) {
case SYSTEM_EVENT_ETH_CONNECTED:
Serial.println("ETH Connected");
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
Serial.println("ETH Disconnected");
break;
case SYSTEM_EVENT_ETH_START:
Serial.println("ETH Started");
ETH.setHostname("esp32-ethernet");
break;
case SYSTEM_EVENT_ETH_GOT_IP:
Serial.print("ETH IP: ");
Serial.println(ETH.localIP());
break;
case SYSTEM_EVENT_ETH_STOP:
Serial.println("ETH Stopped");
break;
default:
break;
}
}

void setupEthernet() {
WiFi.onEvent(WiFiEvent);
ETH.begin();
}

void setupWiFi() {
Serial.println("Connecting to Wi-Fi...");
WiFi.begin(ssid, password);

int maxAttempts = 20;
int attempt = 0;
while (WiFi.status() != WL_CONNECTED && attempt < maxAttempts) {
delay(500);
Serial.print(".");
attempt++;
}

if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.print("WiFi connected. IP address: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("Failed to connect to Wi-Fi");
}
}
#ifndef NETWORK_H
#define NETWORK_H

#include <ETH.h>
#include <WiFi.h>

void WiFiEvent(WiFiEvent_t event);
void setupEthernet();
void setupWiFi();

#endif

HTTPServer.h HTTPServer.cpp – serwer http do obsługi POST / GET

#include "HTTPServer.h"
#include "SIM800L.h"

WebServer server(80);

void handleSendSMS() {
if (server.hasArg("number") && server.hasArg("message")) {
String number = server.arg("number");
String message = server.arg("message");
bool status = sendSMS(number, message);
if (status) {
server.send(200, "application/json", "{\"statusSend\": true}");
} else {
server.send(200, "application/json", "{\"statusSend\": false}");
}
} else {
server.send(400, "application/json", "{\"error\": \"Missing number or message argument\"}");
}
}

void handleCheckSMS() {
String response = "List of received SMS:\n";
for (const auto& sms : receivedSMS) {
response += sms + "\n";
}
server.send(200, "text/plain", response);
}

void setupServer() {
server.on("/sendSMS", HTTP_POST, handleSendSMS);
server.on("/checkSMS", HTTP_GET, handleCheckSMS);
server.begin();
Serial.println("HTTP server started");
}
#ifndef HTTPSERVER_H
#define HTTPSERVER_H

#include <WebServer.h>

extern WebServer server;

void handleSendSMS();
void handleCheckSMS();
void setupServer();

#endif

Wysyłanie SMS przez HTTP POST

Aby wysłać SMS, możemy wykonać żądanie HTTP POST na endpoint /sendSMS z parametrami number i message.

Przykładowe żądanie:

POST /sendSMS HTTP/1.1
Host: <IP_ESP32>
Content-Type: application/json

{
"number": "+48123456789",
"message": "Testowa wiadomość"
}

Sprawdzanie Odebranych SMS

Aby sprawdzić odebrane SMS-y, możemy wykonać żądanie HTTP GET na endpoint /checkSMS.

Przykładowe żądanie:

GET /checkSMS HTTP/1.1
Host: <IP_ESP32>

Pełen kod github możesz przejrzeć tutaj: