A la hora de dialogar con los Serials en Arduino, durante estos días he desarrollado funciones para leer cadenas completas de texto desde el Serial y para escribir con la sintaxis de printf(), ya que esto es mucho más fácil cuando se trata de formatear texto.
Bien, ahora se trata de unirlo todo y de dar soporte a cualquier Serial, ya sea HardwareSerial o SoftwareSerial sin complicarnos mucho la vida, con la posibilidad de cambiar esta entrada/salida en cualquier momento y así hacer nuestro programa más flexible.
Para ello he creado la biblioteca SerialExt:
SerialExt.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #ifndef SERIALEXT_H #define SERIALEXT_H /* Quitar comentario si no vamos a usar SoftwareSerial, así reducimos un poco el tamaño del ejecutable. */ /* #define SEXT_NOSOFTWARESERIAL */ #include <WProgram.h> #include <stdio.h> #include <stdarg.h> /* Si no vamos a usar el Software Serial, no creamos soporte para el */ #ifndef SEXT_NOSOFTWARESERIAL #include <dynmem.h> #include <SoftwareSerial.h> #endif class SerialExt { public: ~SerialExt(); SerialExt(const HardwareSerial &serial, long bps=19200); #ifndef SEXT_NOSOFTWARESERIAL SerialExt(uint8_t rxp=2, uint8_t txp=3, long bps=19200); #endif int readString (char *str, unsigned size, const char *stop="\0"); void printf(const char *fmt,...); // A veces, podemos necesitar esto desde fuera void serialPrint(const char *txt); private: bool serialAvailable(); int serialRead(); HardwareSerial *hs; #ifndef SEXT_NOSOFTWARESERIAL SoftwareSerial *ss; #endif int timeout; }; #endif |
SerialExt.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #include "SerialExt.h" void SerialExt::serialPrint(const char *txt) { if (hs) hs->print(txt); #ifndef SEXT_NOSOFTWARESERIAL else ss->print(txt); #endif } void SerialExt::printf(const char *fmt,...) { char tmp[128]; // resulting string limited to 128 chars va_list args; va_start (args, fmt ); vsnprintf(tmp, 128, fmt, args); va_end (args); serialPrint(tmp); } SerialExt::SerialExt(const HardwareSerial &serial, long bps) { hs=(HardwareSerial*)&serial; #ifndef SEXT_NOSOFTWARESERIAL ss=NULL; #endif timeout=3000000/(bps/8); // ( 1000/(bps/8) ) * 1000 * 3.0 (milisegundos por signo por 1.5) // Initialize serial hs->begin(bps); } #ifndef SEXT_NOSOFTWARESERIAL SerialExt::SerialExt(uint8_t rxp, uint8_t txp, long bps) { ss=new SoftwareSerial(rxp, txp); hs=NULL; // Initialize serial ss->begin(bps); } #endif SerialExt::~SerialExt() { #ifndef SEXT_NOSOFTWARESERIAL if (ss) delete ss; #endif } // Sólo aplicable con HardwareSerial bool SerialExt::serialAvailable() { if (hs) return hs->available(); else return true; } int SerialExt::serialRead() { if (hs) hs->read(); #ifndef SEXT_NOSOFTWARESERIAL else ss->read(); #endif } int SerialExt::readString(char *str, unsigned size, const char *stop) { unsigned i=0; char sIn; unsigned long m; // Queremos que la cadena se rellene hasta size-2 para que en el carácter // size-1 (el último) podamos meter el terminador \0 --size; while (serialAvailable() && i<size) { sIn=serialRead(); if (strchr(stop, sIn)) break; str[i++]=sIn; m=micros(); while (!serialAvailable() && micros()<m+timeout); } str[i++]='\0'; return i; } |
Esta biblioteca, hace uso de dynmem, para poder utilizar new y delete y crear el objeto SoftwareSerial. Aunque si no vamos a utilizar el Serial por Software, hay una directiva de pre-procesador (#define SEXT_NOSOFTWARESERIAL) que si quitamos el comentario no compilará nada del soporte, con lo que ahorraremos unos 600 bytes en el binario (que a veces, pueden salvarnos la vida).
Para probar la biblioteca, cargamos el siguiente programa de ejemplo:
serial1.h
1 2 3 4 5 6 7 | /* En este archivo veremos la configuración de nuestro proyecto */ #define SERIAL_SPEED 19200 /* Led que queremos que parpadee mientras se ejecuta el programa */ #define STATUS_LED 11 /* Led que indica transferencia de datos */ #define BLINK_DELAY 500 |
serial1.pde:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | // serial1 // Hace un eco con el puerto serie del Arduino // Dialogamos con el Serial a través de la clase SerialExt. Desde aquí podremos dialogar // con cualquier HardwareSerial o SoftwareSerial #include "serial1.h" #include <dynmem.h> #include <SerialExt.h> #include <SoftwareSerial.h> #define MAX_BUFFER 100 // Almacenamos el estado como variable global int estado=LOW; // Almacenamos también el número de milisegundos anterior unsigned long momento_anterior=0; unsigned long bytes_recibidos=0; // SerialExt SExt(Serial); SerialExt *SExt; void setup() { // Queremos que un led parpadee mientras trabajamos pinMode(STATUS_LED, OUTPUT); digitalWrite(STATUS_LED, HIGH); delay(1000); digitalWrite(STATUS_LED, LOW); delay(1000); SExt = new SerialExt(Serial); digitalWrite(STATUS_LED, HIGH); } void loop () { int recibe; unsigned long momento_actual=millis(); char buf[MAX_BUFFER]; // No bloqueante, si hay algo para leer entramos, si no, no. if(Serial.available()) { SExt->readString(buf, MAX_BUFFER); // Escribimos el buffer completo SExt->printf("Texto recibido: %s\n", buf); } // No usamos delay para el parpadeo porque nos entorpece la comunicación con el serial if (momento_actual-momento_anterior>=BLINK_DELAY) { // Cambiamos el estado siguiente. Si era HIGH (1) ahora será // LOW (0). He leído en algún lado que el valor de HIGH no // siempre es 1; pero en este caso sí es así. estado=!estado; // Escribimos el estado actual del led digitalWrite(STATUS_LED, estado); // Establecemos el momento anterior como actual. momento_anterior=momento_actual; } } |
Como vemos, SerialExt se encarga de hacer Serial.begin() y todo (tampoco es que sea gran cosa, pero nos ahorra una línea); en este ejemplo podemos ver cómo podemos intercambiar líneas completas de texto con Arduino a través del puerto serie.
Pingback: Bitacoras.com /
Compañero me da el siguiente error tu codigo y toy mas perdido que la cresta, help
sketch_feb28a.cpp:14:21: error: serial1.h: No such file or directory
sketch_feb28a.cpp:15:20: error: dynmem.h: No such file or directory
sketch_feb28a.cpp:16:23: error: SerialExt.h: No such file or directory
sketch_feb28a.cpp: In function ‘void setup()’:
sketch_feb28a:37: error: ‘SExt’ was not declared in this scope
sketch_feb28a:37: error: expected type-specifier before ‘SerialExt’
sketch_feb28a:37: error: expected `;’ before ‘SerialExt’
sketch_feb28a.cpp: In function ‘void loop()’:
sketch_feb28a:49: error: ‘SExt’ was not declared in this scope
@reynaldo
Gracias por tu comentario Reynaldo. Tienes que crear hacer dynmem y SerialExt como librerías dentro de la IDE de Arduino. serial1.h tiene que estar en el mismo directorio que tu sketch para poder incluirlo bien.
Saludos
a mi me da el siguiente error
In file included from serial1.cpp:7:
dynmem.h:5: error: declaration of ‘operator new’ as non-function
dynmem.h:5: error: ‘size_t’ was not declared in this scope
dynmem.h:6: error: declaration of ‘operator new []’ as non-function
dynmem.h:6: error: ‘size_t’ was not declared in this scope
@roberto en el dynmem.h y me cuentas.
Qué compilador estás usando ¿? El error lo suele dar cuando no encuentra el tipo de dato que le estamos pasando, en este caso el size_t, prueba incluir