LILYGO TTGO SIM800 & ESP32 Module with MQTT

Good morning Cayenne-fellows,

I have purchased this module:

and I’m trying to get it to work with this minimum code example:
Features: connect co cayenne over MQTT using cellular connection and writing one single value. I’ve followed the code examples from cayenne and called the Cayenne.begin in the setup. However, to get any value transmitted, I have to call the Cayenne.begin() function before the measurement loop again. I am a bit puzzled now. Also, not all values are transmitted then but just 1-2 if I want to transmit more values.

Please see the code example from the manufacturer:

Could you eventually recommend a minimum code example from ESP32 with SIM800 to try for me?

#define SIM800L_IP5306_VERSION_20190610
#include “utilities.h”
#define SerialMon Serial
#define SerialAT Serial1
#define gsmSerial Serial1

// Configure TinyGSM library
#define TINY_GSM_MODEM_SIM800 // Modem is SIM800
#define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb
#include <TinyGsmClient.h>

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

#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds /
#define TIME_TO_SLEEP 20 /
Time ESP32 will go to sleep (in seconds) */

// Your GPRS credentials (leave empty, if not needed)
const char apn = “iot.1nce.net”; // Access point name. Leave empty if it is not needed.
const char gprsUser = “”; // GPRS User
const char gprsPass = “”; // GPRS Password
char simPIN = “”; // SIM pin number. Leave empty if it is not needed.

// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
#include <CayenneMQTTGSM.h>
char username = “XXX”;
char password = “XXX”;
char clientID = “XXX”;
#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial
bool connected_ = false;

TinyGsmClient client(modem);
const int port = 80;

void setupModem()
{
#ifdef MODEM_RST
// Keep reset high
pinMode(MODEM_RST, OUTPUT);
digitalWrite(MODEM_RST, HIGH);
#endif

pinMode(MODEM_PWRKEY, OUTPUT);
pinMode(MODEM_POWER_ON, OUTPUT);

// Turn on the Modem power first
digitalWrite(MODEM_POWER_ON, HIGH);

// Pull down PWRKEY for more than 1 second according to manual requirements
digitalWrite(MODEM_PWRKEY, HIGH);
delay(100);
digitalWrite(MODEM_PWRKEY, LOW);
delay(1000);
digitalWrite(MODEM_PWRKEY, HIGH);

// Initialize the indicator as an output
pinMode(LED_GPIO, OUTPUT);
digitalWrite(LED_GPIO, LED_OFF);

}

void turnOffNetlight()
{
SerialMon.println(“Turning off SIM800 Red LED…”);
modem.sendAT(“+CNETLIGHT=0”);
}

void turnOnNetlight()
{
SerialMon.println(“Turning on SIM800 Red LED…”);
modem.sendAT(“+CNETLIGHT=1”);
}

void setup()
{
SerialMon.begin(115200);
delay(10);
// Start power management
if (setupPMU() == false) {
Serial.println(“Setting power error”);
}
// Some start operations
setupModem();
// Set GSM module baud rate and UART pins
SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
Cayenne.begin(username, password, clientID, gsmSerial, apn, gprsUser, gprsPass, simPIN);
}

void loop()
{
modem.restart();
Cayenne.begin(username, password, clientID, gsmSerial, apn, gprsUser, gprsPass, simPIN);
int temperature = 25;
SerialMon.println(“Sending values to Cayenne”);
Cayenne.virtualWrite(0, temperature, “temp”, “c”);
Cayenne.virtualWrite(1, 25, “temp”, “c”);
SerialMon.println(F(“Poweroff”));
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_deep_sleep_start();
}

CAYENNE_CONNECTED() {
connected_ = true;
Serial.println(“I am connected to Cayenne”);
}

CAYENNE_DISCONNECTED() {
connected_ = false;
Serial.println(“I am DISconnected from Cayenne”);
}

you can just use Cayenne.loop(); in the main function.

Thank you Shramik, I’ve also screened one of your earlier posts:

I’ve started adding feature by feature from the minimum working example.

The critical part was adding a delay before shutting down the module…I’ve kind of confirmed this by trying it multiple times with and without:

Can you recommend a best practice number of bytes? In the current version, I am using 6 double values via MQTT. Is that a bit much?

Also, the module has occasional problems connecting to Cayenne when only powered by battery but that’s hardware related, where it’s impossible for you to troubleshoot it…might be the pure wires delivered by the manufacturer. So if we assume the wire resistance is around 100-200mOhm and the SIM800 takes a maximum of 2000mA when sending, the voltage drop will be around 200-400mV, which could be enough to cause problems for the 3.7V LiPo and the voltage regulator afterwards.

Cayenne.virtualWrite(0, temperature, “temp”, “c”);
Cayenne.virtualWrite(1, signal_str, “signal”, “null”);
Cayenne.virtualWrite(2, distance, “analog_sensor”, “null”);
Cayenne.virtualWrite(3, humidity, “rel_hum”, “p”);
Cayenne.virtualWrite(4, (double)(pressure), “bp”, “hpa”);
Cayenne.virtualWrite(5, (double)(batterylvl), “batt”, “p”);
delay(3000);
modem.gprsDisconnect();
SerialMon.println(F(“GPRS disconnected”));
//After all off
modem.poweroff();
SerialMon.println(F(“Poweroff”));
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_deep_sleep_start();

what are you referring to?

UPDATE:

Hi Guys, Hi Shramik,

I’m using following SIM800 & ESP32 board from lilygo:

More precisely the ip5306 version that has been discontinued.

My code performs following tasks: 1) connect to mqtt broker (Cayenne) that uses the tinygsm library to 2) establish network connection and then 3) perform some measurements and send the data.

My ‘prototype’ has been working for like a week already but occasionally it just freezes and seems to hang in the finding network stage, which is indicated by the slow blining of the module.

I’ve tried already: using different power supplies like LiPo battery (enough capacity, fully charged), benchtop power supply. I’ve also soldered a 470uF cap to VCC and GND to be sure that current spikes of the SIM800 are not a problem.

I am running out of ideas now since in principal the project works, please have a look at my code below.
Is there a way to adapt the tinygsm library such that the MCU goes to sleep after not finding a network?

bool connected_ = false;
#define ARRAY_SIZE 20
uint16_t distance_array_5[ARRAY_SIZE] = {0};
uint16_t distance_array_5_installation[ARRAY_SIZE] = {0};

// Your GPRS credentials (leave empty, if missing)
const char apn[] = "iot.1nce.net"; // Access point name. Leave empty if it is not needed.
const char gprsUser[] = ""; // GPRS User
const char gprsPass[] = ""; // GPRS Password
char simPIN[] = ""; // SIM pin number. Leave empty if it is not needed.


#define IP5306_ADDR 0x75
#define IP5306_REG_SYS_CTL0 0x00
#define IP5306_REG_SYS_CTL1 0x01


// TTGO T-Call pin definitions
#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

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

// BME280 pins
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
Adafruit_BME280 bme; 
#define SEALEVELPRESSURE_HPA (1013.25)

// Pin assignment for sensors
const int ANALOG_PIN_0 = 35; // ADC1_0 GPIO36
const int enable_5 = 34;
int analog_value, distance_mm, distance_cm;
int distance;

// Configure TinyGSM library
#define TINY_GSM_MODEM_SIM800      // Modem is SIM800
#define TINY_GSM_RX_BUFFER   1024  // Set RX buffer to 1Kb
#include <CayenneMQTTGSM.h>
char username[] = "XXX";
char password[] = "XXX";
char clientID[] = "XXX";

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

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

#define SIM800L_IP5306_VERSION_20190610
#include "utilities.h"

TinyGsm modem(SerialAT);
#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  250        /* Time ESP32 will go to sleep (in seconds) */

// Server details
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";

const int  port = 80;

void setup() {
  // Set console baud rate
  SerialMon.begin(9600);
  delay(10);
  Wire.begin(I2C_SDA, I2C_SCL);
  pinMode(ANALOG_PIN_0, INPUT);
  pinMode(enable_5, OUTPUT);

    // Start power management
    if (setupPMU() == false) {
        Serial.println("Setting power error");
    }

  
  // Set-up 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(9600, SERIAL_8N1, MODEM_RX, MODEM_TX);
  delay(3000);

  // Restart takes quite some time
  // To skip it, call init() instead of restart()
  SerialMon.println("Initializing modem...");
  modem.init();
  SerialMon.println("Modem initialized....");
  // Or, use modem.init() if you don't need the complete restart
delay(200);
Wire.beginTransmission(IP5306_ADDR);
Wire.write(IP5306_REG_SYS_CTL0);
Wire.write(0b110111);
// 0x37 = 0b110111 TCALL example
/*
[1] Boost EN (default 1) [EXM note: if 0 ESP32 will not boot from battery]
[1] Charger EN (1) [EXM note: did not observe difference]
[1] Reserved (1) [EXM note: did not observe difference]
[1] Insert load auto power EN (1) [EXM note: did not observe difference]
[1] BOOST output normally open ( 1) [EXM note: if 0 will shutdown when
ESP32 sleeps after 32s]
[1] Key off EN: (0) [EXM note: could not detect difference]
*/
Wire.write(IP5306_REG_SYS_CTL1);
Wire.write(0x1D); // Set HEX:1D DEC:29 BIN:11101
/*
[1] Turn off boost control signal selection: short press twice
[1] Switch WLED flashlight control signal selection: short press twice
[1] Short press switch boost: disabled
[0] Whether to turn on Boost after VIN is pulled out: opened
[1] Batlow 3.0V Low Power Shutdown EN: enabled
*/
  SerialMon.println("Some IP5306 flags set");
  String modemInfo = modem.getModemInfo();
  SerialMon.print("Modem: ");
  SerialMon.println(modemInfo);

  // Unlock your SIM card with a PIN if needed
  if (strlen(simPIN) && modem.getSimStatus() != 3 ) {
    modem.simUnlock(simPIN);
  }
 SerialMon.println("Sim card unlocked if necessary");
    SerialMon.println("Still in setup mode and calling Cayenne.begin");
    Cayenne.begin(username, password, clientID, SerialAT, apn, gprsUser, gprsPass, simPIN);
    delay(200);
    SerialMon.println("successful cayenne connection called in setup");

}

void loop() {
  SerialMon.println("Entering loop and calling Cayenne.loop");
  Cayenne.loop();
}

// Default function for sending sensor data at intervals to Cayenne.
// You can also use functions for specific channels, e.g CAYENNE_OUT(1) for sending channel 1 data.
CAYENNE_OUT_DEFAULT()
{
int signal_str = signal_strength();
bme.begin();
delay(200);
double temperature = bme.readTemperature();
uint16_t humidity = bme.readHumidity();
uint16_t pressure = bme.readPressure() / 100.0F;
double distance = determine_distance_5(temperature);
double batterylvl = getBatteryLevel();
SerialMon.print("Temperature: ");
SerialMon.print(temperature);
SerialMon.print("°C");
SerialMon.print(", Humidity: ");
SerialMon.print(humidity);
SerialMon.print("%RH");
SerialMon.print(", Pressure: ");
SerialMon.print(pressure);
SerialMon.print("hPa");
SerialMon.print(", Distance: ");
SerialMon.print(distance);
SerialMon.print("cm");
SerialMon.print(", Signal strength: ");
SerialMon.print(signal_str);
SerialMon.print(", Battery level: ");
SerialMon.print(batterylvl);
SerialMon.println("%");
SerialMon.println("Transmitting...");
  // Write data to Cayenne here. This example just sends the current uptime in milliseconds on virtual channel 0.
Cayenne.virtualWrite(0, temperature, "temp", "c");
Cayenne.virtualWrite(1, signal_str, "signal", "null");
Cayenne.virtualWrite(2, distance, "analog_sensor", "null");
Cayenne.virtualWrite(3, humidity, "rel_hum", "p");
Cayenne.virtualWrite(4, (double)(pressure), "bp", "hpa");
Cayenne.virtualWrite(5, (double)(batterylvl), "batt", "p");
delay(3000);
SerialMon.println("Transmission of values successful, now trying to disconect modem");
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
//After all off
modem.poweroff();
SerialMon.println(F("Poweroff modem successfull now going to sleep"));
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_deep_sleep_start();
}

CAYENNE_CONNECTED() {
  connected_ = true;
  Serial.println("I am connected to Cayenne");
}

CAYENNE_DISCONNECTED() {
  connected_ = false;
  Serial.println("I am DISconnected from Cayenne");
}

// Default function for processing actuator commands from the Cayenne Dashboard.
// You can also use functions for specific channels, e.g CAYENNE_IN(1) for channel 1 commands.
CAYENNE_IN_DEFAULT()
{
  CAYENNE_LOG("Channel %u, value %s", request.channel, getValue.asString());
  //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");
}


// Reading the network signal 
int signal_strength() {
  int csq = modem.getSignalQuality();
  delay(500);
  Serial.print("Signal quality:");
  Serial.println(csq);
  return (int) (csq);
}


double getBatteryLevel()
{
  Wire.beginTransmission(0x75);
  Wire.write(0x78);
  if (Wire.endTransmission(false) == 0
   && Wire.requestFrom(0x75, 1)) {
    switch (Wire.read() & 0xF0) {
    case 0xE0: return 25;
    case 0xC0: return 50;
    case 0x80: return 75;
    case 0x00: return 100;
    default: return 0;
    }
  }
  return -1;
}

uint16_t determine_distance_5(double temperature) {
  calculate_distance_5(temperature);
  int ok_index = 0;
  double deviation = 0.5;
  uint16_t val_ = 0;
  float average_ = getAverage(distance_array_5, ARRAY_SIZE);
  average_ = 12.71 + 1.128 * average_;
  float std_dev = getStdDev(distance_array_5, ARRAY_SIZE, average_);
  std_dev = 12.71 + 1.128 * std_dev;
  Serial.print("AVG distance: "); Serial.print(average_); Serial.println(" cm");
  Serial.print("STD distance: "); Serial.print(std_dev); Serial.println(" cm");
  return average_;
}

float getAverage(uint16_t * val, int arrayCount) {
  long total = 0;
  for (int i = 0; i < arrayCount; i++) {
    total += val[i];
  }
  return total / float(arrayCount);
}


float getStdDev(uint16_t * val, int arrayCount, double avrg) {
  float avg = avrg;
  long total = 0;
  for (int i = 0; i < arrayCount; i++) {
    total = total + (val[i] - avg) * (val[i] - avg);
  }

  float variance = total / (float)arrayCount;
  float stdDev = sqrt(variance);
  return stdDev;
}

uint16_t calculate_distance_5(double temperature) {
  Serial.println("Starting with the US sensor measurement ");
  for (int i = 0; i < ARRAY_SIZE - 1; i++) {
    distance_array_5[i] = calculateDistanceUS_5(temperature);
    delay(50);
  }
}

uint16_t calculateDistanceUS_5(double temperature) {
  digitalWrite(enable_5, HIGH);
  delay(10);
  float adc = (analogRead(ANALOG_PIN_0) / 4096.0) * 3385.0;
  float distance_20 = (adc / 3385.0) * 500; //distance in cm
  float coeff = (sqrt(temperature + 273.15) / 17.12);
  float distance_compansated = distance_20 * coeff;
  digitalWrite(enable_5, LOW);
  return (double)(distance_20);
}

what is this that you doing?