📜 ⬆️ ⬇️

GSM module control with AVR

Project idea: to design a device based on an AVR microcontroller to control a ready-made GSM module (I chose the TC35 module from SIEMENS, but you can use any other if you are using RS232 serial communication). The device should be compact, minimally simple and reliable.

Sending a pre-stored message to the specified number should be performed after pressing the button. In total, there were 6 buttons to send to 6 different numbers. 3 LEDs (Ready, Send, Error) were selected to indicate the process, but later an alphanumeric LCD 16x2 was added (more for debugging the device than for normal use).

The whole thing was designed on a Pinboard II (Rev 2) board with a standard processor module on the ATmega16. On the finished device, the circuit was slightly different (and the microcontroller was used by the ATmega8). The program was written in AVR Studio 4.19. The project used various header files (#include) to switch between the Pinboard and the finished device.
')
General system layout:

image

For the controller, such a scarf was cut out:

image

There was a lot of time, so later I ordered the boards from the Chinese:

image

And when the hardware was finished, the programming process followed. Everything is written in C under AVR Studio 4.19. I post the complete project at the end of the article if anyone is interested in the complete code. But for now, let's talk about communication with the GSM module.

A complete list of AT commands is for each module in its documentation. But sending a message occurs by several teams.

//: AT+CMGF=1<enter> // : OK<enter> 

Puts the module into text mode. I have not yet mastered the digital mode (until it was necessary). The module’s response was not used at the initial stages of the project. But then (when the descrambler of commands was written) served as a condition for the continuation of sending or a protocol error message. Go ahead:

 : AT+CMGS=( )<enter>  : >  : Hello, GSM module!<ctrl-Z>  : +CMGS: 62 OK 

After typing the message, you need to send not Enter (0x0D) but CTRL-Z (0x1A). The module response after sending contains the sequence number of the message to be sent.

To send commands to the module and get answers, I used two ring buffers with incoming and outgoing indices.

For better clarity of the code I will give the headers:

 #define BUF_SIZE 128 #define BUF_MASK (BUF_SIZE-1) #define IN_BUF_SIZE 64 #define IN_BUF_MASK (IN_BUF_SIZE-1) volatile char buffer[BUF_SIZE]=""; volatile char inbuf[IN_BUF_SIZE]="$"; //inner buffer of USART volatile uint8_t ind_in=0, ind_out=0, rxind_out=0, rxind_in=0, mess = 0; 

Writing strings or individual characters to the buffer was done using normal functions:

 //sending RS232 with interupt void SendByte(char byte) { buffer[ind_in++] = byte; ind_in &= BUF_MASK; } void SendStr(char *string) { while (*string!='\n') //check if End { SendByte(*string); string++; } } 

And sending is made through an interrupt handler:

 //Sending data from buffer ISR (USART_UDRE_vect) { UDR = buffer[ind_out++]; //   ind_out &= BUF_MASK; //    if (ind_in == ind_out) //    { UCSRB &= ~(1<<UDRIE); //disable instrupt UDR empty UCSRB |= (1<<RXEN); //     } sei (); } 

Now, to send, you need to write the necessary command to the buffer (including the final \ n character), and then enable interrupts to empty the Send Register (UDR):

 SendStr("AT+CMGF=1\n"); SendByte(CR); // <Enter> (0x0D) UCSRB &= ~(1<<RXEN); //     UCSRB |= (1<<UDRIE); //     

While sending is in progress, you can send a message to the LCD or just wait (delay).
You cannot write to the buffer at this time. Experienced to find that the module does not have time to process a solid then commands. A stop occurs when the buffer is empty (incoming and outgoing indices are equal).

And so we send the message. Depending on the button pressed (in the main loop I scan the port), a message is sent:

 while (1) { tmp = PINC; switch (tmp) { case 1: send_sms(0,NUM1); break; case 2: send_sms(0,NUM2); break; case 3: send_sms(0,NUM3); break; //  ... default: break; } Ready_Snd (); //     } 

I send the selected message number (I have 2 types) and a phone number to the sending function.
You can even send a command to the call with the same AT commands. It all depends on the required function.

Now about receiving commands from the module.

The module sends many commands. For example, OK, RING, ERROR ...
Sometimes it is necessary that when receiving a command the controller can recognize it and perform some kind of action. For example, received an incoming call. The module then sends to the controller:

 RING RING RING 

Depending on the module settings, it can also send the number of the caller. While there is no program, the controller will not be able to do anything with it (at best) or (at worst) it will do something wrong, or even completely freeze (cannot exit the interrupt).

Requirements for processing code:
1. The minimum amount of time to save the received commands. There should be no delays in the interrupt program. Then we will do anything with the resulting array.

2. Saving all received commands in one buffer. To separate the individual will use the $ symbol.

3. Recognize common commands in numeric codes. For example, OK will be 1, ERROR - 4, RING - 2.

I will cite the headings from the previous article with amendments:

 #define BUF_SIZE 128 //  #define BUF_MASK (BUF_SIZE-1) #define IN_BUF_SIZE 64 //  #define IN_BUF_MASK (IN_BUF_SIZE-1) volatile char buffer[BUF_SIZE]=""; volatile char inbuf[IN_BUF_SIZE]="$"; //inner buffer of USART volatile uint8_t ind_in=0, ind_out=0, rxind_out=0, rxind_in=0, mess = 0; volatile uint8_t com_detect=0; //     #define TIMEOUT 100 //        

Writing a data reception interrupt handler:

 //recieving Data from RS232 ISR (USART_RXC_vect) { uint8_t tmp; tmp = UDR; if (tmp == 0x0D) //   - <enter> { mess++; //one more message inbuf[rxind_in++] = '$'; //    rxind_in &= IN_BUF_MASK; } else { if (tmp != 0x0A) //     { inbuf[rxind_in++] = tmp; //   rxind_in &= IN_BUF_MASK; } } sei (); } 

Now we have all the commands written in the buffer. You can check the mess variable in your free time, and if it is not zero, run the command handler. In the project itself, commands for the LCD screen were added. Here I will miss them as useless.

 void rx_check_in (void) { uint8_t count=0; com_detect = 0; //  (    ) while (1) { if (inbuf[rxind_out] != '$') //   () { com_detect ^= inbuf[rxind_out++]; // XOR   rxind_out &= IN_BUF_MASK; count++; //,     } else { rxind_out++; rxind_out &= IN_BUF_MASK; code_com (count); //!!   -   break; } } } 

We pass the received symbols through the meat grinder. We do XOR operation. Thus we get a unique code (not sure about the uniqueness, but so far has not failed). R ^ I ^ N ^ G will give us 0x12. O ^ K will give 0x04. This code and the number of characters (in the command) are stored in the com_detect (global) and count variables. Now run the handler:

 void code_com (uint8_t count) { switch (com_detect) { case (0x12): if (count == 4) com_detect = 2; break; //R^I^N^G case (0x58): if (count == 5) com_detect = 3; break; //ERROR case (0x04): if (count == 2) com_detect = 1; break; //OK case (0x5C): if (count == 3) com_detect = 4; break; //ATI default: com_detect = 0; } } 

Recognized the team. I entered the number of characters for reliability in case the code in the long XOR command matches. Recognizable commands can be added. It is only necessary to calculate (or macro) the XOR code of the desired command and assign it a number.

Now in com_detect we got the command. Now the device can respond by SMS to the received call:

 while (1) { if (mess != 0) //if we have mess in buffer { // code mess--; //minus one rx_check_in (); //   if (com_detect == 2) //   RING ( 2) { //  //     (OK) if (!send_sms (1,NUM0)) ErrMes (); //      OK } //      com_detect = 0; //  } 

So you can handle different received commands.

The bottom line: the device can send a message to the phone and can respond to various commands from the GSM module.


Thanks for attention.

I post the finished project for Pinboard II as an example.

Source: https://habr.com/ru/post/256349/


All Articles