Envío de Comandos AT por puerto serial desde placa Nano a placa UnaMKR Mini

Hola a todos, estoy utilizando una placa Arduino Nano en conjunto con la placa UniBiz unaMKR Mini la cual trabaja con SigFox. Por un lado estoy enviando desde la Nano los comandos AT para que la unaMKR Mini se encargue de enviar por la red el valor númérico deseado. El tema es que quiero envíar una variable y no encuentro la forma de enviarlo en el formato AT. Traté de concatenar y tampoco tuve éxito. Un ejemplo válido es el siguiente: module.sendCommand(“AT$SF=91,0”, 5000); en este caso el dato enviado es 91 y el 5000 hace referencia a milisegundos.
No encuentro la forma de lograr incorporar a esta expresión “AT$SF=91,0” una variable de tipo long o int como para poder enviar cada tanto tiempo dicha variable.
Agradezco cualquier sugerencia.

Tienes dos formas por lo menos de hacerlo via cadenas de bytes usando sprintf por ejemplo o usando Strings.

Opcion 1:
Quiere enviar “AT$SF=91,0”, 5000" donde los datos son 91 y 5000
reservo espacio para digamos 20 bytes, si necesitas mas amplica el array

void setup() {
    Serial.begin(9600);
    Serial.println("Iniciando");
    char buffer[30];
    int t = 91;
    unsigned long tiempo = 5000;
    sprintf(buffer, "AT$SF=%d,0\”,%ld", t, tiempo);
    Serial.println(buffer);
}

void loop() {
} 

Respuesta:

19:36:28.835 → AT$SF=91,0”,5000

Opcion 2: Con Strings

void setup() {
    Serial.begin(9600);
    Serial.println("Iniciando");
    String buffer = "AT$SF=";
    int t = 91;
    unsigned long tiempo = 50000000;
    buffer += String(t) +",0\”,"+String(tiempo);
    Serial.println(buffer);
}

void loop() {
} 

Resultado

AT$SF=91,0”,5000

Estaba escribiendo una propuesta de solución mientras @surbyte respondió.
Ya que estamos, si es errónea por favor corrijanme.

String comando = "AT$SF=91,";
unsigned long pausa = 5000;

comando += pausa;

Como resultado comando contendrá

"AT$SF=91,5000"

Asumí que 5000 debía reemplazar al 0 como segundo parámetro del comando, de no ser así, hay que agregar “0,” al final de la String comando

String comando = "AT$SF=91,0,";

Saludos

Si es errónea porque se debe agregar String(pausa) para concatener una variable NO String a un String.
Te faltó el 91 que tambien es variable.

Sin embargo lo he compilado y funciona correctamente…
Lo estudiaré más detalladamente.

Lo del primer parámetro (91) es un detalle fácilmente modificable, el autor preguntó como agregar el valor 5000, o sea el segundo parámetro, de ahí mi respuesta ajustada a su consulta.

Saludos

PD: lo que propongo es perfectamente válido según la referencia de Arduino, String Addition Operations

Gracias por el lineamiento.
Estoy teniendo un tema y es que me tira un error.
A continuación mando el código que estoy manejando el cual es obtenido de una librería y el que pretendo adaptar a una variable para después transformar en una función.
Tal vez el código se pueda optimizar más aún, pero así como está es el que vino con la librería.

#include "ATcommands.h"
#include <SoftwareSerial.h>

#define MCU_RX 11
#define MCU_TX 8
#define RST 4 // 

SoftwareSerial moduleSS = SoftwareSerial(MCU_RX, MCU_TX); 
SoftwareSerial *moduleSerial = &moduleSS;

ATcommands module = ATcommands(RST, true); // Usa "falso" si no quieres comandos AT con nueva línea, "verdadero" de lo contrario

String buffer = "AT$SF=";
int t = 91;
unsigned long tiempo = 50000000;


void setup() {

  buffer += String(t) +",0\”,"+String(tiempo);
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  //Serial.print("despierto");
  moduleSerial->begin(115200); // baudrate de la placa unabiz
  module.begin(*moduleSerial);
  
}

void loop() {
  
  Serial.println(buffer);
  digitalWrite(LED_BUILTIN,HIGH);
  //module.sendCommand("AT$ID?", 5000); //"13CAA0B",
  //module.sendCommand("AT$SF=91,0", 5000);//logra enviar el dato!!
  module.sendCommand(buffer);
  digitalWrite(LED_BUILTIN,LOW);
  module.sendCommand("AT$SLEEP", 5000); //luego de 5 segundos de inactividad entra en modo sleep hasta que recibe el próximo dato
  delay(5000);

}

En este caso me tira el siguiente error que es similar a las pruebas realizadas anteriormente: no matching function for call to 'ATcommands::sendCommand(String&)'

Estoy teniendo el tema de que no encuentro ningún caso de ejemplos en los cuales se utilicen las placas unaMKR Mini, por ahora la estoy utilizando como módem pero tengo mis dudas de si no se puede programar también para usar la placa para desarrollo aparte de módem.

Probé también con la línea :

module.sendCommand("AT$SF=%d,0\”,%ld", t, tiempo);

y logré transmitir pero no lo deseado.

Moderador:
Por favor postea usando etiquetas para código y no simple texto plano como has hecho.
Edita.

La única librería ATcommands.h que encontré es la de botletics, dinos si la que usas es otra.

Suponiendo que es la misma, sendCommand() espera como primer parámetro un char, por lo tanto tienes que convertir la variable String a un array tipo char con la función toCharArray().
A modo de ejemplo, tendrías que hacer algo así

String pp = "AT$SF=91,0";
unsigned int t = 5000;
char buff[50];

pp.toCharArray(buff, 11);
// 11 es la longitud del comando + 1 (por el caracter nulo)
// puedes usar pp.length()+1 en su lugar para que sea mas versátil

sendCommad(buff, t);

He simulado la función sendCommand() e hice dos pruebas:

  • Sin convertir la String me daba el mismo error.
  • Haciendo la conversión compiló y ejecutó sin problemas.

Un detalle, la librería define (desde mi punto de vista es un error) el parámetro timeout como tipo uint_16 (unsigned int) por lo que el mismo no puede superar los 65535 ms.
Aunque es un tiempo excesivo para esperar una respuesta a un comando, lo correcto es que sea unsigned long porque la función compara timeout con millis().

@Surbyte si me dices como se hace lo haré con gusto. Saludos.

@gatul como andas? te cuento, he simulado las líneas:

String pp = "AT$SF=91,0";
unsigned int t = 5000;
unsigned long tiempo = 5000;
char buff[50];
pp.toCharArray(buff, 11);
module.sendCommand(buff, t);

y te cuento que la transmisión se ha realizado con éxito, eso es una buena noticia.
Ahora, tengo una duda y es muy cercana a la inicial, creo saber como hacerla, el hecho es que la variable a enviar es el valor 91 (solamente) en este caso. Mi pregunta inicial era como enviar la variable como para que pueda enviar un dato cualquiera sea esta variable. Calculo que concatenando el string, aún no lo he probado...

Desde ya agradezco mucho tu tiempo dedicado.

Abrazo.

Pd: no se si lo que ppedía @gatul se hace así...

Claro, lo haces concatenando.
Supongamos que tu comando sigue siendo "AT$SF=91,0" pero quieres poder enviar distintos valores en lugar de solo 91 y 0.
Yo haría, por ej.

String cmd = "AT$SF=";
int prm1 = 103;
int prm2 = 1;
cmd += prm1;
cmd += ",";
cmd += prm2;

Entonces cmd contendrá "AT$SF=103,1"

Por supuesto prm1 y prm2 pueden ser strings, solo que los valores irán entre comillas.Todo depende de si los datos son fijos y conocidos o variables como la lectura de un sensor.
Incluso puedes usar sprintf() como te mostró @Surbyte más arriba para generar la cadena formateada.

Luego lo conviertes a array de char y listo para enviar. :wink:

La librería que estoy utilizando es:

#ifndef ATcommands_h
#define ATcommands_h

#include "Arduino.h"

#define default_pulse_ms 100
#define default_timeout_ms 1000


class ATcommands {
  public:
    ATcommands(int8_t rst, bool newline = false);
    void begin(Stream &port);
    void reset(uint8_t pulse, uint16_t duration = default_pulse_ms);
    bool sendBlindCommand(char *command);
    bool sendCommand(char *command, uint16_t timeout = default_timeout_ms);
    bool sendCommand(char *command, char *reply, uint16_t timeout = default_timeout_ms);
  
  private:
    int8_t _rst;
    bool newline = false;
    char replybuffer[255];
    unsigned long timer;
    Stream *mySerial;
};

#endif

@gatul vos sabés que parece que quiere, manda pero no logra traducir todos, de hecho le mande lo siguiente:

#include "ATcommands.h"
#include <SoftwareSerial.h>

#define MCU_RX 11
#define MCU_TX 8
#define RST 4 // 

SoftwareSerial moduleSS = SoftwareSerial(MCU_RX, MCU_TX); 
SoftwareSerial *moduleSerial = &moduleSS;

ATcommands module = ATcommands(RST, true); // Usa "falso" si no quieres comandos AT con nueva línea, "verdadero" de lo contrario

String pp = "AT$SF=";
int var = 7;
unsigned int t = 5000;
unsigned long tiempo = 5000;
char buff[50];

void setup() {
  //buffer += String(t) +",0\”,";//+String(tiempo); //concateno todos los valores

  
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  //Serial.print("despierto");
  moduleSerial->begin(115200); // baudrate de la placa unabiz
  module.begin(*moduleSerial);
}

void loop() {

  digitalWrite(LED_BUILTIN,HIGH);
  //module.sendCommand("AT$ID?", 5000); //"13CAA0B",
  //module.sendCommand("AT$SF=91,0", 5000);//logra enviar el dato!!
  var ++;
  pp = "AT$SF=";
  pp += var;
  pp += ",0";
  pp.toCharArray(buff, 12);
  module.sendCommand(buff, t);//veremos si anda....
  digitalWrite(LED_BUILTIN,LOW);
  module.sendCommand("AT$SLEEP", 5000); //luego de 5 segundos de inactividad entra en modo sleep hasta que recibe el róximo dato
  delay(5000);
}

y me recibió...

38, después 41.. lo resetie y arrancó en 56 y 59, más nada.. siniestro... como que quiere pero no llega.. ojo que estoy utilizando la placa unaMKR mini para mandar los datos por la red y no hay mucha documentación al respecto.

Si, la librería es la misma.

Usá pp.length()+1 como segundo parámetro de toCharArray() porque la longitud varía según el número que uses como 1er. parámetro del comando y estás usando una longitud fija de 12, eso va a generar problemas en la conversión.

No conozco la unoMKR, de hecho, todavía no he salido del Nano. :worried:

Si claro, como ya te he dicho, Lee las normas del foro. 1er hilo de cada Sección.
De todos modos ya lo he hecho. Se pierde mucho tiempo explicando lo que se puede entender leyendo las normas. Cuando entres a un foro siempre lee sus normas.

En el post#2 te indiqué como hacerlo usando dos opciones, solo tenias que usar la mas adecuada.
las soluciones funcionan en entornos distintos. En tu caso desconocía como funciona module.sendComand() si acepta o no String o acepta o no const char *

Eso no funcionó porque no usaste lo que te sugerí.
debes crear un buffer previo

    char buffer[30];
    int t = 91; // reemplaza con tu variable 1
    unsigned long tiempo = 5000;  // reemplaza con tu variable 2
    sprintf(buffer, "AT$SF=%d,0\”,%ld", t, tiempo);
   // Serial.println(buffer); suponía que en en código que usabas esto no respresentaría problemas
   module.sendCommand(buffer);

Ustedes saben que el tema de armar el buffer con sprintf y luego mandarlo con sendCommand me genera problemas, por ejemplo si intento mandar el valor 1 no me deja, si pongo 91 si, si pongo 1555 no me deja.. sinceramente es algo que no logro comprender a nivel de programación. Solo trato de poner una línea como de costumbre y olvidarme del tema pero al parecer no es tan accesible a mi mente.

Este código así como está, me manda un dato cada 15 minutos. Me ayudaría bastante poder mandar una variable para optimizar el código dado que dicha variable va a llevar una cuenta que va incrementándose con el tiempo.

En este caso el dato que envío es solamente 155589, no es ni la coma, ni el cero siguiente ni el 5000... es solamente 155589 que puede valer eso como 1, 2 o 3... por eso es que me facilitaría que fuese por ejemplo int t jejeje.

Saludos.

//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////

#include "ATcommands.h"
#include <SoftwareSerial.h>

#define MCU_RX 11
#define MCU_TX 8
#define RST 4 // 

SoftwareSerial moduleSS = SoftwareSerial(MCU_RX, MCU_TX); 
SoftwareSerial *moduleSerial = &moduleSS;

ATcommands module = ATcommands(RST, true); // Usa "falso" si no quieres comandos AT con nueva línea, "verdadero" de lo contrario

//String pp = "AT$SF=";
//int t = 1;
//unsigned long tiempo = 5000;
//char buffer[30];
unsigned long tiempo_millis = millis();

//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  //Serial.print("despierto");
  moduleSerial->begin(115200); // baudrate de la placa unabiz
  module.begin(*moduleSerial);
}

//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////

void loop(){
  // Si pasó un tiempo x se envía la variable t con el valor de ese instante
  if ( millis() - tiempo_millis >= 890000 ) {//está para probar de enviar el dato cada un minuto
                                            //10000 ms = 10 segundos
                                            //60000 ms = 1 minuto
                                            //900000 ms = 15 minutos
     
      Serial.println("pasaron x ms, entonces invoco a envio_dato");
      envio_dato();
      //t ++;
      tiempo_millis = millis(); // Guardamos el tiempo de nuevo para comprobar en las siguientes pasadas.
  }else{
    if ( millis() - tiempo_millis <= 0 ) {
      tiempo_millis = millis();
    }
  }
  
}

//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////

void envio_dato() {//en total demora 10 segundos en mandar el dato
  //module.sendCommand("AT$SF=91,0", 5000);//logra enviar el dato!!
  //despierto la placa
  module.sendCommand("AT$SB=1,0");
  delay(4000);
  digitalWrite(LED_BUILTIN,HIGH);
  module.sendCommand("AT$SF=155589,0", 5000);
  delay(6000);
  digitalWrite(LED_BUILTIN,LOW);
  module.sendCommand("AT$SLEEP", 5000); //luego de 5 segundos de inactividad entra en modo sleep hasta que recibe el róximo dato
  //delay(10000);
}

Es sencillo

long valor = 155589;
int valor2 = 1;

sprintf(buffer, "AT$SF=%ld", valor);

sprintf(buffer, "AT$SF=%ld,0", valor);  // el segundo parámetro fijo (en este caso 0)

sprintf(buffer, "AT$SF=%ld,%d", valor, valor2);  // el 2do parametro también variable

%d solo admite int
%ld es para usar con long (como el valor 155589)

Te aclaro que lo probé e imprime cualquier valor, incluso el 1. :wink:

Sobre tu código,

if ( millis() - tiempo_millis <= 0 ) {
      tiempo_millis = millis();
}

solo se va a cumplir si el resultado es 0 porque al ser una resta entre dos unsigned long, obviamente nunca habrá resultado negativo porque no tienen signo.
Aclarado esto, realmente toda la sentencia deja de tener sentido porque, para que el resultado sea 0, necesariamente tiempo_millis tiene que ser igual a millis(), o sea que no tiene razón de ser el forzar la igualdad.