ESP32 + SIM800C 2G GPRS Telemetry to SiliconWit IO
In this tutorial, you will learn how to send IoT telemetry data from an ESP32 microcontroller to the SiliconWit IO platform using a SIM800C 2G GPRS cellular module. This is ideal for remote deployments where WiFi is not available — such as agricultural monitoring, weather stations, asset tracking, and industrial sensors in the field.
The ESP32 communicates with the SIM800C over UART, establishes a GPRS data connection through the cellular network, and publishes MQTT messages over a secure TLS connection to the SiliconWit IO cloud.
What You Will Learn
Section titled “What You Will Learn”- How to wire a SIM800C module to an ESP32
- How to establish a 2G GPRS data connection using TinyGSM
- How to handle TLS encryption on the ESP32 (since SIM800C only supports plain TCP)
- How to publish telemetry data to SiliconWit IO over MQTT
- Practical considerations and limitations of 2G GPRS for IoT
Prerequisites
Section titled “Prerequisites”Hardware
Section titled “Hardware”| Component | Description |
|---|---|
| ESP32 Development Board | Any ESP32, ESP32-S3, or ESP32-C3 board |
| SIM800C GSM/GPRS Module | 2G cellular modem (SIM800L or SIM800 variants also work) |
| SIM Card | Active SIM with 2G data plan from your mobile carrier |
| Power Supply | 3.7V–4.2V LiPo battery or regulated supply for the SIM800C (draws up to 2A peak) |
| Jumper Wires | For connecting ESP32 to SIM800C (TX, RX, GND) |
| USB Cable | For programming and powering the ESP32 |
| Antenna | GSM antenna for the SIM800C (usually included with the module) |
Software
Section titled “Software”| Software | Purpose |
|---|---|
| PlatformIO | Build system and IDE (VSCode extension or CLI) |
| SiliconWit IO Account | Free IoT platform account |
Libraries
Section titled “Libraries”| Library | Version | Purpose |
|---|---|---|
| TinyGSM | ^0.11.7 | AT command interface for GSM/GPRS modems |
| ESP_SSLClient | ^2.1.7 | TLS/SSL handled by the ESP32 (not the SIM800C) |
| PubSubClient | ^2.8 | MQTT client for Arduino |
| ArduinoJson | ^7.0.0 | JSON serialization |
Why TLS is Handled by the ESP32, Not the SIM800C
Section titled “Why TLS is Handled by the ESP32, Not the SIM800C”The SiliconWit IO MQTT broker requires TLS 1.2 on port 8883. The SIM800C module only supports up to TLS 1.0, which modern servers reject.
The solution is a layered communication stack:
┌──────────────────────────────────────────────────┐│ SiliconWit IO Cloud ││ MQTT Broker (TLS 1.2, port 8883) │└────────────────────┬─────────────────────────────┘ │ TLS 1.2 (encrypted) │┌────────────────────┴─────────────────────────────┐│ ESP32 (ESP_SSLClient) ││ Handles TLS handshake and encryption ││ using hardware crypto acceleration │└────────────────────┬─────────────────────────────┘ │ Plain TCP (unencrypted, internal) │┌────────────────────┴─────────────────────────────┐│ SIM800C (TinyGSM Client) ││ Handles GPRS connection and raw TCP ││ transport over the cellular network │└────────────────────┬─────────────────────────────┘ │ 2G Cellular (GPRS) │┌────────────────────┴─────────────────────────────┐│ Cell Tower / Mobile Network ││ → Internet │└──────────────────────────────────────────────────┘Stack summary: SIM800C (plain TCP) → ESP32 (TLS 1.2) → MQTT Broker
The ESP32’s hardware crypto acceleration makes TLS handshakes fast even over the slow 2G link.
Architecture Overview
Section titled “Architecture Overview”┌─────────────────────────────────────────────────────────┐│ SiliconWit IO Cloud ││ ││ Dashboard ←→ MQTT Broker (TLS on port 8883) ││ ↕ ││ Telemetry Topic ││ d/{device_id}/t │└────────────────────┬────────────────────────────────────┘ │ 2G Cellular Network │┌────────────────────┴────────────────────────────────────┐│ ││ ESP32 SIM800C ││ ┌──────────┐ UART (9600) ┌──────────┐ ││ │ GPIO 4 │←───── TXD ───────│ TXD │ ││ │ GPIO 2 │─────→ RXD ───────│ RXD │ ││ │ GND │──────────────────│ GND │ ┌─────┐ ││ └──────────┘ └──────────┘ │ SIM │ ││ └─────┘ ││ Handles: Handles: ││ - TLS encryption - AT commands ││ - MQTT protocol - GPRS connection ││ - JSON serialization - Raw TCP transport ││ - Sensor reading - Cellular network ││ │└─────────────────────────────────────────────────────────┘Note on Subscribe/Commands: 2G GPRS has high latency and unreliable persistent connections, making MQTT subscribe impractical. This tutorial focuses on publish-only (sending data to the cloud), which works reliably over 2G. For bidirectional control, use WiFi (see the WiFi MQTT Relay Control tutorial) or upgrade to a 4G module.
Step 1: Create a SiliconWit IO Account and Register a Device
Section titled “Step 1: Create a SiliconWit IO Account and Register a Device”- Go to siliconwit.io/register and create a free account.
- Verify your email and log in to the dashboard.
- Navigate to Devices and click Add Device.
- Give your device a name (e.g., “ESP32 GPRS Sensor”).
- Note down these credentials:
| Credential | Example | Description |
|---|---|---|
| Device ID | xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | Unique device identifier (also used as MQTT username) |
| Access Token | your_access_token_here | MQTT password |
| Publish Topic | d/{device_id}/t | Topic for sending data (d = device, t = telemetry) |
Keep your access token secure. Never share it publicly or commit it to version control.
Step 2: Prepare the SIM Card
Section titled “Step 2: Prepare the SIM Card”- Insert the SIM card into the SIM800C module’s SIM tray (usually a micro-SIM slot).
- Ensure 2G data is active on the SIM card. Contact your carrier if unsure.
- Find your carrier’s APN settings. Common examples:
| Country | Carrier | APN | Username | Password |
|---|---|---|---|---|
| Kenya | Safaricom | safaricom | saf | data |
| Kenya | Airtel | internet | (empty) | (empty) |
| USA | T-Mobile | epc.tmobile.com | (empty) | (empty) |
| USA | AT&T | broadband | (empty) | (empty) |
| UK | Vodafone | internet | web | web |
| India | Jio | jionet | (empty) | (empty) |
| Global | Hologram | hologram | (empty) | (empty) |
If you do not know your carrier’s APN, search for “[Your Carrier] APN settings” or contact customer support. Using the wrong APN is the most common cause of GPRS connection failures.
Step 3: Wire the SIM800C to the ESP32
Section titled “Step 3: Wire the SIM800C to the ESP32”Pin Connections
Section titled “Pin Connections”| SIM800C Pin | ESP32 Pin | Description |
|---|---|---|
| TXD | GPIO 4 | SIM800C transmits → ESP32 receives |
| RXD | GPIO 2 | ESP32 transmits → SIM800C receives |
| GND | GND | Common ground (required!) |
| VCC | External 3.7–4.2V | Do NOT power from ESP32’s 3.3V pin |
ESP32 SIM800C┌──────────┐ ┌──────────────┐│ │ │ ││ GPIO 4 │←─── UART ────→│ TXD ││ (RX) │ │ ││ │ │ ││ GPIO 2 │──── UART ────→│ RXD ││ (TX) │ │ ││ │ │ │ ┌─────────┐│ GND ├────────────────│ GND │ │ SIM ││ │ │ │ │ Card │└──────────┘ │ VCC ←───────┼────┐ └─────────┘ └──────────────┘ │ │ ┌────┴────┐ │ 3.7V │ │ LiPo │ │ Battery │ └─────────┘Critical: Power Supply for SIM800C
Section titled “Critical: Power Supply for SIM800C”The SIM800C module draws up to 2A during transmission bursts. The ESP32’s 3.3V regulator cannot supply this. You must use a separate power source:
| Power Option | Voltage | Notes |
|---|---|---|
| 3.7V LiPo battery | 3.7–4.2V | Best option, handles current spikes |
| LM2596 buck converter | Set to 4.0V | From 5V USB or 12V supply |
| 18650 Li-ion cell | 3.7V nominal | Good for portable deployments |
Common symptom of inadequate power: The SIM800C resets repeatedly, the network LED blinks fast but never stabilizes, or AT commands return garbage characters.
SIM800C LED Indicator
Section titled “SIM800C LED Indicator”| LED Behavior | Meaning |
|---|---|
| Blinks every 1 second | Searching for network |
| Blinks every 2 seconds | Connected to network (GPRS ready) |
| Blinks every 3 seconds | Connected and data session active |
| Always on | Module not powered correctly |
Wait until the LED blinks every 2–3 seconds before attempting to connect.
Step 4: Set Up the PlatformIO Project
Section titled “Step 4: Set Up the PlatformIO Project”4.1 Project Structure
Section titled “4.1 Project Structure”project/├── src/│ └── main.cpp├── platformio.ini└── ...4.2 Configure platformio.ini
Section titled “4.2 Configure platformio.ini”; PlatformIO Project Configuration File
[env:esp32-s3-devkitc-1]platform = espressif32board = esp32-s3-devkitc-1framework = arduinomonitor_speed = 115200upload_port = /dev/ttyACM0monitor_port = /dev/ttyACM0upload_flags = --before=no_resetbuild_flags = -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1lib_deps = vshymanskyy/TinyGSM@^0.11.7 knolleary/PubSubClient@^2.8 bblanchon/ArduinoJson@^7.0.0 mobizt/ESP_SSLClient@^2.1.7Board Selection: Change the
boardvalue to match your ESP32 variant:
- ESP32-S3:
esp32-s3-devkitc-1- ESP32-C3:
esp32-c3-devkitm-1- ESP32 (original):
esp32devUSB CDC Flags: Only needed for ESP32-S3/C3 boards using built-in USB for serial. Remove if your board has a dedicated USB-to-UART chip.
Step 5: Write the Firmware
Section titled “Step 5: Write the Firmware”Create src/main.cpp with the following code:
#define TINY_GSM_MODEM_SIM800#define TINY_GSM_RX_BUFFER 1024
#include <Arduino.h>#include <TinyGsmClient.h>#include <ESP_SSLClient.h>#include <PubSubClient.h>#include <ArduinoJson.h>
// ========== PIN DEFINITIONS ==========// ESP32 GPIO4 ← SIM800C TXD (ESP32 receives from SIM800C)// ESP32 GPIO2 → SIM800C RXD (ESP32 sends to SIM800C)#define SIM800C_RX_PIN 4#define SIM800C_TX_PIN 2
// ========== GPRS CREDENTIALS ==========// Change these to match your mobile carrier's APN settingsconst char apn[] = "YOUR_CARRIER_APN";const char gprsUser[] = "";const char gprsPass[] = "";
// ========== SILICONWIT IO MQTT ==========const char* mqtt_server = "mqtt.siliconwit.io";const int mqtt_port = 8883;const char* device_id = "YOUR_DEVICE_ID";const char* access_token = "YOUR_ACCESS_TOKEN";const char* pub_topic = "d/YOUR_DEVICE_ID/t";
// ========== COMMUNICATION STACK ==========// SIM800C (plain TCP) → ESP32 (TLS) → MQTT BrokerHardwareSerial SerialAT(1);TinyGsm modem(SerialAT);TinyGsmClient gsmClient(modem);ESP_SSLClient sslClient;PubSubClient mqtt(sslClient);
// ========== TIMING ==========unsigned long lastTelemetry = 0;const unsigned long TELEMETRY_INTERVAL = 30000;
// ========== MODEM INIT ==========bool initModem() { Serial.println("[MODEM] Initializing..."); modem.restart(); delay(3000);
String modemInfo = modem.getModemInfo(); Serial.print("[MODEM] Info: "); Serial.println(modemInfo);
int simStatus = modem.getSimStatus(); if (simStatus != 1) { Serial.println("[MODEM] SIM not ready!"); return false; } Serial.println("[MODEM] SIM ready");
Serial.print("[MODEM] Waiting for network..."); if (!modem.waitForNetwork(60000)) { Serial.println(" FAILED"); return false; } Serial.println(" OK");
Serial.print("[MODEM] Signal quality: "); Serial.println(modem.getSignalQuality());
return true;}
// ========== GPRS CONNECTION ==========bool connectGPRS() { Serial.print("[GPRS] Connecting to APN: "); Serial.println(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { Serial.println("[GPRS] Connection FAILED"); return false; }
Serial.println("[GPRS] Connected!"); Serial.print("[GPRS] IP: "); Serial.println(modem.localIP()); return true;}
// ========== MQTT CONNECTION ==========bool connectMQTT() { Serial.print("[MQTT] Connecting to SiliconWit IO...");
if (mqtt.connect(device_id, device_id, access_token)) { Serial.println(" connected!"); return true; }
Serial.print(" failed, rc="); Serial.println(mqtt.state()); return false;}
// ========== SEND TELEMETRY ==========void sendTelemetry() { JsonDocument doc; doc["signal"] = modem.getSignalQuality(); doc["uptime"] = millis() / 1000;
String payload; serializeJson(doc, payload);
if (mqtt.publish(pub_topic, payload.c_str())) { Serial.print("[TELEMETRY] Sent: "); Serial.println(payload); } else { Serial.println("[TELEMETRY] Publish failed!"); }}
// ========== SETUP ==========void setup() { Serial.begin(115200); delay(1000); Serial.println("\n=== ESP32 + SIM800C GPRS → SiliconWit IO ===\n");
// Initialize SIM800C UART at 9600 baud (default for SIM800C) SerialAT.begin(9600, SERIAL_8N1, SIM800C_RX_PIN, SIM800C_TX_PIN); delay(3000);
// Initialize modem and wait for network if (!initModem()) { Serial.println("[ERROR] Modem init failed. Check SIM800C wiring and SIM card."); while (true) delay(1000); }
// Connect to GPRS if (!connectGPRS()) { Serial.println("[ERROR] GPRS failed. Check APN settings."); while (true) delay(1000); }
// TLS handled by ESP32 (SIM800C only does plain TCP) sslClient.setClient(&gsmClient); sslClient.setInsecure();
// Configure MQTT mqtt.setServer(mqtt_server, mqtt_port); mqtt.setBufferSize(512);
// Connect to MQTT broker with retries int retries = 0; while (!connectMQTT() && retries < 5) { retries++; delay(5000); }
if (!mqtt.connected()) { Serial.println("[ERROR] MQTT connection failed after 5 retries."); while (true) delay(1000); }
// Send initial telemetry sendTelemetry(); lastTelemetry = millis();}
// ========== LOOP ==========void loop() { // Reconnect GPRS if disconnected if (!modem.isGprsConnected()) { Serial.println("[GPRS] Disconnected, reconnecting..."); connectGPRS(); }
// Reconnect MQTT if disconnected if (!mqtt.connected()) { Serial.println("[MQTT] Disconnected, reconnecting..."); connectMQTT(); }
// Keep MQTT alive mqtt.loop();
// Send telemetry at regular intervals if (millis() - lastTelemetry >= TELEMETRY_INTERVAL) { lastTelemetry = millis(); sendTelemetry(); }}Step 6: Understanding the Code
Section titled “Step 6: Understanding the Code”6.1 The TinyGSM Library
Section titled “6.1 The TinyGSM Library”TinyGSM provides an Arduino Client-compatible interface for GSM modems. It handles all the AT commands internally:
#define TINY_GSM_MODEM_SIM800 // Tell TinyGSM which modem we're using#define TINY_GSM_RX_BUFFER 1024 // Increase receive buffer for MQTT packets
HardwareSerial SerialAT(1); // Use UART1 for modem communicationTinyGsm modem(SerialAT); // Create modem instanceTinyGsmClient gsmClient(modem); // Create TCP client from modemThe HardwareSerial(1) uses UART1 on the ESP32, leaving UART0 (or USB CDC) free for debug serial output.
6.2 The TLS Layer
Section titled “6.2 The TLS Layer”Since the SIM800C cannot handle TLS 1.2, we use the ESP_SSLClient library to perform TLS on the ESP32:
ESP_SSLClient sslClient;sslClient.setClient(&gsmClient); // Wrap the plain TCP client with TLSsslClient.setInsecure(); // Skip certificate verificationThis creates a layered stack: gsmClient (plain TCP via SIM800C) is wrapped by sslClient (TLS via ESP32), which is then used by PubSubClient (MQTT).
6.3 GPRS Connection Flow
Section titled “6.3 GPRS Connection Flow”The connection sequence is:
modem.restart()— Reset the SIM800C modulemodem.getSimStatus()— Verify a SIM card is inserted and readymodem.waitForNetwork()— Wait for the module to register on the cellular networkmodem.gprsConnect(apn, user, pass)— Establish a GPRS data sessionmqtt.connect()— Connect to the MQTT broker over the GPRS link
6.4 Telemetry Payload
Section titled “6.4 Telemetry Payload”The firmware publishes a JSON payload every 30 seconds:
{ "signal": 15, "uptime": 120}| Field | Type | Description |
|---|---|---|
signal | integer | GSM signal quality (0–31, higher is better; 99 = unknown) |
uptime | integer | Seconds since the ESP32 booted |
You can add any custom data fields. For example, with a temperature sensor:
doc["temperature"] = readTemperature();doc["humidity"] = readHumidity();doc["battery_voltage"] = readBatteryVoltage();6.5 Signal Quality Reference
Section titled “6.5 Signal Quality Reference”| Value | Signal Strength | Description |
|---|---|---|
| 0–9 | Marginal | May have connection issues |
| 10–14 | OK | Usable for data |
| 15–19 | Good | Reliable for MQTT |
| 20–31 | Excellent | Best performance |
| 99 | Unknown | Not detectable |
Step 7: Flash the Firmware
Section titled “Step 7: Flash the Firmware”7.1 Build and Upload
Section titled “7.1 Build and Upload”pio run --target upload7.2 Entering Bootloader Mode (if needed)
Section titled “7.2 Entering Bootloader Mode (if needed)”If upload fails:
- Hold the BOOT button
- Press and release the RST button
- Release the BOOT button
- Run the upload command immediately
7.3 Monitor Serial Output
Section titled “7.3 Monitor Serial Output”pio device monitorExpected output on successful connection:
=== ESP32 + SIM800C GPRS → SiliconWit IO ===
[MODEM] Initializing...[MODEM] Info: SIM800 R14.18[MODEM] SIM ready[MODEM] Waiting for network... OK[MODEM] Signal quality: 18[GPRS] Connecting to APN: safaricom[GPRS] Connected![GPRS] IP: 10.214.xx.xx[MQTT] Connecting to SiliconWit IO... connected![TELEMETRY] Sent: {"signal":18,"uptime":12}Step 8: View Data on SiliconWit IO
Section titled “Step 8: View Data on SiliconWit IO”- Log in to your SiliconWit IO dashboard.
- Navigate to your registered device.
- You should see telemetry data updating every 30 seconds.
- The dashboard displays signal strength and uptime, and any additional sensor fields you add.
Troubleshooting
Section titled “Troubleshooting”Modem Issues
Section titled “Modem Issues”| Symptom | Cause | Solution |
|---|---|---|
| ”[MODEM] SIM not ready!” | SIM card not detected | Re-seat the SIM card. Check orientation. |
| ”Waiting for network… FAILED” | No 2G coverage or SIM not activated | Try a different location. Verify 2G is available with your carrier. |
| Modem returns garbage characters | Wrong baud rate | Try 115200 instead of 9600, or auto-detect with modem.setBaud(9600). |
| Modem doesn’t respond at all | Wiring or power issue | Check TX/RX connections (they should be crossed). Verify power supply. |
GPRS Issues
Section titled “GPRS Issues”| Symptom | Cause | Solution |
|---|---|---|
| ”[GPRS] Connection FAILED” | Wrong APN | Verify APN with your carrier. Try with empty username/password. |
| GPRS connects then drops | Weak signal or carrier issue | Move to better coverage. Increase reconnect delay. |
| No IP address assigned | Data plan not active | Contact your carrier to ensure data is enabled on the SIM. |
MQTT Issues
Section titled “MQTT Issues”| Symptom | mqtt.state() | Solution |
|---|---|---|
| Connection timeout | -2 | GPRS may be too slow. Increase connection timeout. Check signal strength. |
| TLS handshake fails | -2 | Ensure sslClient.setInsecure() is called before connecting. |
| Authentication failed | 4 or 5 | Verify device_id and access_token match your SiliconWit IO dashboard. |
| Publishes fail after a while | — | GPRS connection dropped silently. The auto-reconnect in loop() should handle this. |
Power Issues
Section titled “Power Issues”| Symptom | Cause | Solution |
|---|---|---|
| SIM800C resets during transmission | Insufficient current | Use a LiPo battery or dedicated 4V/2A supply. |
| Voltage drops below 3.4V under load | Power supply too weak | Add a large capacitor (1000–2200μF) near VCC/GND. |
| ESP32 resets when SIM800C transmits | Shared power supply overloaded | Use separate power supplies for ESP32 and SIM800C. |
2G GPRS Limitations and Considerations
Section titled “2G GPRS Limitations and Considerations”What Works Well on 2G
Section titled “What Works Well on 2G”- Publishing telemetry data at intervals of 30 seconds or more
- Small JSON payloads (under 256 bytes)
- Infrequent connections (connect, send, disconnect pattern)
What Does NOT Work Well on 2G
Section titled “What Does NOT Work Well on 2G”- MQTT Subscribe — The persistent connection required for receiving messages is unreliable over 2G due to high latency and frequent keep-alive failures.
- Real-time control — Round-trip latency on 2G GPRS is typically 500ms–2000ms, making it unsuitable for real-time relay control.
- Large payloads — GPRS bandwidth is limited to ~50–80 kbps. Keep payloads small.
- Frequent publishes — Publishing more often than every 10 seconds may overwhelm the slow connection.
Recommended Telemetry Intervals for 2G
Section titled “Recommended Telemetry Intervals for 2G”| Use Case | Interval | Notes |
|---|---|---|
| Weather station | 5–15 minutes | Temperature, humidity, pressure change slowly |
| Asset tracking | 1–5 minutes | GPS location updates |
| Soil moisture | 15–30 minutes | Changes very slowly |
| General monitoring | 30 seconds | Default in this tutorial |
| Power-constrained | 15–60 minutes | Maximizes battery life |
Power Optimization for Remote Deployments
Section titled “Power Optimization for Remote Deployments”For battery-powered deployments, consider using deep sleep between transmissions:
#define uS_TO_S_FACTOR 1000000#define TIME_TO_SLEEP 300 // Sleep for 5 minutes
void setup() { Serial.begin(115200);
// Initialize modem and connect SerialAT.begin(9600, SERIAL_8N1, SIM800C_RX_PIN, SIM800C_TX_PIN); delay(3000); initModem(); connectGPRS();
sslClient.setClient(&gsmClient); sslClient.setInsecure(); mqtt.setServer(mqtt_server, mqtt_port); mqtt.setBufferSize(512);
if (mqtt.connect(device_id, device_id, access_token)) { sendTelemetry(); delay(100); mqtt.disconnect(); }
// Disconnect GPRS and put modem to sleep modem.gprsDisconnect(); modem.sleepEnable();
// Put ESP32 into deep sleep esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); esp_deep_sleep_start();}
void loop() { // Never reached — ESP32 resets after deep sleep}This pattern dramatically reduces power consumption:
| Mode | Current Draw | Duration |
|---|---|---|
| Active + transmitting | ~350mA (ESP32) + ~2A peak (SIM800C) | ~10 seconds |
| Deep sleep (ESP32) + SIM800C sleep | ~10μA (ESP32) + ~1mA (SIM800C) | 5 minutes |
Adding Custom Sensors
Section titled “Adding Custom Sensors”Example: DHT22 Temperature and Humidity
Section titled “Example: DHT22 Temperature and Humidity”#include <DHT.h>#define DHTPIN 15#define DHTTYPE DHT22DHT dht(DHTPIN, DHTTYPE);
void sendTelemetry() { float temp = dht.readTemperature(); float hum = dht.readHumidity();
JsonDocument doc; doc["signal"] = modem.getSignalQuality(); doc["uptime"] = millis() / 1000;
if (!isnan(temp)) doc["temperature"] = temp; if (!isnan(hum)) doc["humidity"] = hum;
String payload; serializeJson(doc, payload); mqtt.publish(pub_topic, payload.c_str());}Example: GPS Location (if using A9G or GPS module)
Section titled “Example: GPS Location (if using A9G or GPS module)”void sendTelemetry() { float lat, lon; modem.getGPS(&lat, &lon); // Only works with GPS-enabled modems
JsonDocument doc; doc["signal"] = modem.getSignalQuality(); doc["latitude"] = lat; doc["longitude"] = lon;
String payload; serializeJson(doc, payload); mqtt.publish(pub_topic, payload.c_str());}Example: Battery Voltage Monitoring
Section titled “Example: Battery Voltage Monitoring”#define BATTERY_PIN 34 // ADC pin connected to voltage divider
void sendTelemetry() { int raw = analogRead(BATTERY_PIN); float voltage = (raw / 4095.0) * 3.3 * 2.0; // Assuming 1:1 voltage divider
JsonDocument doc; doc["signal"] = modem.getSignalQuality(); doc["battery_v"] = voltage; doc["uptime"] = millis() / 1000;
String payload; serializeJson(doc, payload); mqtt.publish(pub_topic, payload.c_str());}SiliconWit IO automatically detects and displays new data fields on the dashboard.
Summary
Section titled “Summary”In this tutorial, you built a cellular IoT telemetry system using:
- ESP32 as the main controller
- SIM800C for 2G GPRS cellular connectivity
- TinyGSM for modem communication
- ESP_SSLClient for TLS encryption (handled by ESP32, not SIM800C)
- PubSubClient for MQTT messaging
- SiliconWit IO as the cloud platform
This setup is ideal for remote IoT deployments where WiFi is not available. The key insight is that TLS must be handled by the ESP32 because the SIM800C only supports TLS 1.0, while modern MQTT brokers require TLS 1.2.
For bidirectional control (sending commands to the device), use WiFi instead of 2G GPRS, as described in the companion tutorial: ESP32 WiFi MQTT Relay Control.