ESP32 LoRA SIM800 TinyGSM board connection to Cayenne

What is the problem?

Hi Adam,

Thank you very much for replying.

I have a TF Mini plus lidar sensor connected on a TTGO ESP32 SIM800 board. The Modem connects the ESP32 board directly to Cayenne dashboard and sensor distance data is presented on the dashboard.

I am trying to make the ESP go to sleep after 30 seconds of operation as to make sure that data from the LIdar have reached Cayenne. I have tried integrating the deep sleep configuration within the Lidar;s Setup part as the Lidar is the last subsystem needed to shutdown.

The ESP wakes up, conencts to Cayenne and then goes to sleep without any data from the LIdar appearing on the dashboard.

I will try now to integrate the deep sleep commands within the Lidar’s Loop section and see what happens.

Any help is greatly appreciated

try to use a variable to check the sensor data is sent. then if send is true, go to sleep.

//data send
send = true;

if (send)
{
//go to sleep
}

Hi Shramik,

The code worked and sensor has been forwarded and presented on Cayenne. Again sleep functio was not achieved and i AM DOING MORE TESTING.

The code I am attempting to integrate is the following:

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

RTC_DATA_ATTR int bootCount = 0;

void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;

wakeup_reason = esp_sleep_get_wakeup_cause();

switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println(“Wakeup caused by external signal using RTC_IO”); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println(“Wakeup caused by external signal using RTC_CNTL”); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println(“Wakeup caused by timer”); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println(“Wakeup caused by touchpad”); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println(“Wakeup caused by ULP program”); break;
default : Serial.printf(“Wakeup was not caused by deep sleep: %d\n”,wakeup_reason); break;
}
}

void setup(){
Serial.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor

//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));

//Print the wakeup reason for ESP32
print_wakeup_reason();

esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println(“Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
" Seconds”);

Serial.println(“Going to sleep now”);
delay(1000);
Serial.flush();
esp_deep_sleep_start();
Serial.println(“This will never be printed”);
}

void loop(){
//This is not going to be called
}

It seems that the Serial.flush() function was blocking the sensor;s operation.

I will now try the code again to see if the ESP can go to sleep after a predefined time period.

Code looks fine to me. What do you get in the serial monitor?

I do not get any messages from the sensor and no sensor data is transported to Cayenne.

Instead the ESP goes to sleep after the LTE part has worked.

Please find the complete code below:

#define RX  32
#define TX  33

#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial

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

RTC_DATA_ATTR int bootCount = 0;

#define VIRTUAL_CHANNEL 0
 


int dist; //actual distance measurements of LiDAR
int strength; //signal strength of LiDAR
float temprature;
int check; //save check value
int i;
int uart[9]; //save data measured by LiDAR
const int HEADER=0x59; //frame header of data package








// Your GPRS credentials (leave empty, if missing)

const char apn[]      = ""; // Your APN

const char gprsUser[] = ""; // User

const char gprsPass[] = ""; // Password

const char simPIN[]   = ""; // SIM card PIN code, if any



// 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



// 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[] = "";

char password[] = "";

char clientID[] = "";



// Define the serial console for debug prints, if needed

//#define TINY_GSM_DEBUG SerialMon

//#define DUMP_AT_COMMANDS



#include <Wire.h>

#include <TinyGsmClient.h>

TinyGsm modem(SerialAT);


void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}





void GSM_SETUP() {

  // Set console baud rate

  SerialMon.begin(9600);

  delay(10);







  // 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(115200, 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.restart();

  // Or, use modem.init() if you don't need the complete restart



  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);
    Serial.println("SIM Unlocked");

  }

    Cayenne.begin(username, password, clientID, SerialAT, apn, gprsUser, gprsPass, simPIN);

      



}



void GSM_LOOP() {

  // Put ESP32 into deep sleep mode (with timer wake up)

    Cayenne.loop();

}








void TFMINI_SETUP() 
{

Serial2.begin(115200, SERIAL_8N1, RX, TX);

}





void TFMINI_LOOP() {
//  Cayenne.loop();
if (Serial2.available()) { //check if serial port has data input
 
if(Serial2.read() == HEADER) { //assess data package frame header 0x59
uart[0]=HEADER;
if (Serial2.read() == HEADER) { //assess data package frame header 0x59
uart[1] = HEADER;
for (i = 2; i < 9; i++) { //save data in array
uart[i] = Serial2.read();
}
check = uart[0] + uart[1] + uart[2] + uart[3] + uart[4] + uart[5] + uart[6] + uart[7];
if (uart[8] == (check & 0xff)){ //verify the received data as per protocol
dist = uart[2] + uart[3] * 256; //calculate distance value
strength = uart[4] + uart[5] * 256; //calculate signal strength value
temprature = uart[6] + uart[7] *256;//calculate chip temprature
temprature = temprature/8 - 256;

Serial.print("dist = ");
Serial.println(dist); //output measure distance value of LiDAR


// Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

//  Print the wakeup reason for ESP32
  print_wakeup_reason();

 
   esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
  " Seconds");

  
  
  Serial.println("Going to sleep now");
  delay(18000);
  //Serial.flush(); 
   esp_deep_sleep_start();
  Serial.println("This will never be printed");


}
}
}
}
}







void setup() {
  GSM_SETUP();
  TFMINI_SETUP();  
  

}

void loop() {
  GSM_LOOP();
  TFMINI_LOOP();
  
}

in your code i dont see any code where it publish data.

in your code i dont see any code where it publish data.

Apologies Shamrik,

the last paragraph was not copied., please find below the updated code:

#define RX 32
#define TX 33

#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial

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

RTC_DATA_ATTR int bootCount = 0;

#define VIRTUAL_CHANNEL 0

int dist; //actual distance measurements of LiDAR
int strength; //signal strength of LiDAR
float temprature;
int check; //save check value
int i;
int uart[9]; //save data measured by LiDAR
const int HEADER=0x59; //frame header of data package

// Your GPRS credentials (leave empty, if missing)

const char apn = “”; // Your APN

const char gprsUser = “”; // User

const char gprsPass = “”; // Password

const char simPIN = “”; // SIM card PIN code, if any

// 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

// 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 = “”;

char password = “”;

char clientID = “”;

// Define the serial console for debug prints, if needed

//#define TINY_GSM_DEBUG SerialMon

//#define DUMP_AT_COMMANDS

#include <Wire.h>

#include <TinyGsmClient.h>

TinyGsm modem(SerialAT);

void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;

wakeup_reason = esp_sleep_get_wakeup_cause();

switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println(“Wakeup caused by external signal using RTC_IO”); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println(“Wakeup caused by external signal using RTC_CNTL”); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println(“Wakeup caused by timer”); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println(“Wakeup caused by touchpad”); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println(“Wakeup caused by ULP program”); break;
default : Serial.printf(“Wakeup was not caused by deep sleep: %d\n”,wakeup_reason); break;
}
}

void GSM_SETUP() {

// Set console baud rate

SerialMon.begin(9600);

delay(10);

// 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(115200, 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.restart();

// Or, use modem.init() if you don’t need the complete restart

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);
Serial.println("SIM Unlocked");

}

Cayenne.begin(username, password, clientID, SerialAT, apn, gprsUser, gprsPass, simPIN);

}

void GSM_LOOP() {

// Put ESP32 into deep sleep mode (with timer wake up)

Cayenne.loop();

}

void TFMINI_SETUP()
{

Serial2.begin(115200, SERIAL_8N1, RX, TX);

}

void TFMINI_LOOP() {
// Cayenne.loop();
if (Serial2.available()) { //check if serial port has data input

if(Serial2.read() == HEADER) { //assess data package frame header 0x59
uart[0]=HEADER;
if (Serial2.read() == HEADER) { //assess data package frame header 0x59
uart[1] = HEADER;
for (i = 2; i < 9; i++) { //save data in array
uart[i] = Serial2.read();
}
check = uart[0] + uart[1] + uart[2] + uart[3] + uart[4] + uart[5] + uart[6] + uart[7];
if (uart[8] == (check & 0xff)){ //verify the received data as per protocol
dist = uart[2] + uart[3] * 256; //calculate distance value
strength = uart[4] + uart[5] * 256; //calculate signal strength value
temprature = uart[6] + uart[7] *256;//calculate chip temprature
temprature = temprature/8 - 256;

Serial.print("dist = ");
Serial.println(dist); //output measure distance value of LiDAR

// Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));

// Print the wakeup reason for ESP32
print_wakeup_reason();

esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println(“Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
" Seconds”);

Serial.println(“Going to sleep now”);
delay(18000);
//Serial.flush();
esp_deep_sleep_start();
Serial.println(“This will never be printed”);

}
}
}
}
}

CAYENNE_OUT_DEFAULT(){

Cayenne.virtualWrite(VIRTUAL_CHANNEL, dist);
}

void setup() {
GSM_SETUP();
TFMINI_SETUP();

}

void loop() {
GSM_LOOP();
TFMINI_LOOP();

}

when you call this two function. Do you call the sleep first or publish data (CAYENNE_OUT)?

“Cayenne loop” is called withing the GSM_LOOP and the sleep commands have been entered inthe TF Mini (last procedures to be called) as I thought this would help to get the data transported and then go to sleep.

This is new to me again and I am just doing tests to see how it could work I am afraid.

Thank you again for your help and support

try this.

Dear Shramik,

I am not that knowledgeable so I will look up for an example.

I would like to add that the original script without the addirion of the sleep related code works fine and K get reading on the Serial MOnitor as well as Cayenne.

When I use the sleep script embedded in the original code, I do not get rwadings neither on the Serial MOnitor nor Cayenne, The sensor connects to Cayenne as the LTE code seems to work and then goes to sleep immediately

if (send)
{
esp_deep_sleep_start();
}
CAYENNE_OUT_DEFAULT()
{
send = true;
Cayenne.virtualWrite(VIRTUAL_CHANNEL, dist);
}

Dear Shramik,

Thank you again for your support. Idid folloow a different route and it did succeed in someway.

I used a counter and and IF - ELSE statement within the loop that produces the sensor measurements as well as controls the sleep function.

I managed to get the sensor operating for some time interval and its results transported to Cayenne before the sleep command was enabled.

I am afraid tat the timer of the sleep command does not always work as I set to go to sleep in 6 minutes (360 sec) and the sleep is enabled at around 30 sec of board’s operation.

Have I understood correctly that the enable sleep is the function that controls in waht time interval the sensor will go to sleep or it controls how much time the sensor WILL SPEND in sleep?

Please find the code below

#define RX 32
#define TX 33

#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial

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

RTC_DATA_ATTR int bootCount = 0;

#define VIRTUAL_CHANNEL 0

//char ssid = “”;
//char wifiPassword=“”;

/* For Arduinoboards with multiple serial ports like DUEboard, interpret above two pieces of code and
directly use Serial1 serial port*/
int dist; //actual distance measurements of LiDAR
int strength; //signal strength of LiDAR
float temprature;
int check; //save check value
int i;
int uart[9]; //save data measured by LiDAR
const int HEADER=0x59; //frame header of data package

// Your GPRS credentials (leave empty, if missing)

const char apn = “”; // Your APN

const char gprsUser = “”; // User

const char gprsPass = “”; // Password

const char simPIN = “”; // SIM card PIN code, if any

// 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

// 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 = “”;

char password = “d”;

char clientID = “”;

// Define the serial console for debug prints, if needed

//#define TINY_GSM_DEBUG SerialMon

//#define DUMP_AT_COMMANDS

#include <Wire.h>

#include <TinyGsmClient.h>

TinyGsm modem(SerialAT);

void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;

wakeup_reason = esp_sleep_get_wakeup_cause();

switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println(“Wakeup caused by external signal using RTC_IO”); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println(“Wakeup caused by external signal using RTC_CNTL”); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println(“Wakeup caused by timer”); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println(“Wakeup caused by touchpad”); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println(“Wakeup caused by ULP program”); break;
default : Serial.printf(“Wakeup was not caused by deep sleep: %d\n”,wakeup_reason); break;
}
}

void GSM_SETUP() {

// Set console baud rate

SerialMon.begin(9600);

delay(10);

// 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(115200, 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.restart();

// Or, use modem.init() if you don’t need the complete restart

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);
Serial.println("SIM Unlocked");

}

Cayenne.begin(username, password, clientID, SerialAT, apn, gprsUser, gprsPass, simPIN);

}

void GSM_LOOP() {

// Put ESP32 into deep sleep mode (with timer wake up)

Cayenne.loop();

}

void TFMINI_SETUP()
{
//Serial.begin(9600); //set bit rate of serial port connecting Arduino with computer
//Serial1.begin(115200); //set bit rate of serial port connecting LiDAR with Arduino
Serial2.begin(115200, SERIAL_8N1, RX, TX);

}

void TFMINI_LOOP() {

for (int k = 1; k <= 120; k++){

if (k <= 120 )
{
Cayenne.loop();

if (Serial2.available()) { //check if serial port has data input
//Serial.print(“*”);
if(Serial2.read() == HEADER) { //assess data package frame header 0x59
uart[0]=HEADER;
if (Serial2.read() == HEADER) { //assess data package frame header 0x59
uart[1] = HEADER;
for (i = 2; i < 9; i++) { //save data in array
uart[i] = Serial2.read();
}
check = uart[0] + uart[1] + uart[2] + uart[3] + uart[4] + uart[5] + uart[6] + uart[7];
if (uart[8] == (check & 0xff)){ //verify the received data as per protocol
dist = uart[2] + uart[3] * 256; //calculate distance value
strength = uart[4] + uart[5] * 256; //calculate signal strength value
temprature = uart[6] + uart[7] *256;//calculate chip temprature
temprature = temprature/8 - 256;
//Serial.println(“HEllo5”);

Serial.print("dist = ");

Serial.println(dist); //output measure distance value of LiDAR
Serial.print(“Loop Counter k:”);
Serial.println(k);
}

else {

// Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));

// Print the wakeup reason for ESP32
print_wakeup_reason();

esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println(“Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
" Seconds”);

//Serial.println(“Going to sleep now”);

Serial.println(“Before SerialFlush”);
//Serial.flush();
Serial.println(“After SerialFlush”);
Serial.println(“Before Deep Sleep”);
esp_deep_sleep_start();
Serial.println(“After Deep Sleep”);
Serial.println(“This will never be printed”);

}
}
}
}
}

}

}

void setup() {
GSM_SETUP();
TFMINI_SETUP();
//SLEEP_SETUP();

}

void loop() {
GSM_LOOP();
TFMINI_LOOP();
//SLEEP_LOOP();
}

CAYENNE_OUT_DEFAULT(){
// Write data to Cayenne here. This example just sends the current uptime in milliseconds on virtual channel 0.

Cayenne.virtualWrite(VIRTUAL_CHANNEL, dist);

}

Thanks in advance

you can find all the details here Sleep Modes - ESP32 - — ESP-IDF Programming Guide latest documentation

I did find details there Shramik as well as a lot of posts on Google stating that timer works funny.

Have you come across a problem like this in real life?

Moreover would you say that the if statement I used in the TFMINI void loop is correct?
Would a while statement work better in your opinion?

Thank you

what you are doing is not correct. You need to make sure the data is send. So add it in the CAYENNE_OUT() function. which ensures that the data is send.
try this:

void loop()
{
	Cayenne.loop();
if (send)
{
//then go to sleep
}

}

CAYENNE_OUT(VIRTUAL_CHANNEL)
{
	Cayenne.virtualWrite(VIRTUAL_CHANNEL, millis() / 1000);
        send = true;
}

Dear Shramik,

You were right once again.Your code worked perfectly and data is sent to Cayenne before the board goes to sleep.
I have noticed that while the sensor output readings one per second approximately, the Cayenne OUT function transmits data at a predefined interval and therefore transmits only the value encapsulated at a specific moment.

If I would like to encapsulate more values created by the sensor, is there a mechanism to change the timing of the Cayenne OUT function?

thanks

void loop() {
   Cayenne.loop();
   
   //Publish data every 10 seconds (10000 milliseconds). Change this value to publish at a different interval.
   if(millis() - lastMillis > 10000) {
      lastMillis = millis();
      Cayenne.virtualWrite(0, lastMillis);
   }
}