I have an UART communication between AVR (Atmega16) and GSM module. Normally, everything seems to work correctly. Problem is when I send SMS (For example SMS:"H=0,95") to the GSM module, in program there is SMS response after every received SMS. Response (SMS) is sent to mobile phone correctly, but overwritting with random numbers of other variables occurs. When I tried to debug it using Terminal, I suppose it is caused by receiving lot of bytes in UART (variable ser_rec probably corrupts), but I can't find where in program it could happen. During ISR handling there is a condition which should avoid this problem, when maximum number of bytes are written into buffer, buffer is after cleared.
#include<avr/io.h> // Header file for basic avr input/output
//#define F_CPU 8000000UL
#include<util/delay.h> // header file for delay generation
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <util/atomic.h>
#include "rf22.h"
#include "lcd.h"
# define USART_BAUDRATE 9600
# define BAUD_PRESCALE ((( F_CPU / ( USART_BAUDRATE * 16UL))) - 1)
volatile unsigned char INTERRUPT=0;
volatile unsigned char DECODE_SMS=0;
uint16_t EEMEM HumidityLowRange=0;
uint16_t EEMEM HumidityHighRange=100;
int16_t EEMEM TemperatureLowRange=-40;
int16_t EEMEM TemperatureHighRange=80;
uint8_t EEMEM SMS_tel_number[14]="+421911243160";
unsigned char sms_index=0;
volatile int out=0;
int Temperature_low_range=-40;
int Temperature_high_range=80;
int Humidity_low_range=0;
int Humidity_high_range=100;
char tel_num[14];
//debugging tel num changing
unsigned char SMS_alert=0; //shows if SMS has been already sent
typedef struct
{
int Humidity_value;
int Temperature_value;
} BH;
BH BeeHive[5]; //for all BeeHives, ambient sensor included (considered as BeeHive without Heater)
void UART_Transmit_char( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSRA & (1<<UDRE)) );
/* Put data into buffer, sends the data */
UDR = data;
}
void UART_Transmit_string( char string[] )
{
int i=0;
while ( string[i] > 0)
{
UART_Transmit_char(string[i]);
i++;
}
}
unsigned char rx_tmp[40];
volatile char ser_rec[95]="";
volatile unsigned char a=0;
void clear_sec_rec()
{
int i=0;
while(i<sizeof(ser_rec))
{
ser_rec[i]=0;
i++;
}
i=0;
a=0;
}
char sms_index1()
{
int i=0;
char index=0;
while( i<(sizeof(ser_rec)-4))
{
i++; //check if gsm sends SM",
if(ser_rec[i]==((char) 'S'))
{
i++;
if(ser_rec[i]==((char) 'M'))
{
i++;
if(ser_rec[i]==((char) '"'))
{
i++;
if(ser_rec[i]==((char) ','))
{
i++;
index=ser_rec[i]; //return index
}
}
}
}
}//returns it
if(index) clear_sec_rec(); //clears the ser_rec buffer if there was index
return index;
}
void sms_decode()
{
int i=0;
char TemporaryString[95];
int Humidity_low_range_temp;
int Humidity_high_range_temp;
int Temperature_low_range_temp;
int Temperature_high_range_temp;
char SMS_text[80];
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
while( i<sizeof(ser_rec))
{
if(ser_rec[i]==((char) 'H'))
{
if(i<(sizeof(ser_rec)-4)) i++; //go forward only when there is a place for numbers =x,y, therefore number 4
if(ser_rec[i]==((char) '='))
{
i++;
memcpy(TemporaryString, &ser_rec[i], ((strlen(ser_rec))-i));
TemporaryString[(strlen(ser_rec)-i)]='\0';
if((sscanf(TemporaryString,"%d,%d",&Humidity_low_range_temp,&Humidity_high_range_temp)==2))
{
if(Humidity_low_range_temp<Humidity_high_range_temp)
{
if((Humidity_low_range_temp>=0)&&(Humidity_high_range_temp<=100))
{
Humidity_high_range=Humidity_high_range_temp;
Humidity_low_range=Humidity_low_range_temp;
//and finally send the notification about changed limits
clear_sec_rec();
UART_Transmit_string("AT+CMGS=");
_delay_ms(50);
UART_Transmit_char(34);
_delay_ms(50);
UART_Transmit_string(tel_num);//the phone number
_delay_ms(50);
UART_Transmit_char(34);
_delay_ms(50);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec(); //clears the buffer
sprintf(SMS_text,"Humidity range is set: Low=%d%% High=%d%%\r",Humidity_low_range, Humidity_high_range);
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
eeprom_update_word(&HumidityHighRange,Humidity_high_range);
eeprom_update_word(&HumidityLowRange,Humidity_low_range);
}
}
}
}
}
i++;
}
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
i=0;
//Temperature range set
while( i<(sizeof(ser_rec)-4))
{
if(ser_rec[i]==((char) 'T'))
{
i++;
if(ser_rec[i]==((char) '='))
{
if(i<(sizeof(ser_rec)-1)) i++;
memcpy(TemporaryString, &ser_rec[i], ((strlen(ser_rec))-i));
if((sscanf(TemporaryString,"%d,%d",&Temperature_low_range_temp,&Temperature_high_range_temp))==2)
{
if(Temperature_low_range_temp<Temperature_high_range_temp)
{
if((Temperature_low_range_temp>=-40)&&(Temperature_high_range_temp<=80))
{
Temperature_high_range=Temperature_high_range_temp;
Temperature_low_range=Temperature_low_range_temp;
//and finally send the notification about changed limits
clear_sec_rec();//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
_delay_ms(50);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec();//clears the buffer
sprintf(SMS_text,"Temperature range is set: Low=%d C High=%d C\r",Temperature_low_range, Temperature_high_range);
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
eeprom_update_word(&TemperatureHighRange,Temperature_high_range);
eeprom_update_word(&TemperatureLowRange,Temperature_low_range);
}
}
}
}
}
i++;
}
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
i=0;
//Info SMS
while( i<(sizeof(ser_rec)-2)) //go forward only when there is a place for at least 3 characters
{
if(ser_rec[i]==((char) 'I'))
{
i++;
// b1=1;
if(ser_rec[i]==((char) 'N'))
{
i++;
// b1=2;
if(ser_rec[i]==((char) 'F'))
{
// b1=3;
i++;
if(ser_rec[i]==((char) 'O'))
{
//send info about all sensor values
clear_sec_rec();
_delay_ms(200);//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_char('\r');
_delay_ms(500);
clear_sec_rec();
_delay_ms(200);//clear the buffer
//T2=%dC H0=%d%%\nH3=%d%% T3=%dC T0=%dC\nH4=%d%% T4=%dC add , BeeHive[2].Temperature_value,BeeHive[0].Humidity_value, BeeHive[3].Humidity_value,BeeHive[3].Temperature_value, BeeHive[0].Temperature_value,BeeHive[4].Humidity_value, BeeHive[4].Temperature_value
sprintf(SMS_text,"H1=%d%% T1=%dC Amb:\nH2=%d%% T2=%dC H0=%d%%\nH3=%d%% T3=%dC T0=%dC\nH4=%d%% T4=%dC",BeeHive[1].Humidity_value, BeeHive[1].Temperature_value,BeeHive[2].Humidity_value, BeeHive[2].Temperature_value,BeeHive[0].Humidity_value, BeeHive[3].Humidity_value,BeeHive[3].Temperature_value, BeeHive[0].Temperature_value,BeeHive[4].Humidity_value, BeeHive[4].Temperature_value);
// c1=strlen(SMS_text);
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A); //CTRL-Z - required by datasheet
_delay_ms(50);
// UART_Transmit_char('\n');
// d1=strlen(ser_rec);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
}
}
}
}
i++;
}
//Telephone number changing SMS
//clear buffers
i=0;
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
while( i<(sizeof(ser_rec)-15))
{
if(ser_rec[i]==((char) 'T'))
{
i++; //go forward only when there is a place for 16 characters
if(ser_rec[i]==((char) 'E'))
{
i++;
if(ser_rec[i]==((char) 'L'))
{
i++;
if(ser_rec[i]==((char) ':'))
{
i++;
memcpy(TemporaryString, &ser_rec[i], 13);
TemporaryString[13]='\0';
if((strlen(TemporaryString)==strlen(tel_num))&&(strlen(tel_num)==13))
{
memcpy(tel_num,&TemporaryString[0],strlen(TemporaryString));
//send info about changing number
clear_sec_rec();
_delay_ms(200);//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec();
_delay_ms(200);//clear the buffer
sprintf(SMS_text,"This is my new telephone number. BeeHiveMonitor");
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
eeprom_update_block((void*)&tel_num, (const void*)&SMS_tel_number, 13);
}
}
}
}
}
i++;
}
//Alert turn on SMS
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
i=0;
//Info SMS
while( i<sizeof(ser_rec))
{
if(ser_rec[i]==((char) 'K'))
{
if(i<(sizeof(ser_rec))) i++;
if(ser_rec[i]==((char) 'K'))
{
SMS_alert=0;
//send info alert turning on
clear_sec_rec();
_delay_ms(200);//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec();
_delay_ms(200);//clear the buffer
UART_Transmit_string("Alerts are turned on\r");
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
}
}
i++;
}
i=0;
clear_sec_rec(); //clears the buffer
if(sms_index!=0)//deleting SMS
{
UART_Transmit_string("AT+CMGD=");
_delay_ms(50);
UART_Transmit_string("1,4\r"); //clear all SMSs in inbox
_delay_ms(50);
}
clear_sec_rec();
}
int main(void)
{
Humidity_high_range = eeprom_read_word(&HumidityHighRange); //get init values of previously set limits both Humidity and Temperature
Humidity_low_range = eeprom_read_word(&HumidityLowRange);
Temperature_high_range = eeprom_read_word(&TemperatureHighRange);
Temperature_low_range = eeprom_read_word(&TemperatureLowRange);
eeprom_read_block((void*)&tel_num, (const void*)&SMS_tel_number, 13);
sei();
char Text[80]; //character array for writing on the display
char SMS_text[80]; // character array for writing via SMS
char ATCommand[20];
int flag=0;
MCUCSR = (1<<JTD); // no -O0, interrupts disabled
MCUCSR = (1<<JTD); // do not use read-modify-write
DDRC=0xFF;
DDRD=0b11111010;
DDRB=0b10111111;
PORTB = PORTB | (1 << PB3); // Reset pin of SIM800L
_delay_ms(200);
DDRA=0xFF;
PORTC = PORTC | (1 << PC7); // turn on LCD backlight
//UART INIT
UCSRB = (1 << RXEN ) | (1 << TXEN );
UCSRC = (1 << URSEL ) | (1 << UCSZ0 ) | (1 << UCSZ1 );
UBRRH = (unsigned char)( BAUD_PRESCALE >> 8);
UBRRL = (unsigned char) BAUD_PRESCALE&0xFF ;
UCSRB |= (1 << RXCIE );
lcd_init(LCD_DISP_ON); // display initialization
rf22_init();
rf22_setfreq(RF22FREQ(869.545));
rf22_rxmode();
//GSM init 3 times AT
UART_Transmit_string("ATE0\r");
_delay_ms(200);
UART_Transmit_string("AT\r");
_delay_ms(200);
UART_Transmit_string("AT\r");
_delay_ms(200);
UART_Transmit_string("AT\r");
_delay_ms(200);
clear_sec_rec();
UART_Transmit_string("AT+CMGF=1\r");//set text mode
// while(!(strstr((char*)ser_rec,"OK")));
_delay_ms(200);
// while(!(strstr((char*)ser_rec,"OK")));
clear_sec_rec();
UART_Transmit_string("AT+CNMI=1,1,0,0,0\r");
// while(!(strstr((char*)ser_rec,"OK")));
_delay_ms(200);
clear_sec_rec();
UART_Transmit_string("AT+CMGD=1,4\r"); //delete all SMS in inbox
_delay_ms(100);
// UART_Transmit_string("1,4\r");
// while(!(strstr((char*)ser_rec,"OK")));
_delay_ms(200);
clear_sec_rec();
while(1)
{
if((INTERRUPT==1)&&(DECODE_SMS==0))
{
sms_index=sms_index1();
INTERRUPT=0;
if(sms_index!=0)
{
//if index exists - ready for decoding SMS
DECODE_SMS=1;
sprintf(ATCommand,"AT+CMGR=%c\r",sms_index);
UART_Transmit_string(ATCommand); //request for reading SMS with sms index
}
}
if((INTERRUPT==1)&&(DECODE_SMS==1))//decodes the sms
{
sms_decode();
DECODE_SMS=0; //finally clear both flags
INTERRUPT=0;
}
for(int x=0;x<5;++x)
{
rf22_getpacket(rx_tmp);
_delay_ms(10);
sscanf(rx_tmp,"_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d",&BeeHive[0].Humidity_value, &BeeHive[0].Temperature_value,&BeeHive[1].Humidity_value, &BeeHive[1].Temperature_value,&BeeHive[2].Humidity_value, &BeeHive[2].Temperature_value,&BeeHive[3].Humidity_value, &BeeHive[3].Temperature_value,&BeeHive[4].Humidity_value, &BeeHive[4].Temperature_value);
_delay_ms(10);
lcd_clrscr();
_delay_ms(10);
lcd_gotoxy(0,0);
sprintf(Text,"H1=%02d T1=%+03d Amb:\nH2=%02d T2=%+03d H0=%02d\nH3=%02d T3=%+03d T0=%+03d\nH4=%02d T4=%+03d",BeeHive[1].Humidity_value, BeeHive[1].Temperature_value,BeeHive[2].Humidity_value, BeeHive[2].Temperature_value,BeeHive[0].Humidity_value, BeeHive[3].Humidity_value,BeeHive[3].Temperature_value, BeeHive[0].Temperature_value,BeeHive[4].Humidity_value, BeeHive[4].Temperature_value);
// sprintf(Text,"a:%d b:%d c:%d d:%d\n tel:%s\n index: %d",a1,b1,c1,d1,tel_num,sms_index);
// sprintf(Text,"HL:%d HH:%d\n tel:%s\n TL:%d TH:%d",Humidity_low_range,Humidity_high_range,tel_num, Temperature_low_range, Temperature_high_range);
// sprintf(Text,"High Limit:%d\nLow Limit:%d",Humidity_high_range, Humidity_low_range);
//UART_Transmit_string("AT+CMGF=1\r");
//_delay_ms(100);
//sprintf(Text,"%s",ser_rec);
//ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
//{
//UART_Transmit_string("AT\r");
//_delay_ms(1000);
//sprintf(Text,"%s",ser_rec);
//}
//
//UART_Transmit_string("AT\r");
//clear_sec_rec();
lcd_puts(Text);
_delay_ms(2000);
}
//SMS alert condition
for(int j=1;j<5;j++)
{
if(!SMS_alert)
{
if(BeeHive[j].Humidity_value>Humidity_high_range || BeeHive[j].Humidity_value<Humidity_low_range || BeeHive[j].Temperature_value>Temperature_high_range ||BeeHive[j].Temperature_value<Temperature_low_range)
{
//sending SMS alert
SMS_alert=1;
UART_Transmit_string("AT+CMGS=");
_delay_ms(100);
UART_Transmit_char(34);
_delay_ms(100);
UART_Transmit_string(tel_num);//the phone number
_delay_ms(100);
UART_Transmit_char(34);
_delay_ms(100);
UART_Transmit_string("\r");
_delay_ms(100);
clear_sec_rec();
sprintf(SMS_text,"Alert: H%d=%d%% T%d=%+d C\r",j,BeeHive[j].Humidity_value,j,BeeHive[j].Temperature_value);
UART_Transmit_string(SMS_text);
_delay_ms(100);
UART_Transmit_char(0x1A);
_delay_ms(100);
clear_sec_rec();
}
}
}
_delay_ms(100);
}
return 0;
}
ISR ( USART_RXC_vect )
{
if(a<(sizeof(ser_rec)))
{
ser_rec[a]=UDR;
if((ser_rec[a]!=0xd)&&(ser_rec[a]!=0xa)) //we do not need these bytes 0xd,0xa
{
a++;
INTERRUPT=1;
}
}
else
{
out=UDR;
clear_sec_rec();
}
}
Caveat: This isn't so much a solution but cleanup on your code so you can find a solution. That's because your code has so much needless complexity it's hard to analyze it.
However, there are some [obvious] bugs ...
Your ISR is racing with your task level
Your ISR sync with task level for your buffer is suspect despite the volatile
.
When you try to access that from task level, you need to disable interrupts for the duration of the task level access.
You should wrap the access in cli/sti
pairs. Do this for any task level access to ser_rec
or a
. And, not just around a single byte fetch within the loop. You should put the cli
as the first thing of the task level function and sti
as the last thing in the function.
And, I'd rename a
to something more descriptive like ser_len
.
Only process data that you know you have
And, at task level (e.g. sms_decode
), your loop limit is sizeof(ser_rec)
, so you are accessing garbage/random values or partial stale values from a previous incoming message.
Instead of:
while (i < sizeof(ser_rec))
You probably want:
while (i < a)
Or, if you do the rename:
while (i < ser_len)
If you do that, the clear_sec_rec
function no longer needs to zero out the buffer, but just reset ser_len
. The zeroing out was an attempt at mitigating the broken loop limit.
The task level needs to "slide" the buffer
That is, if the current incoming record has (e.g.) 8 chars, but ser_len
is (e.g.) 12, task level has to "slide" the buffer to remove the processed record:
memmove(&ser_rec[0],&ser_rec[8],ser_len - 8);
ser_len -= 8;
ISR should not reset the buffer if it overflows
Your ISR is "resetting" the buffer if too many chars come in. This probably breaks the task level processing because you could do the reset in the middle of the processing.
If the buffer/ring fills up, the ISR should set an "overflow" flag that the task level can examine [and just drop the char], rather than yanking the rug out from under task level with the reset.
Use a ring queue
But, you may want to convert the buffer into a ring queue. This is a bit more complex, but allows the flow from ISR to task to be continuous. (i.e.) This would eliminate the need for the ISR to reset the buffer. And, task level would be able to process multiple records without losing any.
Setting an overly large buffer size might help (e.g. char ser_rec[10000]
). But, only do this after fixing the synchronization.
You might even be able to use atomics (e.g. stdatomic.h
on the ring buffer indexes) and obviate the need for cli/sti
in the task [although the latter is slower but simpler].
Eliminate needless complexity
You're using N level if/else
ladders instead of a simple memcmp
:
char
sms_index1()
{
int i = 0;
char index = 0;
#if 0
while (i < (sizeof(ser_rec) - 4)) {
i++; // check if gsm sends SM",
if (ser_rec[i] == ((char) 'S')) {
i++;
if (ser_rec[i] == ((char) 'M')) {
i++;
if (ser_rec[i] == ((char) '"')) {
i++;
if (ser_rec[i] == ((char) ',')) {
i++;
index = ser_rec[i]; // return index
}
}
}
}
} // returns it
#else
while (i < (sizeof(ser_rec) - 4)) {
// return index
if (memcmp(&ser_rec[i],"SM\",",4) == 0) {
i += 4;
index = ser_rec[i];
}
i += 1;
}
#endif
// clears the ser_rec buffer if there was index
if (index)
clear_sec_rec();
return index;
}
Keep your code to 80 chars per line
Breakup long lines at logical points
Change (e.g.):
if (BeeHive[j].Humidity_value > Humidity_high_range || BeeHive[j].Humidity_value < Humidity_low_range || BeeHive[j].Temperature_value > Temperature_high_range || BeeHive[j].Temperature_value < Temperature_low_range)
Into:
if ((BeeHive[j].Humidity_value > Humidity_high_range) ||
(BeeHive[j].Humidity_value < Humidity_low_range) ||
(BeeHive[j].Temperature_value > Temperature_high_range) ||
(BeeHive[j].Temperature_value < Temperature_low_range)) {
Avoid [long] "sidebar" comments in code. Instead put them on a line above. (e.g.)
Change (e.g.):
i++; // go forward only when there is a place for 16 characters
Into:
// go forward only when there is a place for 16 characters
i++;
Use #define/enum
for special values
You're also doing (e.g.) UART_Transmit_char(34);
The 34
is a "hardwired" value. What does it mean/represent? Do something like: #define STARTCHAR 34 // framing char
and then do: UART_Transmit_char(STARTCHAR);
Separate blocks of related/unrelated code with blank lines
Add some blank lines to group things. For example, you do UART_Transmit_char
and then do: _delay_ms(50)
. Add a blank line after the _delay_ms
. Or, better yet create a function that does both and call that function.
Fix broken comments
Some of your comments are incorrect/misplaced:
_delay_ms(200); // clear the buffer
Compile code with warnings enables and fix all warnings
Because I don't have access to AVR files (e.g. avr/interrupt.h
), I can't compile your code. But, you should compile with -Wall -O2
to generate warnings and fix them. And, I suspect you have a few.