В Интернет довольно много библиотек для работы с SIM800L с Arduino, однако при использовании их под ESP32/ESP8266 некоторые из них не компилируются, иные не поддерживают HardwareSerial, работая только через SoftwareSerial.
Я потратил немало времени на поиск стабильно работающего варианта. Пришлось поправить исходники нескольких библиотек. Правки залил на GitHub. Надеюсь, после CI вскоре эти фиксы будут имплементированы в основной код библиотек.
Подробно по подключению модуля SIM800L к ESP32 рассмотрено в предыдущей статье. При тестировании использовались модули:
- SIM800L — оптимально приобретать Aliexpress.
- MH-ET Live ESP32 Devkit 38 pins — также на Aliexpres.
- DC-DC step down converter. В схеме использовался недорогой модуль Mini-360 за 0,3 $.
Библиотека TinyGSM
Из всего многообразия выбора мне приглянулась библиотека TinyGSM. До моих правок библиотека не компилировалась на ESP32 и ESP8266 выдавая сообщение об ошибке: cannot declare variable ‘client’ to be of abstract type ‘TinyGsmSim800::GsmClient’. Если поправить код, то компиляция проходит нормально.
Однако остается другая проблема. При передаче POST запроса под ESP32/ESP8266 используется штатная библиотека HTTPClient. Она при инициализации для отправки данных через GSM модем должна как-то принять Client (отнаследован от класса Stream) от него. Но передать GSMClient нельзя, поскольку библиотека может принимать только WiFiClient. И это проблема, поскольку библиотеку HTTPClient нельзя использовать не вписав вариант работы через GSMClient.
Однако, выход есть — использовать библиотеку ArduinoHttpClient. Она не компилируется под ESP32/ESP8266, однако я внес на GitHub нужные правки, так что, надеюсь, они будут приняты и станут доступны для всех страждущих. На всякий случай код, который нужно вписать в class HttpClient такой:
#ifdef ESP32 || ESP8266
virtual int connect(IPAddress ip, uint16_t port, int timeout)
{
throw "Method [virtual int connect(IPAddress ip, uint16_t port, int timeout)] has not implemented yet in HttpClient.h";
}
virtual int connect(const char *host, uint16_t port, int timeout)
{
throw "Method [virtual int connect(const char *host, uint16_t port, int timeout)] has not implemented yet in HttpClient.h";
}
#endifПосле того как правки внесены в обе библиотеки они нормально компилируются и стабильно работают с SIM800L передавая телеметрию.
Пример кода
Прмер кода я залил на GitHub. Возможно, к моменту прочтения статьи он станет доступным в Examples\HttpClient_ESP32. На всякий случай дублирую здесь.
#include <ArduinoHttpClient.h>
#define TINY_GSM_MODEM_SIM800
#include <TinyGsmClient.h>
#define RXD2 16
#define TXD2 17
#define APN_NAME "internet.beeline.ru"
#define APN_USER "beeline"
#define APN_PSWD "beeline"
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 38400
#define Max_Modem_Reboots 5
// set your cad pin (optional)
#define SIM_PIN ""
TinyGsm modemGSM(Serial2);
TinyGsmClient clientGSM(modemGSM);
String server = "andrey.fedorov.ru";
const int port = 80;
int Modem_Reboots_Counter = 0;
HttpClient* http;
void RestartGSMModem()
{
Serial.println("Restarting GSM...");
if (!modemGSM.restart())
{
Serial.println("\tFailed. :-(\r\n");
//ESP.restart();
}
if (Modem_Reboots_Counter < Max_Modem_Reboots)
{
Init_GSM_SIM800();
}
Modem_Reboots_Counter++;
}
String GSMSignalLevel(int level)
{
switch (level)
{
case 0:
return "-115 dBm or less";
case 1:
return "-111 dBm";
case 31:
return "-52 dBm or greater";
case 99:
return "not known or not detectable";
default:
if (level > 1 && level < 31)
return "-110... -54 dBm";
}
return "Unknown";
}
String GSMRegistrationStatus(RegStatus state)
{
switch (state)
{
case REG_UNREGISTERED:
return "Not registered, MT is not currently searching a new operator to register to";
case REG_SEARCHING:
return "Not registered, but MT is currently searching a new operator to register to";
case REG_DENIED:
return "Registration denied";
case REG_OK_HOME:
return "Registered, home network";
case REG_OK_ROAMING:
return "Registered, roaming";
case REG_UNKNOWN:
return "Unknown";
}
return "Unknown";
}
String SIMStatus(SimStatus state)
{
switch (state)
{
case SIM_ERROR:
return "SIM card ERROR";
case SIM_READY:
return "SIM card is READY";
case SIM_LOCKED:
return "SIM card is LOCKED. PIN/PUK needed.";
}
return "Unknown STATUS";
}
String SwapLocation(String location)
{
int i = location.indexOf(',');
int j = location.indexOf(',', i+1);
String longitude = location.substring(i+1, j);
i = location.indexOf(',', j+1);
String latitude = location.substring(j+1, i);
return latitude + "," + longitude;
}
bool SendTextByPOST(String server, String url, String postData)
{
bool stateGPRS = modemGSM.isGprsConnected();
bool res = false;
if (stateGPRS)
{
http = new HttpClient(clientGSM, server, port);
Serial.println("Send POST request...");
http->beginRequest();
http->post(url);
http->sendHeader("Content-Type", "application/x-www-form-urlencoded");
http->sendHeader("Content-Length", postData.length());
//http.sendHeader("X-Custom-Header", "custom-header-value");
http->beginBody();
http->print(postData);
http->endRequest();
// read the status code and body of the response
int statusCode = http->responseStatusCode();
if (statusCode == t_http_codes::HTTP_CODE_OK)
{
//String response = http->responseBody();
Serial.println("Status code: " + String(statusCode));
//Serial.println("Response: " + response);
res = false;
}
else
{
Serial.println("Error code: " + String(statusCode));
res = true;
}
http->stop();
}
return res;
}
void Init_GSM_SIM800()
{
Serial.println("Initialize GSM modem...");
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
Serial.println("Serial GSM Txd is on GPIO" + String(TXD2));
Serial.println("Serial GSM Rxd is on GPIO" + String(RXD2));
//pinMode(LED_PIN, OUTPUT);
delay(3000);
TinyGsmAutoBaud(Serial2, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
String info = modemGSM.getModemInfo();
Serial.println(info);
if (!modemGSM.restart())
{
RestartGSMModem();
}
else
{
Serial.println("Modem restart OK");
}
if (modemGSM.getSimStatus() != SIM_READY)
{
Serial.println("Check PIN code for the SIM. SIM status: " + SimStatus(modemGSM.getSimStatus()));
if (SIM_PIN != "")
{
Serial.println("Try to unlock SIM PIN.");
modemGSM.simUnlock(SIM_PIN);
delay(3000);
if (modemGSM.getSimStatus() != 3)
{
RestartGSMModem();
}
}
}
if (!modemGSM.waitForNetwork())
{
Serial.println("Failed to connect to network");
RestartGSMModem();
}
else
{
RegStatus registration = modemGSM.getRegistrationStatus();
Serial.println("Registration: [" + GSMRegistrationStatus(registration) + "]");
Serial.println("Modem network OK");
}
Serial.println(modemGSM.gprsConnect(APN_NAME,APN_USER,APN_PSWD) ? "GPRS Connect OK" : "GPRS Connection failed");
bool stateGPRS = modemGSM.isGprsConnected();
if (!stateGPRS)
{
RestartGSMModem();
}
String state = stateGPRS ? "connected" : "not connected";
Serial.println("GPRS status: " + state);
Serial.println("CCID: " + modemGSM.getSimCCID());
Serial.println("IMEI: " + modemGSM.getIMEI());
Serial.println("Operator: " + modemGSM.getOperator());
IPAddress local = modemGSM.localIP();
Serial.println("Local IP: " + local.toString());
int csq = modemGSM.getSignalQuality();
if (csq == 0)
{
Serial.println("Signal quality is 0. Restart modem.");
RestartGSMModem();
}
Serial.println("Signal quality: " + GSMSignalLevel(csq) + " [" + String(csq) + "]");
int battLevel = modemGSM.getBattPercent();
Serial.println("Battery level: " + String(battLevel) + "%");
float battVoltage = modemGSM.getBattVoltage() / 1000.0F;
Serial.println("Battery voltage: " + String(battVoltage));
String gsmLoc = modemGSM.getGsmLocation();
Serial.println("GSM location: " + gsmLoc);
Serial.println("GSM location: " + SwapLocation(gsmLoc));
}
void setup()
{
Serial.begin(9600);
Init_GSM_SIM800();
}
void loop()
{
float h0 = 20 + (float)random(20)/10;
float t0 = 30 + (float)random(30)/10;
String postData = "IMEI=861349031663619&PTYPE=DUMP";
postData += "&H0=" + String(h0); //.replace(',','.');
postData += "&T0=" + String(t0); //.replace(',','.');
Serial.println("Send data [" + postData + "] to [" + server + "].");
SendTextByPOST(server, "/", postData);
delay(60000*5);
}