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”
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.