Arduino Mid carrier MQTT

Hi, I have recently acquired a portenta mid carrier card, arduino portenta c33 and 4g pro module.
I have been able to make connections to thingspeak, read and send data over https.
Now I am trying to send the data directly to my own mqtt broker, but I cannot find a way to configure the sending since the Arduino cellular library does not reference MQTT. Thank you.

I have a similar challenge and want to use this setup to connect to Arduino cloud which also uses MQTT but can’t find any references.

Thanks for any support

Hi Luis. I am a newbie to arduino and was looking to built an IoT monitoring device via LTE cellular. I read up on arduino and arrived at the same solution of yours using portenta mid carrier card, portenta c33 and 4g pro module. May I shamelessly request if you would share your code with me? Thank you.

You can use this file as a reference for your project. In this file you have how to send the data to the mqtt server

Hi, I'm going to share with you a code that I'm using to read 3 soilwatch 10 sensors and 1 sht31 for ambient temperature and humidity. This sketch sends the data to my own EMQX server that I have mounted on a QNAP.

#include "ArduinoCellular.h"
#include <PubSubClient.h>
#include <Arduino.h>
#include "arduino_secrets.h"
#include <Wire.h>
#include "Adafruit_SHT31.h"
#include <ArduinoJson.h>

// Configuraciones MQTT
const char* mqtt_server = SECRET_SERVER_MQTT;
const char* mqtt_user = SECRET_USER_MQTT;
const char* mqtt_password = SECRET_PASSWORD_MQTT;
const int mqtt_port = atoi(SECRET_PORT_MQTT);

// Inicializar las instancias de ArduinoCellular y PubSubClient
ArduinoCellular cellular = ArduinoCellular();

TinyGsmClient netClient = cellular.getNetworkClient();  // Cliente de red básico
PubSubClient client(netClient); // Usar el cliente básico para PubSubClient


// Variables para la gestión de reconexión
const int max_reconnect_attempts = 5;  // Número máximo de intentos de reconexión
int reconnect_attempts = 0;            // Contador de intentos de reconexión
unsigned long lastReconnectAttempt = 0; // Marca de tiempo del último intento de reconexión
unsigned long reconnectionPause = 0;    // Marca de tiempo para esperar entre reconexiones
bool inPause = false;                   // Indicador de pausa en reconexión

// Intervalo de publicación (10 minutos en milisegundos)
const unsigned long publishInterval = 3 * 60 * 1000; 
unsigned long lastMsg = 0;

Adafruit_SHT31 sht31 = Adafruit_SHT31();

// Sensor SHT31
float HumedadSemillero;          // Variable para almacenar la humedad ambiente
float TemperaturaSemillero;      // Variable para almacenar la temperatura ambiente exterior.

int moistureValue1, mappedValue1;
int moistureValue2, mappedValue2;
int moistureValue3, mappedValue3;

int8_t HoraReferencia;     // Almacenamos la hora actual.
int8_t MinutosReferencia;  // Almacenamos los minutos.
int8_t SegundosReferencia; // Almacenamos los segundos.

const int Ventilador = 31;
//const int EstadoVentilador = 32;

const int analogInPin1 = A1;      // Analog input pin that the sensor output is attached to (white wire)
const int analogInPin2 = A2;      // Analog input pin that the sensor output is attached to (white wire)
const int analogInPin3 = A3;      // Analog input pin that the sensor output is attached to (white wire)


int InversorEstado = 0;
//int ControlVentilador = 0;
int ValmaxHum = 75;

int minADC1 = 23;
int maxADC1 = 865;
int minADC2 = 15;
int maxADC2 = 886;
int minADC3 = 21;
int maxADC3 = 849;

String horaCompleta;  // Variable global para almacenar la hora completa
String fechaHoraFormateada;


void setup() {
  Serial.begin(115200);

  pinMode(Ventilador, OUTPUT);
  digitalWrite(Ventilador, HIGH);
  // pinMode (EstadoVentilador, INPUT_PULLUP);

  if (!sht31.begin(0x44)) {
    Serial.println("Couldn't find SHT31");
    while (1) delay(1);
  }

  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, HIGH);


  // Inicialización del módulo celular
  cellular.begin();

  // Intentar desbloquear la SIM si se proporciona un PIN
  if (String(SECRET_PINNUMBER).length() > 0 && !cellular.unlockSIM(SECRET_PINNUMBER)) {
    Serial.println("Fallo al desbloquear la SIM. Continuando...");
  }

  // Conexión a la red celular
  Serial.println("Conectando...");
  if (!cellular.connect(SECRET_GPRS_APN, SECRET_GPRS_LOGIN, SECRET_GPRS_PASSWORD)) {
    Serial.println("Fallo al conectar a la red. Reintentando en el loop...");
  } else {
    Serial.println("Conectado!");
    client.setServer(mqtt_server, mqtt_port);
    client.setCallback(callback);
  }

ledSequence();

}



// La función callback
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Mensaje recibido [");
  Serial.print(topic);
  Serial.print("]: ");
  
  // Convertir el payload a un string
  String message;
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.println(message);

  // Crea un objeto JSON a partir del mensaje recibido
  StaticJsonDocument<200> doc;
  DeserializationError error = deserializeJson(doc, message);

  // Verifica si hubo un error al parsear el JSON
  if (error) {
    Serial.print("Error al parsear JSON: ");
    Serial.println(error.c_str());
    return;
  }

  // Si el mensaje es para el topic "Propagador/ValmaxHum", extraemos y actualizamos la variable ValmaxHum
  if (String(topic) == "Propagador/ValmaxHum") {
    int valor = doc["valor"]; // Extrae el valor del JSON

    // Actualiza ValmaxHum
    ValmaxHum = valor;
    Serial.print("ValmaxHum actualizado a: ");
    Serial.println(ValmaxHum);
    ledSequence3();
  }
}

bool reconnect() {
  Serial.print("Intentando conexión MQTT...");

  // Desconectar el cliente MQTT antes de intentar reconectar
  client.disconnect();

  if (client.connect("Semillero", mqtt_user, mqtt_password)) {
    Serial.println("Conectado");
    ledSequence3();

    // Suscribirse a los temas
    client.subscribe("Propagador/Garaje");
    client.subscribe("Propagador/ValmaxHum");  // Suscripción al topic para recibir el valor de ValmaxHum

    return true;
  } else {
    ledSequence2();
    Serial.print("Fallo, rc=");
    Serial.print(client.state());
    Serial.println(" intentando de nuevo más tarde");
    return false;
  }
}

void loop() {
  
  LecturaSensores();
  Serial.print("Valor Maximo Humedad: ");
  Serial.println(ValmaxHum);

  // Control del ventilador basado en la humedad
  if (HumedadSemillero >= ValmaxHum) {
    digitalWrite(Ventilador, LOW);
    InversorEstado = 1;
    Serial.println("Ventilador activado");
  } else if (HumedadSemillero < (ValmaxHum - 1)) {
    digitalWrite(Ventilador, HIGH);
    InversorEstado = 0;
    Serial.println("Ventilador Desactivado");
  }

/*
  ControlVentilador = digitalRead (EstadoVentilador);

  if (ControlVentilador == 1) {
    InversorEstado = 0;
  }
  
  if (ControlVentilador == 0) {
    InversorEstado = 1;
  }
*/

  unsigned long now = millis();

  // Gestión de la conexión MQTT
  if (!client.connected()) {
    if (inPause) {
      // Si estamos en la pausa, comprobar si ha pasado el tiempo de espera
      if (now - reconnectionPause > 30000) {  // 30 segundos de espera
        inPause = false;  // Terminar la pausa y permitir intentos de reconexión
        reconnect_attempts = 0;  // Reiniciar el contador de intentos de reconexión
      }
    } else {
      // Intentar reconectar si no estamos en pausa
      if (now - lastReconnectAttempt > 5000) {  // Intentar reconectar cada 5 segundos
        lastReconnectAttempt = now;
        if (reconnect()) {
          reconnect_attempts = 0;  // Reiniciar contador de intentos si se reconecta exitosamente
        } else {
          reconnect_attempts++;
          if (reconnect_attempts > max_reconnect_attempts) {
            reconnectionPause = millis();  // Iniciar la pausa
            inPause = true;  // Activar la pausa
            Serial.println("Número máximo de intentos de reconexión alcanzado. Pausando antes de intentar nuevamente...");
          }
        }
      }
    }
  } else {
    client.loop();

    // Publicar un mensaje cada 10 minutos
    if (now - lastMsg > publishInterval) {
      lastMsg = now;

      ObtenerHora();
      PublicarHoraFormateada ();

      // Crear objeto JSON
      StaticJsonDocument<256> doc;
      doc["HumedadSemillero"] = String (HumedadSemillero, 2);
      doc["TemperaturaSemillero"] = String (TemperaturaSemillero, 2);
      doc["MoistureValue1"] = mappedValue1;
      doc["MoistureValue2"] = mappedValue2;
      doc["MoistureValue3"] = mappedValue3;
      doc["InversorEstado"] = InversorEstado;
      doc["ValmaxHum"] = ValmaxHum;  // Añadir ValmaxHum al mensaje JSON
      doc["HoraCompleta"] = horaCompleta; // Añadir hora completa al JSON


      // Convertir JSON a string
      char buffer[256];
      size_t n = serializeJson(doc, buffer);

      Serial.print("Publicando mensaje: ");
      Serial.println(buffer);

      // Publicar con retención
      client.publish("Propagador/Garaje", (const uint8_t*)buffer, n, true);
      
    }
  }
}

void ledSequence() {
  // Control de LEDs
  digitalWrite(LEDR, HIGH); //Encendemos Led Azul
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, LOW);
  delay(1000);
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, HIGH);
  delay(1000);
}

void ledSequence2() {
  // Control de LEDs
  digitalWrite(LEDR, LOW);  //Encendemos led Rojo
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, HIGH);
  delay (1000);
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, HIGH);

}

void ledSequence3() {
  // Control de LEDs
  digitalWrite(LEDR, HIGH); //Encendemos led Verde
  digitalWrite(LEDG, LOW);
  digitalWrite(LEDB, HIGH);
  delay(1000); 
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, HIGH);
  delay(1000);

}

/* 

**********************************************************************************************************************************  

**************************************** SUBPROGRAMAS*****************************************************************************

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

*/


void ObtenerHora() {
    
    Time time = cellular.getCellularTime();

    // Verifica si la cadena ISO 8601 no está vacía, lo que indica que se ha recibido un tiempo válido
    if (!time.getISO8601().isEmpty()) {

        // Imprime la hora en formato ISO 8601
        Serial.print("Current time (ISO8601): ");
        Serial.println(time.getISO8601()); 

        // Lee la cadena ISO 8601 desde el objeto Time
        String isoTime = time.getISO8601();

        // Encuentra el índice del delimitador 'T'
        int T_index = isoTime.indexOf('T');

        // Extrae la fecha completa (antes de la 'T')
        String fechaCompleta = isoTime.substring(0, T_index);

        // Separa el año, mes y día
        String anio = fechaCompleta.substring(0, 4);
        String mes = fechaCompleta.substring(5, 7);
        String dia = fechaCompleta.substring(8, 10);

        // Extrae la hora completa (después de la 'T' hasta el final)
        String horaCompleta = isoTime.substring(T_index + 1);

        // Elimina la parte +00:00 si está presente
        int plusIndex = horaCompleta.indexOf('+');
        if (plusIndex != -1) {
            horaCompleta = horaCompleta.substring(0, plusIndex);  // Elimina +00:00
        }

        // Formatea la fecha y hora como DD-MM-AAAA HH:MM:SS
        fechaHoraFormateada = dia + "-" + mes + "-" + anio + " " + horaCompleta;
        
        // Imprime la fecha y hora formateada
        Serial.print("Fecha y Hora Formateada: ");
        Serial.println(fechaHoraFormateada);

        // Si necesitas trabajar con las horas, minutos y segundos individualmente:
        int colon_index1 = horaCompleta.indexOf(':');
        int colon_index2 = horaCompleta.indexOf(':', colon_index1 + 1);

        // Extrae las partes de la cadena
        String hourString = horaCompleta.substring(0, colon_index1);
        String minuteString = horaCompleta.substring(colon_index1 + 1, colon_index2);
        String secondString = horaCompleta.substring(colon_index2 + 1, colon_index2 + 3);

        // Convierte las partes a enteros
        HoraReferencia = hourString.toInt();
        MinutosReferencia = minuteString.toInt();
        SegundosReferencia = secondString.toInt();  // Corrección aquí: usar .toInt() en vez de ()
        
        // Llama a la función para formatear la hora completa
        formatTime();

        // Imprime los valores de hora, minutos y segundos
        Serial.print("Horas: ");
        Serial.println(HoraReferencia);
        Serial.print("Minutos: ");
        Serial.println(MinutosReferencia);
        Serial.print("Segundos: ");
        Serial.println(SegundosReferencia);

    } else {
        Serial.println("No se pudo obtener el tiempo válido.");
    }
}




void LecturaSensores () {

    HumedadSemillero = sht31.readHumidity();    // Leemos el sensor de humedad ambiente.       
    Serial.print("Humedad Semillero = ");
    Serial.println(HumedadSemillero);
    
    TemperaturaSemillero = sht31.readTemperature();   // Leemos el sensor de Temperatura ambiente exterior. 
    Serial.print("Temperatura Semillero = ");
    Serial.println(TemperaturaSemillero);



    //**********LETS READ THE WM1 SENSOR ON CHANNEL 1**************
    // first take reading through path A

    moistureValue1 = analogRead(analogInPin1);
    Serial.print("ADC1 = ");
    Serial.print(moistureValue1);
    Serial.print(", ");
    mappedValue1 = map(moistureValue1, minADC1, maxADC1, 0, 100); 
    Serial.print("Moisture value1 = ");
    Serial.println(mappedValue1);



    //**********LETS READ THE WM2 SENSOR ON CHANNEL 2**************
    // first take reading through path A
    

    moistureValue2 = analogRead(analogInPin2);
    Serial.print("ADC2 = ");
    Serial.print(moistureValue2);
    Serial.print(", ");
    mappedValue2 = map(moistureValue2, minADC2, maxADC2, 0, 100); 
    Serial.print("Moisture value2 = ");
    Serial.println(mappedValue2);



//**********LETS READ THE WM2 SENSOR ON CHANNEL 3**************
    // first take reading through path A


    moistureValue3 = analogRead(analogInPin3);
    Serial.print("ADC3 = ");// print ADC results to the serial monitor:
    Serial.print(moistureValue3);
    Serial.print(", ");
    mappedValue3 = map(moistureValue3, minADC3, maxADC3, 0, 100); 
    Serial.print("Moisture value3 = ");
    Serial.println(mappedValue3);
    delay(500);

}

void formatTime() {

    // Formatear cada parte de la hora, minuto y segundo con ceros a la izquierda si es necesario
    String hourString = (HoraReferencia < 10) ? "0" + String(HoraReferencia) : String(HoraReferencia);
    String minuteString = (MinutosReferencia < 10) ? "0" + String(MinutosReferencia) : String(MinutosReferencia);
    String secondString = (SegundosReferencia < 10) ? "0" + String(SegundosReferencia) : String(SegundosReferencia);

    // Concatenar la hora completa en formato HH:MM:SS
    horaCompleta = hourString + ":" + minuteString + ":" + secondString;
}


void PublicarHoraFormateada() {
  if (!client.connected()) {
    reconnect();
  }
  String payload = String(fechaHoraFormateada);
  // Publicar el dato con retención activada
  if (client.publish("Propagador/Fecha&HoraAmbiente", payload.c_str(), true)) {
    Serial.println("Dato de Fecha&Hora publicado correctamente con retención");
  } else {
    Serial.println("Error al publicar dato de Fecha&Hora");
  }
}

arduino_secret.h


#define SECRET_PINNUMBER “pin” // replace with your SIM card PIN
#define SECRET_GPRS_APN  “apn” // replace with your GPRS APN
#define SECRET_GPRS_LOGIN    “user”    // replace with your GPRS login
#define SECRET_GPRS_PASSWORD “password” // replace with your GPRS password
#define SECRET_SERVER_MQTT “your server” // Server MQTT
#define SECRET_USER_MQTT “user mqtt”  
#define SECRET_PASSWORD_MQTT “password mqtt“
#define SECRET_PORT_MQTT “port mqtt”

1 Like

Hi Luis, Thank you for being so generous! Much appreciated!!

Hi Luis. Thank you. May I ask if the code that you shared above, the part where data is sent to your own EMQX server is working? Thanks again!

Hello, the code I have provided you is working correctly. I currently have it working in a seedbed that I have in production. It controls the soil humidity of three trays, the ambient humidity and the ambient temperature. And the switching on and off of a fan that is activated based on the ambient humidity and the threshold set by the reception of mqtt data.

Understood. Thank you Luis. Very grateful.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.