The LilyGo TTGO T-Call is an ESP32-based module with connectivity via GSM cellular network. In this tutorial you will learn how to forward BME280 sensor data to Ubidots using the Monogoto worldwide IoT/M2M connectivity network.
Learn how to connect the TTGO T-Call ESP32 SIM800L board to the Internet using a Monogoto SIM card data plan and publish data to the cloud. We’ll program this board with Arduino IDE. This tutorial assumes you can create an Arduino project and add libraries to it.
Requirements
Monogoto SIM card for global access
1. Monogoto SIM card data access
Monogoto provides global access to cellular networks in over 180 countries. It has an intuitive self-service portal to manage your connected devices. Check this quick start guide to activate your SIM card and the device configuration needed to connect the TTGO T-Call.
2. Arduino Setup
Libraries needed: TinyGSM, Adafruit BME280, ArduinoJson , and make sure to install all other libraries needed by the Adafruit BME280.
Important note: The TTGO T-Call only works on 2G networks. Make sure you have access to a 2G network in your country.
Define the size of your JSON object:
In order to forward data to Ubidots we make an HTTP post request and send a valid “body“, as indicated in our documentation here. For that purpose, we use the Arduino JSON library, where you need to determine the size in bytes of that body. Use the ArduinoJson Assistant so that, by defining the structure of your payload using dummy values, it gives you the size recommended code for the JSON object and serializer, as seen in the GIF below.
Output code:
StaticJsonDocument<48> doc;
doc["temperature"] = 45;
doc["humidity"] = 12.3;
doc["pressure"] = 1234;
serializeJson(doc, output);
The StaticJsonDocument variable doc has a size of 48 bytes. Note that the “output” of the serializeJson() function is a string.
3. Connect the BME280 sensor
Here's how you should connect the sensors to the LilyGo:
Once connected, here is the sample code for your project. Note that you can use your Ubidots account token for a quick test, but a device token is highly advised instead.
#define TINY_GSM_MODEM_SIM800 // Modem is SIM800
#define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb
#include <Wire.h>
#include <ArduinoJson.h>
#include <TinyGsmClient.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
const char apn[] = "data.mono"; // APN https://docs.monogoto.io/getting-started/initial-configuration#device-configuration
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[] = "industrial.api.ubidots.com";
const char resource[] = "/api/v1.6/devices/lilygo"; // resource path "/api/v1.6/devices/<device label>"
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 token = "<Ubidots account Token>";
StaticJsonDocument<48> doc; //Reserve space for JSON object
#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
#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 the serial console for debug prints, if needed
//#define DUMP_AT_COMMANDS
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
// I2C for SIM800 (to keep it running when powered from battery)
TwoWire I2CPower = TwoWire(0);
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
uint32_t frq = 400000;
I2CPower.begin(I2C_SDA, I2C_SCL, frq);
I2CBME.begin(I2C_SDA_2, I2C_SCL_2, frq);
// 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);
}
if (!bme.begin(0x76, &I2CBME)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
// You might need to change the BME280 I2C address, in our case it's 0x76
// Configure the wake up source as timer wake up
//esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
}
void loop() {
String json;
//Build JSON Object
doc["humidity"] = bme.readHumidity();
doc["temperature"] = bme.readTemperature();
doc["pressure"] = bme.readPressure()/100;
//Serialize your JSON Object
serializeJson(doc, json);
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...");
client.print(String("POST ") + resource + " HTTP/1.1\r\n");
client.print(String("Host: ") + server + "\r\n");
client.println("User-Agent: ESP32");
client.println("Connection: close");
client.println("Content-Type: application/json");
client.print("X-Auth-Token: " + token + "\r\n");
client.print("Content-Length: ");
client.println(json.length());
client.println();
client.println(json);
unsigned long timeout = millis();
while (client.connected() && millis() - timeout < 10000L) {
// Print 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"));
}
}
delay(5000);
}
4. Serial monitor output and the final result
Once the device has been compiled and flashed, the serial monitor output is this:
And the data received at Ubidots:
Humidity variable: