Extraer datos seriales de tramas extensas | AVR ?

Hola de nuevo a todos, paso por aquí para molestarlos, tengo un inconveniente en extraer datos de tramas extensas por medio del protocolo serial con el atmega328p.

Estoy trabajando con un módulo GSM SIM800F, puedo enviar datos a un servidor por el método POST, pero esta vez también quiero usar el GET para realizar un corte y reconexión de energía de una casa desde una página web.

Cuando se usa el GET, el GSM envía una gran cantidad de caracteres al uC y éste tiene que ser capaz de procesar toda la información y depende de uno si desea extraer alguna información, en mi caso dentro de toda esta trama al final llegará la palabra "accion=1," o "accion=0," y tengo que procesar solo el "1" y "0" el resto de caracteres de toda la trama no me interesa.

Estoy trabajando en recepcionar y procesar datos seriales con ayuda del hercules y proteus pero al momento de enviar una gran cantidad de datos al uC no llega a procesar solo procesa cuando le envío pocos datos o bytes.

Estoy usando maquinas de estado para recibir y procesar información, si alguien puede darme una mano o sugerencia para modificar mi código, lo dejaré para que puedan darle un ojo.



C:
/*
* Energy.c
*
* Created: 13/12/2020 10:33:05
* Author : Marcelo Higa
*/

#define F_CPU 1000000UL
#include <avr/io.h>
#include <stdio.h>  
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#include "USART.h"



bool Flag=false;   
char Str_data[10];  
uint8_t dato;
char Data_Rx;
char DataRx_sim[500];
uint16_t point = 0;
char Str_token1[10];

typedef enum{
    Idle=0,
    ProcessData,
   
}state;
state StatePIC;  //Estados para procesar la data en el while



typedef enum{
    Read_normal=0,
    Read_C,
    Read_I,
    Read_O,
    Read_N,
    captura_data,
   
}StateReceiveSerial;
StateReceiveSerial Receive; //Estados para recepcionar datos seriales.


int main(void)
{
    sei();
    Serial_begin(9600);
   
    StatePIC = Idle;
    Receive = Read_normal;     //Estados iniciales
   
    DDRB |=(1<<DDB2);
    PORTB &= ~(1<<PORTB2);
   
   
    while (1)
    {
        switch(StatePIC){
           
            case Idle:
                if (Flag==true)
                {
                    StatePIC = ProcessData;
                }
           
            break;
           
           
           
            case ProcessData:
           
            strcpy(Str_token1,strtok(DataRx_sim,"="));
            strcpy(Str_token1,strtok(NULL,","));
           
            dato=atoi(Str_token1);
           
            PORTB = (dato<<PORTB2);
           
            Flag=false;
            point=0;
            StatePIC=Idle;
            memset(DataRx_sim,0,sizeof(DataRx_sim));
            sei();
           
            break;
           
           
        }
           
    }
}







ISR(USART_RX_vect){
   
    Data_Rx = (char)UDR0;
   
    switch(Receive){
       
        case Read_normal:
               
                if (Data_Rx == 'c')
                {
                    Receive = Read_C;
                    point=0;
                }
                else
                {
                    DataRx_sim[point] = Data_Rx;
                    point++;
                }
                   
                   
        break;
       
       
        case Read_C:
            DataRx_sim[point] = Data_Rx;
       
                if (Data_Rx == 'c')
                {
                    Receive = Read_I;
                    point++;
                }
                else
                {
                    Receive = Read_normal;
                    point = 0;  
                }
               
        break;
       
       
         case Read_I:
            DataRx_sim[point] = Data_Rx;
                if (Data_Rx  =='i')
                {
                    Receive = Read_O;
                    point++;
                }
                else
                {
                    point=0;
                    Receive = Read_normal;
                }
        break;
       
       
        case Read_O:
            DataRx_sim[point] = Data_Rx;
                if (Data_Rx  =='o')
                {
                    Receive = Read_N;
                    point++;
                }
                else
                {
                    point=0;
                    Receive = Read_normal;
                }
        break;
       
       
        case Read_N:
            DataRx_sim[point] = Data_Rx;
                if (Data_Rx  =='n')
                {
                    Receive = captura_data;
                    point++;
                }
                else
                {
                    point=0;
                    Receive = Read_normal;
                }
        break;
       
        case captura_data:
           
                if (Data_Rx ==',')
                {  
                    point=0;
                    Receive=Read_normal;
                    Flag=true;
                    cli();     //Desactivo interrupciones
                }
                else
                {
                    DataRx_sim[point]=Data_Rx;
                    point++;
                }
               
        break;
       
       
    }
}

La libreria USART :
C:
///***** Marcelo Higa *****///
//Libreria USART///


#include <avr/io.h>
#define F_CPU 1000000UL

void Serial_begin(uint32_t baudios);
void printc_USART(char c);
void prints_USART(char *p_data);



//---**********************************************************
void Serial_begin(uint32_t baudios){  //9600 bps
    uint16_t MyUBRR=0;
    DDRD   |= (0<<DDD0)|(1<<DDD1); // PD0:Rx->Entrada, PD1:Tx->Salida
    UCSR0A |= (1<<U2X0); //Habilito Doble velocidad
    UCSR0B |= (1<<RXEN0)|(1<<TXEN0); //Habilito Tx y Rx
   
    UCSR0B |= (1<<RXCIE0);  //Habilito interrupcion por recepcion
   
    //Seleciono Modo Asincrono, no paridad, 1 bit Stop, 8 bits Datos
    UCSR0C |= (0<<UMSEL01)|(0<<UMSEL00)|(0<<UPM01)|(0<<UPM00)|(0<<USBS0)|(1<<UCSZ01)|(1<<UCSZ00);
    MyUBRR  = (uint16_t) ( ( ( F_CPU / 8 ) / baudios ) -1 ) ;
    //Cargo el valor al registro UBRR para generar la velociad de transmision
    UBRR0L  = MyUBRR;
    UBRR0H  = (MyUBRR>>8);
}

//Ejemplo: printc('A');
void printc_USART(char c){
    while( (UCSR0A & (1<<UDRE0)) == 0); //Espero que el dato se transmita
    UDR0 = c;
}



// Ejemplo: prints_USART("Hola mundo AVR");
void prints_USART(char *p_data){
    while(*p_data != '\0'){
        printc_USART(*p_data);
        p_data++;
    }
}
 
Mejor publicá la trama de datos que enviás y la que recibís.
Estoy trabajándolo con hércules y proteus, dejaré una imagen de lo que llego a enviar y no es mas que una pequeña trama y con esto si se logra procesar la informacion que yo quiero en este caso el digito de la palabra "accion =1" o "0"(prender o apagar el led) pero cuando envío una trama extensa no se logra procesar, la idea es simular envíos desde el hercules tramas casi de la misma longitud que lo haría un GSM al uC.
 

Adjuntos

  • gsm1.png
    gsm1.png
    679.4 KB · Visitas: 27
  • gsm2.png
    gsm2.png
    693.8 KB · Visitas: 27
  • gsm3.png
    gsm3.png
    736.8 KB · Visitas: 27

Dr. Zoidberg

Well-known-Papá Pitufo
La última imagen muestra los headers del protocolo Http, pero no sé quien los envía.
De todas formas hay bibliotecas libres para hacer el parsing del protocolo http y recuperar los headers y los datos que a vos te interesan.
 
Los headers que mencionas solo es un ejemplo que yo escribí en el Hércules y los envié a proteus solo para simular la recepción y procesamiento de datos.

Repito, cuando envió una trama larga no llego a procesar "ccion=x" (x=0 o 1) pero el uC si procesa cuando envío una trama pequeña como se ve en las imágenes.
 

Dr. Zoidberg

Well-known-Papá Pitufo
Repito, cuando envió una trama larga no llego a procesar "ccion=x" (x=0 o 1) pero el uC si procesa cuando envío una trama pequeña como se ve en las imágenes.
Buffer demasiado chico??
Agrandá DataRx_sim a 512 bytes.

Y veo a la ISR (USART_RX_vect) demasiado complicada para solo recibir por la interfaz serie y cargar un arreglo de char...
 
En mi caso cuando tuve que hacer algo similar, gracias al uC que usé tenía la posibilidad de darme el lujo de reservar una buena cantidad de memoria como buffer para almacenar lo que recibía (128/256 bytes) y luego tomarme el tiempo para analizarlos.

La metodología que usaba era estar pendiente del 1er byte y esperar con un timer un cierto tiempo hasta que se complete la recepción del mensaje (1 seg por ej.), obviamente desde la interrupción iba guardando en el buffer byte a byte lo que recibía . Luego usando el puntero del buffer con la función "strstr" de C, obtenía la dirección de memoria donde aparecía la palabra clave (en tu caso, "accion="), en caso de no aparecer, el puntero que obtenías era nulo. Una vez terminado el análisis, limpiaba el buffer, es decir que NO era circular.

La contra de esta metodología, era que no podías levantar dos o más mensajes a la vez, ahí necesitarías trabajar con otro buffer auxiliar. Sin embargo, como el mensaje venía desde un celular manejado por un usuario, el problema no era tan grave.
 
Muy interesante lo que mencionas tío cosme, de hecho el algoritmo que tengo al inicio espero hasta que me llega la "c",luego la otra "c", luego "i" así hasta la coma que me representa el final de trama.

Después de eso desactivo interrupciones y proceso en el while con strtok lo que contiene "ccion=1," solo me interesa el último dígito.


Buffer demasiado chico??
Agrandá DataRx_sim a 512 bytes.
No creo que sea demasiado chico, son 500 bytes y estoy enviando una cantidad menor y luego lo llego a limpiar.
 

Dr. Zoidberg

Well-known-Papá Pitufo
En mi caso cuando tuve que hacer algo similar, gracias al uC que usé tenía la posibilidad de darme el lujo de reservar una buena cantidad de memoria como buffer para almacenar lo que recibía (128/256 bytes) y luego tomarme el tiempo para analizarlos.
Es que acá ha hecho mucho lío para determinar la secuencia "ccion" mientras va recibiendo los bytes. O hay algún motivo para hacerlo (que yo no veo) o es otro que hace "sobre-uso" de las state-machines para organizar el código...
Mensaje automáticamente combinado:

No creo que sea demasiado chico, son 500 bytes y estoy enviando una cantidad menor
Vos le enviarás menos, pero no decís que el GSM le agrega una parva de datos??
 
Es que acá ha hecho mucho lío para determinar la secuencia "ccion" mientras va recibiendo los bytes. O hay algún motivo para hacerlo (que yo no veo) o es otro que hace "sobre-uso" de las state-machines para organizar el código...
Doc lo que pasa es que junto a un amigo de sistemas se realizará un sistema de corte y reconexión de energía eléctrica de una o varias viviendas.

El asunto es que se realizará con un módulo GSM SIM800F , cada día el sistema enviará por el método POST hacia la página el consumo de energía.

La página cuenta con 2 opciones de "Corte" y "Reconexión "

El uso del método GET se programará al uC que realice esta petición cada 3 a 5 segundos a la página y es aquí donde el GSM enviará una trama extensa de bytes al uC ( ya que éste realizo la petición por GET) , dentro de toda esa trama tiene que haber algo que el uC pueda procesar para que corte o reconecte la energía.

Yo le sugerí a mi compañero que añada una línea o palabra clave en su código cuando se use GET, en este caso añadió la palabra "accion=1" al final de la trama. También puede ser "accion=0" y como ya lo mencioné solo interesa el último dígito para corte o reconexión.

Quizás hay otra manera de procesar o extraer un dato que nos interesa de trama extensas, a mi se me ocurrió hacerlo de esa manera pero estoy atento a cualquier sugerencia.
 
Es que acá ha hecho mucho lío para determinar la secuencia "ccion" mientras va recibiendo los bytes. O hay algún motivo para hacerlo (que yo no veo) o es otro que hace "sobre-uso" de las state-machines para organizar el código...

Yo soy super fanático de las máquinas de estado. Cuando el proceso secuencial es muy complejo, la máquina de estado te lo resuelve muy fácilmente.

El problema que le veo al estar pendiente letra a letra en la secuencia, es que termina siendo poco flexible. Por ej. si tenés que analizar otro comando "lectura=", tenés que hacer todo una máquina de estado para "lectura".
 

Dr. Zoidberg

Well-known-Papá Pitufo
Cuando el proceso secuencial es muy complejo, la máquina de estado te lo resuelve muy fácilmente.
Pero es que en este caso no hay complejidad en la recepción de un string. Solo hay que meter dato a dato en el arreglo, agregar un '\0' despues del ultimo (que acá debería reemplazar al '\n' que especifica la RFC del protocolo http) y listo. Notificar al programa que llegó algo y que otro se encargue de ver que es.

Las maquinas de estado son buenas herramientas nada mas, pero sin análisis no sirven de nada.

@MaShicO debería preocuparse de recibir todo el string y asegurarse que llega completo. Luego puede buscar las partes que le interese.
 
Última edición:
debería preocuparse de recibir todo el string y asegurarse que llega completo. Luego puede buscar las partes que le interese.
Yo opto por ésta opcion
De todas formas hay bibliotecas libres para hacer el parsing del protocolo http y recuperar los headers y los datos que a vos te interesan.
Tenes todo resuelto, y solo te preocupas por lo que interesa.

Como dice Cosme, si quieres recibir otra cosa, tienes que programar el doble, aparte de que si en algun momento recibes un "ccion" de otro lado, por ejemplo de algun header (dificil, porque esta en ingles, peeero), vas a tener lecturas basura
 
@MaShicO debería preocuparse de recibir todo el string y asegurarse que llega completo. Luego puede buscar las partes que le interese
De hecho eso es lo que trato de hacer, solo busco maneras de seleccionar el dato que me interesa usando las máquinas de estado.

De igual manera los datos se siguen recepcionando hasta encontrar una secuencia "ccion=x" y desactivo las interrupciones.

Lo que dice cosme es totalmente cierto, para otro string necesitaría otro state-machine pero en este caso no es necesario.

Por el momento me encuentro en mina y no poseo mi gateway SIM800F y mostrarles la trama que se recibe. Ni bien bajé estaré dándoles los alcances de mi proyecto el cual ya lo he trabajado en otras ocasiones conectándome a diferentes servidores usando sólo método POST (Hasta ahora no usé GET).
 
Última edición:
Perdón por mi ignorancia: ¿en qué parte del programa revisás si el índice de DataRx_sim se pasa de 499?

Hace bastante ya compré el librito "Debugging" de Agans (Debugging Rules! – Find out what's wrong with anything, fast.) y lo recomiendo para resolver cualquier problema. Las reglas (dichas de memoria) son:
1. Entender el sistema.
2. Hacerlo fallar.
3. (Dejar de pensar y) Observar.
4. Cambiar una cosa por vez.
5. Registrar todo.
6. Dividir el sistema o el problema en partes.
7. Revisar lo obvio (por ej. ¿está enchufado?).
8. Buscar un nuevo punto de vista.
9. Asegurarse de que lo que eliminó la falla es lo que uno hizo.

Con respecto al punto 2, ¿podés crear tramas de prueba y mandarlas al microcontrolador?
 
Con respecto al punto 2, ¿podés crear tramas de prueba y mandarlas al microcontrolador?
Eso es lo que ya hice y comenté estimado, enviando tramas pequeñas como prueba funciona correctamente, enviando tramas más extensas es en donde no selecciona mis datos que yo quiero.

De todos modos les mandaré la trama que recibe el uC cuando este trabajando con el SIM800F. (y)
 
¿Lo del índice cómo es?
Aparte: ¿ya hiciste lo de subir y bajar una pata libre del microcontrolador en distintas partes del código, para saber qué se está ejecutando (por qué estados va pasando)?
 
Hola y feliz año nuevo a todos ! 🥳

¿Lo del índice cómo es?
Inicialmente en el primer post en donde subí el código si de tas cuenta lo del índice no lo tomaba mucha atención ya que la información que recibía no superaría los 500 bytes ( DataRx_sim[point] = Data_Rx ) y dentro del while, al final, simplemente lo limpiaba.

Hoy que ya me encuentro en casa estuve revisando todo nuevamente, monté el circuito y realicé cambios en el vector de interrupción, precisamente con el índice "point".

Me conecté al servidor mediante el módulo GSM y WalaA ! Funciona de maravilla !!!

Estaré subiendo imágenes ...

Gracias a cosme, al doc, gracias a todos por sus acotaciones y sugerencias, he seguido atentamente y tomado todas en cuenta.😄
 
Por favor, en lo posible no abandones éste post, y coloca toda la informacion posible. Todos los usuarios actuales y futuros te lo agradeceran
Hola DJ, de hecho siempre estoy leyendo el foro.

A ver con respecto al proyecto, ya lo describí líneas arriba cómo funcionaría y ahora les traigo imágenes de ellas.
Para acotar algunas cosas con respecto al módulo GSM SIM800F, he tenido que buscar toda la información necesaria con respecto a los comandos AT y cuáles usar para establecer una conexión mediante el protocolo TCP.

Otro punto a tener en cuenta es obtener el método POST correcto para cada servidor. Por qué digo para cada servidor ? - Porque inicialmente cuando empecé con esto de IoT realizaba el envío de datos hacia ...idots(no digito el nombre completo, no sé si este permitido, pero es una página de IoT conocida) y su POST es de la siguiente forma:

POST /api/v1.6/devices/micasa/?token=BBFF-71d3e1a88bc313612755a54763d42bb42e1 HTTP/1.1<LF>Host: things.ubidots.com<LF>Content-Type: application/json<LF>Content-Length: Datalen<LF><LF>

y enseguida enviar el formato Json el cual contiene la información de algún sensor :

{"ApiLabel1": valor}

Si se dan cuenta toda esa información del POST contiene una "API"-su versión de la misma, una etiqueta en este caso "micasa", contiene un "Token".

Ahora qué pasaba con aquellos servidores que no poseen toda esta información ? Qué pasaba con aquellos que necesitaban realizar una dashboard propia pero sin tanto rollo?

La verdad de la milanesa es que si se puede establecer una comunicación sin poseer un "token" o una "api" solo se tiene que acoplar al que esta completo. Con el GET es igual, solo que el rollo es procesar toda la información que a uno le interese.

Cómo formar y enviar toda esta información con un uC ? Esa ya es otra historia pero si alguien esta trabajando en algo similar con gusto puedo ayudarlo.

Éste es el menu del dashboard, posee información del usuario y esta la opción de "Acción" el cual permite realizar el "corte" y "reconexíon" de la energía eléctrica. Asimismo con el uso del post se envía la información y ésta se almacena como se muestra en la imagen:
Menu.png
Lista de consumos (enviados por POST):
Lista_Consumos.png
Opción de corte y reconexión, al seleccionar corte el servidor actualiza la data "accion=0" y al seleccionar reconexión la data se actualiza a "acción=1"
Corte_Reconexion_.png
Envío de comandos AT para conectarme a la red GPRS y establecer una comunicación con el servidor mediante el protocolo TCP:
Comandos_AT.png
Después de esto establezco la conexión con el server, realizo la petición GET y me envía toda la trama que tengo que procesar, del cual solo me interesa la "data" que indico en la imagen.
Data_GET.png
Disculpen el desorden, solo para terminar, con un encendido y apagado cuando recibo la data.
accion=0
accion=0.jpg

accion=1
accion=1.jpg

Edit:
Por última la modificación del código en el vector de interrupción para seleccionar la data de interés:
C:
ISR(USART_RX_vect){
    
    Data_Rx = (char)UDR0;
    
    switch(Receive){
        
        case Read_normal:
                DataRx_sim[point] = Data_Rx;
                point++;
                if(point >150){
                    
                    Receive = Read_C;
                    point++;
                
                }
                            
                else
                {
                }
                    
                    
        break;
        
        
        case Read_C:
            DataRx_sim[point] = Data_Rx;
        
                if (Data_Rx == 'c')
                {
                    Receive = Read_I;
                    point++;
                }
                else
                {
                    DataRx_sim[point] = Data_Rx;
                    point=0;
                }
                
        break;
        
        
         case Read_I:
            DataRx_sim[point] = Data_Rx;
                if (Data_Rx  =='i')
                {
                    Receive = Read_O;
                    point++;
                }
                else
                {
                    DataRx_sim[point] = Data_Rx;
                    point=0;
                }
        break;
        
        
        case Read_O:
            DataRx_sim[point] = Data_Rx;
                if (Data_Rx  =='o')
                {
                    Receive = Read_N;
                    point++;
                }
                else
                {
                    DataRx_sim[point] = Data_Rx;
                    point=0;
                
                }
        break;
        
        
        case Read_N:
            DataRx_sim[point] = Data_Rx;
                if (Data_Rx  =='n')
                {
                    Receive = captura_data;
                    point++;
                }
                else
                {
                    DataRx_sim[point] = Data_Rx;
                    point=0;
                    Receive = Read_normal;
                }
        break;
        
        case captura_data:
            DataRx_sim[point] = Data_Rx;
                if (Data_Rx ==',')
                {   
                    point=0;
                    Receive=Read_normal;
                    Flag=true;
                    UCSR0B &= ~(1<<RXCIE0);     //Desactivo interrupciones
                    
                }
                else
                {
                    DataRx_sim[point]=Data_Rx;
                    point++;
                    
                }
                
        break;
        
        
    }
}

Saludos desde Lima/Perú - país del rico ceviche 😬
 
Última edición:
Arriba