ESP32 SIM800L: публикация данных в облако без Wi-Fi (TTGO T-Call)

Этот проект показывает, как подключить плату TTGO T-Call ESP32 SIM800L к интернету с помощью тарифного плана SIM-карты и публиковать данные в облако без использования Wi-Fi. Мы будем программировать эту плату в Arduino IDE.

TTGO T-Call ESP32 SIM800L: публикация данных в облако без Wi-Fi

Знакомство с TTGO T-Call ESP32 SIM800L

TTGO T-Call — это новая плата разработки на базе ESP32, которая включает в себя модуль GSM/GPRS SIM800L. Её можно приобрести примерно за $11.

TTGO T-Call ESP32 SIM800L GPS GPRS Board

Помимо Wi-Fi и Bluetooth, с этой платой ESP32 можно связываться по SMS или телефонным звонкам, а также подключать её к интернету через тарифный план SIM-карты. Это отлично подходит для IoT-проектов, которые не имеют доступа к ближайшему роутеру.

Важно: SIM800L работает в сетях 2G, поэтому он будет работать только в вашей стране, если доступны сети 2G. Проверьте наличие сети 2G в вашей стране, иначе он не будет работать.

Для использования возможностей этой платы вам понадобится nano SIM-карта с тарифным планом и кабель USB-C для загрузки кода на плату.

ESP32 SIM800L Board USB-C кабель для зарядки и передачи данных

В комплект входят штыревые разъёмы, коннектор для батареи и внешняя антенна, которую следует подключить к плате.

Комплектация ESP32 SIM800L Board

Однако у нас возникли некоторые проблемы с этой антенной, поэтому мы решили перейти на другой тип антенны, и все проблемы были решены. На следующем рисунке показана новая антенна.

ESP32 SIM800L Board альтернативная антенна

Обзор проекта

Идея этого проекта — публиковать данные датчиков из любого места в любой облачный сервис по вашему выбору. ESP32 не нужен доступ к роутеру через Wi-Fi, потому что мы будем подключаться к интернету через тарифный план SIM-карты.

TTGO T-Call ESP32 SIM800L: схема проекта публикации данных в облако без Wi-Fi

В предыдущем проекте мы создали собственный серверный домен с базой данных для отображения показаний датчиков в графиках, к которым вы можете получить доступ из любой точки мира.

В этом проекте мы будем публиковать показания датчиков на этот сервер. Вы можете публиковать свои показания датчиков в любой другой сервис, например ThingSpeak, IFTTT и т.д.

Если вы хотите повторить именно этот проект, вам следует сначала выполнить предыдущее руководство, чтобы подготовить собственный серверный домен. Затем загрузите код, предоставленный в этом проекте, на вашу плату ESP32.

Вкратце, вот как работает проект:

  1. Плата T-Call ESP32 SIM800L находится в режиме глубокого сна.

  2. Она просыпается и подключается к интернету через тарифный план SIM-карты.

  3. Публикует показания датчиков на сервер и снова засыпает.

В нашем примере время сна составляет 60 минут, но вы можете легко изменить его в коде.

Мы будем использовать датчик BME280, но вы можете использовать любой другой датчик, который лучше всего подходит для ваших нужд.

Хостинг-провайдер

Если у вас нет аккаунта хостинга, рекомендуем зарегистрироваться на Bluehost, так как он может обеспечить все требования проекта.

Получить хостинг и доменное имя на Bluehost »

Предварительные требования

1. Дополнение ESP32 для Arduino IDE

Мы будем программировать ESP32 в Arduino IDE. Поэтому вам необходимо установить дополнение ESP32 в Arduino IDE. Следуйте следующему руководству, если вы этого ещё не сделали.

2. Подготовка серверного домена

В этом проекте мы покажем, как публиковать данные в любой облачный сервис. Мы будем использовать собственный серверный домен с базой данных для публикации всех данных, но вы можете использовать любой другой сервис, например ThingSpeak, IFTTT и т.д.

Если вы хотите повторить именно этот проект, следуйте следующему руководству для подготовки собственного серверного домена.

3. SIM-карта с тарифным планом

Для использования платы TTGO T-Call ESP32 SIM800L вам нужна nano SIM-карта с тарифным планом для передачи данных. Мы рекомендуем использовать SIM-карту с предоплаченным или ежемесячным тарифом, чтобы вы точно знали, сколько потратите.

4. Данные APN

Для подключения SIM-карты к интернету вам необходимо знать данные APN вашего оператора. Вам нужны: доменное имя, имя пользователя и пароль.

В моём случае я использую vodafone Portugal. Если вы поищете GPRS APN settings с названием вашего оператора (в моём случае: «GPRS APN vodafone Portugal»), обычно можно найти на форуме или на их сайте всю необходимую информацию.

Я нашёл этот сайт, который может быть очень полезен для поиска всей нужной информации.

Может быть немного сложно найти данные, если вы используете не очень известного оператора. В этом случае вам, возможно, придётся связаться с ними напрямую.

5. Библиотеки

Для этого проекта вам нужно установить следующие библиотеки: Adafruit_BME280, Adafruit_Sensor и TinyGSM. Следуйте инструкциям ниже для установки этих библиотек.

Установка библиотеки Adafruit BME280

Откройте Arduino IDE и перейдите в Sketch > Include Library > Manage Libraries. Откроется Library Manager.

Введите «adafruit bme280» в поле поиска и установите библиотеку.

Установка библиотеки BME280 в Arduino IDE

Установка библиотеки Adafruit Sensor

Для использования библиотеки BME280 вам также нужно установить библиотеку Adafruit_Sensor. Выполните следующие шаги для установки библиотеки в Arduino IDE:

Перейдите в Sketch > Include Library > Manage Libraries и введите «Adafruit Unified Sensor» в поле поиска. Прокрутите вниз, найдите библиотеку и установите её.

Установка библиотеки Adafruit Unified Sensor Driver

Установка библиотеки TinyGSM

В Library Manager Arduino IDE найдите TinyGSM. Выберите библиотеку TinyGSM от Volodymyr Shymanskyy.

Установка библиотеки TinyGSM

После установки библиотек перезапустите Arduino IDE.

Необходимые компоненты

Для сборки этого проекта вам понадобятся следующие компоненты:

Схема подключения

Подключите BME280 к плате T-Call ESP32 SIM800L, как показано на следующей схеме.

TTGO T-Call ESP32 SIM800L с BME280 — схема подключения

Мы подключаем вывод SDA к GPIO 18, а вывод SCL к GPIO 19. Мы не используем стандартные выводы I2C, потому что они уже используются микросхемой управления питанием батареи платы T-Call ESP32 SIM800L.

Код

Скопируйте следующий код в Arduino IDE, но пока не загружайте его. Сначала необходимо внести некоторые изменения, чтобы он заработал.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-sim800l-publish-data-to-cloud/

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

// Your GPRS credentials (leave empty, if not needed)
const char apn[]      = ""; // APN (example: internet.vodafone.pt) use https://wiki.apnchanger.org
const char gprsUser[] = ""; // GPRS User
const char gprsPass[] = ""; // GPRS Password

// SIM card PIN (leave empty, if not defined)
const char simPIN[]   = "";

// Server details
// The server variable can be just a domain name or it can have a subdomain. It depends on the service you are using
const char server[] = "example.com"; // domain name: example.com, maker.ifttt.com, etc
const char resource[] = "/post-data.php";         // resource path, for example: /post-data.php
const int  port = 80;                             // server port number

// Keep this API Key value to be compatible with the PHP code provided in the project page.
// If you change the apiKeyValue value, the PHP file /post-data.php also needs to have the same key
String apiKeyValue = "tPmAT5Ab3j7F9";

// TTGO T-Call pins
#define MODEM_RST            5
#define MODEM_PWKEY          4
#define MODEM_POWER_ON       23
#define MODEM_TX             27
#define MODEM_RX             26
#define I2C_SDA              21
#define I2C_SCL              22
// BME280 pins
#define I2C_SDA_2            18
#define I2C_SCL_2            19

// Set serial for debug console (to Serial Monitor, default speed 115200)
#define SerialMon Serial
// Set serial for AT commands (to SIM800 module)
#define SerialAT Serial1

// Configure TinyGSM library
#define TINY_GSM_MODEM_SIM800      // Modem is SIM800
#define TINY_GSM_RX_BUFFER   1024  // Set RX buffer to 1Kb

// Define the serial console for debug prints, if needed
//#define DUMP_AT_COMMANDS

#include <Wire.h>
#include <TinyGsmClient.h>

#ifdef DUMP_AT_COMMANDS
  #include <StreamDebugger.h>
  StreamDebugger debugger(SerialAT, SerialMon);
  TinyGsm modem(debugger);
#else
  TinyGsm modem(SerialAT);
#endif

#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// I2C for SIM800 (to keep it running when powered from battery)
TwoWire I2CPower = TwoWire(0);

// I2C for BME280 sensor
TwoWire I2CBME = TwoWire(1);
Adafruit_BME280 bme;

// TinyGSM Client for Internet connection
TinyGsmClient client(modem);

#define uS_TO_S_FACTOR 1000000UL   /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  3600        /* Time ESP32 will go to sleep (in seconds) 3600 seconds = 1 hour */

#define IP5306_ADDR          0x75
#define IP5306_REG_SYS_CTL0  0x00

bool setPowerBoostKeepOn(int en){
  I2CPower.beginTransmission(IP5306_ADDR);
  I2CPower.write(IP5306_REG_SYS_CTL0);
  if (en) {
    I2CPower.write(0x37); // Set bit1: 1 enable 0 disable boost keep on
  } else {
    I2CPower.write(0x35); // 0x37 is default reg value
  }
  return I2CPower.endTransmission() == 0;
}

void setup() {
  // Set serial monitor debugging window baud rate to 115200
  SerialMon.begin(115200);

  // Start I2C communication
  I2CPower.begin(I2C_SDA, I2C_SCL, 400000);
  I2CBME.begin(I2C_SDA_2, I2C_SCL_2, 400000);

  // Keep power when running from battery
  bool isOk = setPowerBoostKeepOn(1);
  SerialMon.println(String("IP5306 KeepOn ") + (isOk ? "OK" : "FAIL"));

  // Set modem reset, enable, power pins
  pinMode(MODEM_PWKEY, OUTPUT);
  pinMode(MODEM_RST, OUTPUT);
  pinMode(MODEM_POWER_ON, OUTPUT);
  digitalWrite(MODEM_PWKEY, LOW);
  digitalWrite(MODEM_RST, HIGH);
  digitalWrite(MODEM_POWER_ON, HIGH);

  // Set GSM module baud rate and UART pins
  SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
  delay(3000);

  // Restart SIM800 module, it takes quite some time
  // To skip it, call init() instead of restart()
  SerialMon.println("Initializing modem...");
  modem.restart();
  // use modem.init() if you don't need the complete restart

  // Unlock your SIM card with a PIN if needed
  if (strlen(simPIN) && modem.getSimStatus() != 3 ) {
    modem.simUnlock(simPIN);
  }

  // You might need to change the BME280 I2C address, in our case it's 0x76
  if (!bme.begin(0x76, &I2CBME)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  // Configure the wake up source as timer wake up
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
}

void loop() {
  SerialMon.print("Connecting to APN: ");
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    SerialMon.println(" fail");
  }
  else {
    SerialMon.println(" OK");

    SerialMon.print("Connecting to ");
    SerialMon.print(server);
    if (!client.connect(server, port)) {
      SerialMon.println(" fail");
    }
    else {
      SerialMon.println(" OK");

      // Making an HTTP POST request
      SerialMon.println("Performing HTTP POST request...");
      // Prepare your HTTP POST request data (Temperature in Celsius degrees)
      String httpRequestData = "api_key=" + apiKeyValue + "&value1=" + String(bme.readTemperature())
                             + "&value2=" + String(bme.readHumidity()) + "&value3=" + String(bme.readPressure()/100.0F) + "";
      // Prepare your HTTP POST request data (Temperature in Fahrenheit degrees)
      //String httpRequestData = "api_key=" + apiKeyValue + "&value1=" + String(1.8 * bme.readTemperature() + 32)
      //                       + "&value2=" + String(bme.readHumidity()) + "&value3=" + String(bme.readPressure()/100.0F) + "";

      // You can comment the httpRequestData variable above
      // then, use the httpRequestData variable below (for testing purposes without the BME280 sensor)
      //String httpRequestData = "api_key=tPmAT5Ab3j7F9&value1=24.75&value2=49.54&value3=1005.14";

      client.print(String("POST ") + resource + " HTTP/1.1\r\n");
      client.print(String("Host: ") + server + "\r\n");
      client.println("Connection: close");
      client.println("Content-Type: application/x-www-form-urlencoded");
      client.print("Content-Length: ");
      client.println(httpRequestData.length());
      client.println();
      client.println(httpRequestData);

      unsigned long timeout = millis();
      while (client.connected() && millis() - timeout < 10000L) {
        // Print available data (HTTP response from server)
        while (client.available()) {
          char c = client.read();
          SerialMon.print(c);
          timeout = millis();
        }
      }
      SerialMon.println();

      // Close client and disconnect
      client.stop();
      SerialMon.println(F("Server disconnected"));
      modem.gprsDisconnect();
      SerialMon.println(F("GPRS disconnected"));
    }
  }
  // Put ESP32 into deep sleep mode (with timer wake up)
  esp_deep_sleep_start();
}

Просмотреть исходный код

Перед загрузкой кода вам нужно вставить данные APN, PIN-код SIM-карты (если применимо) и домен вашего сервера.

Важно: Большинство хостинг-сервисов требуют выполнения HTTPS-запросов. Этот код несовместим с HTTPS. Поэтому, чтобы он заработал, вам нужно отключить HTTPS на вашем сервере или включить одновременно HTTP и HTTPS (свяжитесь с вашим хостинг-провайдером). Хотя эта плата поддерживает HTTPS-запросы, нам не удалось заставить их работать. Тем не менее, вы можете попробовать этот пример скетча и проверить, работают ли HTTPS-запросы для вашей платы.

Как работает код

Введите учётные данные GPRS APN в следующие переменные:

const char apn[]      = ""; // APN (example: internet.vodafone.pt) use https://wiki.apnchanger.org
const char gprsUser[] = ""; // GPRS User
const char gprsPass[] = ""; // GPRS Password

В нашем случае APN — это internet.vodafone.pt. У вас он должен быть другим. Ранее в этом руководстве мы объяснили, как получить данные APN.

Введите PIN-код SIM-карты, если применимо:

const char simPIN[] = "";

Вам также нужно указать данные сервера в следующих переменных. Это может быть ваш собственный серверный домен или любой другой сервер, на который вы хотите публиковать данные.

const char server[] = "example.com"; // domain name: example.com, maker.ifttt.com, etc
const char resource[] = "/post-data.php";   // resource path, for example: /post-data.php
const int port = 80;                        // server port number

Если вы используете собственный серверный домен, как мы в этом руководстве, вам также понадобится API-ключ. В данном случае apiKeyValue — это просто случайная строка, которую вы можете изменить. Она используется в целях безопасности, чтобы только тот, кто знает ваш API-ключ, мог публиковать данные в вашу базу данных.

Код подробно прокомментирован, чтобы вы понимали назначение каждой строки.

Следующие строки определяют выводы, используемые модулем SIM800L:

#define MODEM_RST            5
#define MODEM_PWKEY          4
#define MODEM_POWER_ON       23
#define MODEM_TX             27
#define MODEM_RX             26
#define I2C_SDA              21
#define I2C_SCL              22

Определяем выводы I2C для BME280. В этом примере мы не используем стандартные выводы, потому что они уже используются микросхемой управления питанием батареи платы T-Call ESP32 SIM800L. Поэтому мы используем GPIO 18 и GPIO 19.

#define I2C_SDA_2            18
#define I2C_SCL_2            19

Определяем последовательное соединение для Serial Monitor и ещё одно для связи с модулем SIM800L:

// Set serial for debug console (to Serial Monitor, default speed 115200)
#define SerialMon Serial
// Set serial for AT commands (to SIM800 module)
#define SerialAT Serial1

Настраиваем библиотеку TinyGSM для работы с модулем SIM800L.

// Configure TinyGSM library
#define TINY_GSM_MODEM_SIM800      // Modem is SIM800
#define TINY_GSM_RX_BUFFER 1024    // Set RX buffer to 1Kb

Подключаем следующие библиотеки для связи с SIM800L.

#include <Wire.h>
#include <TinyGsmClient.h>

И эти библиотеки для работы с датчиком BME280:

#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

Создаём экземпляр I2C-соединения для SIM800L.

TwoWire I2CPower = TwoWire(0);

И ещё одно I2C-соединение для датчика BME280.

TwoWire I2CBME = TwoWire(1);
Adafruit_BME280 bme;

Инициализируем TinyGSMClient для интернет-соединения.

TinyGsmClient client(modem);

Определяем время глубокого сна в переменной TIME_TO_SLEEP в секундах.

#define uS_TO_S_FACTOR 1000000     /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  3600        /* Time ESP32 will go to sleep (in seconds) 3600 seconds = 1 hour */

В setup() инициализируем Serial Monitor на скорости 115200:

SerialMon.begin(115200);

Запускаем I2C-соединение для модуля SIM800L и для модуля датчика BME280:

I2CPower.begin(I2C_SDA, I2C_SCL, 400000);
I2CBME.begin(I2C_SDA_2, I2C_SCL_2, 400000);

Настраиваем выводы SIM800L в правильное состояние для работы:

pinMode(MODEM_PWKEY, OUTPUT);
pinMode(MODEM_RST, OUTPUT);
pinMode(MODEM_POWER_ON, OUTPUT);
digitalWrite(MODEM_PWKEY, LOW);
digitalWrite(MODEM_RST, HIGH);
digitalWrite(MODEM_POWER_ON, HIGH);

Инициализируем последовательное соединение с модулем SIM800L:

SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);

Инициализируем модуль SIM800L и разблокируем PIN-код SIM-карты при необходимости:

SerialMon.println("Initializing modem...");
modem.restart();
// use modem.init() if you don't need the complete restart

// Unlock your SIM card with a PIN if needed
if (strlen(simPIN) && modem.getSimStatus() != 3 ) {
  modem.simUnlock(simPIN);
}

Инициализируем модуль датчика BME280:

if (!bme.begin(0x76, &I2CBME)) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
}

Настраиваем глубокий сон как источник пробуждения:

esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

Рекомендуемое чтение: ESP32 Deep Sleep и источники пробуждения

В loop() происходит непосредственное подключение к интернету и выполнение HTTP POST-запроса для публикации данных датчиков. Поскольку ESP32 перейдёт в режим глубокого сна в конце loop(), этот код выполнится только один раз.

Следующие строки подключают модуль к интернету:

SerialMon.print("Connecting to APN: ");
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
  SerialMon.println(" fail");
}
else {
  SerialMon.println(" OK");

  SerialMon.print("Connecting to ");
  SerialMon.print(server);
  if (!client.connect(server, port)) {
    SerialMon.println(" fail");
  }
  else {
    SerialMon.println(" OK");

Подготавливаем данные сообщения для отправки через HTTP POST-запрос:

String httpRequestData = "api_key=" + apiKeyValue + "&value1=" + String(bme.readTemperature())
+ "&value2=" + String(bme.readHumidity()) + "&value3=" + String(bme.readPressure()/100.0F) + "";

По сути, мы создаём строку с API-ключом и всеми показаниями датчиков. Вам следует изменить эту строку в зависимости от данных, которые вы хотите отправить.

Следующие строки выполняют POST-запрос.

client.print(String("POST ") + resource + " HTTP/1.1\r\n");
client.print(String("Host: ") + server + "\r\n");
client.println("Connection: close");
client.println("Content-Type: application/x-www-form-urlencoded");
client.print("Content-Length: ");
client.println(httpRequestData.length());
client.println();
client.println(httpRequestData);

unsigned long timeout = millis();
while (client.connected() && millis() - timeout < 10000L) {
  // Print available data (HTTP response from server)
  while (client.available()) {
    char c = client.read();
    SerialMon.print(c);
    timeout = millis();
  }
}

Наконец, закрываем соединение и отключаемся от интернета.

client.stop();
SerialMon.println(F("Server disconnected"));
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));

В конце переводим ESP32 в режим глубокого сна.

esp_deep_sleep_start();

Загрузка кода

После ввода всех необходимых данных вы можете загрузить код на плату.

Для загрузки кода на плату перейдите в Tools > Board и выберите ESP32 Dev module. Перейдите в Tools > Port и выберите COM-порт, к которому подключена ваша плата. Затем нажмите кнопку загрузки, чтобы загрузить код на плату.

Кнопка загрузки в Arduino IDE

Примечание: на данный момент для T-Call ESP32 SIM800L нет отдельной платы в списке, но мы выбрали ESP32 Dev Module, и всё работает нормально.

Демонстрация

Откройте Serial Monitor на скорости 115200 и нажмите кнопку RST на плате.

Сначала модуль инициализируется, затем пытается подключиться к интернету. Обратите внимание, что это может занять некоторое время (в некоторых случаях выполнение запроса заняло почти 1 минуту).

После подключения к интернету он подключится к вашему серверу для выполнения HTTP POST-запроса.

Наконец, он отключится от сервера, отключит интернет и перейдёт в режим сна.

ESP32 SIM800L подключение к интернету через SIM-карту — демонстрация

В этом примере новые показания датчиков публикуются каждые 60 минут, но для тестирования можно использовать более короткую задержку.

Затем откройте браузер и введите домен вашего сервера с URL /esp-chart.php. Вы должны увидеть графики с последними показаниями датчиков.

TTGO T-Call ESP32 SIM800L: графики BME280 — демонстрация публикации данных в облако без Wi-Fi

Устранение неполадок

Если на этом этапе вы не можете подключить модуль к интернету, это может быть вызвано одной из следующих причин:

  • Учётные данные APN могут быть неверными;

  • Антенна может работать неправильно. В нашем случае нам пришлось заменить антенну;

  • Возможно, вам нужно выйти на улицу для лучшего покрытия сигнала;

  • Или вы можете подавать недостаточно тока на модуль. Если вы подключаете модуль к компьютеру через USB-хаб без внешнего блока питания, он может не обеспечить достаточный ток для работы.

Заключение

Надеемся, вам понравился этот проект. На наш взгляд, плата T-Call SIM800 ESP32 может быть очень полезна для IoT-проектов, которые не имеют доступа к ближайшему роутеру через Wi-Fi. Вы можете довольно легко подключить плату к интернету, используя тарифный план SIM-карты.

Вам также может быть интересно:

Дополнительные ресурсы по ESP32:

Примечание

Данная статья является переводом материала с сайта Random Nerd Tutorials.