⬆️ ⬇️

Working with a GSM module using the example of SIM900D

Not long ago, a friend offered me a job related to creating firmware for a microcontroller, which was supposed to communicate with the server using the GSM-module SIM900D . Previously, I didn’t deal with programming microcontrollers, and I also programmed with C for the last time in student days, but curiosity outweighed me and I set to work. Documentation for this piece of hardware is present on the Internet, but no good examples of working with TCP / IP were found in the code. There was nothing left but to lay down the documentation, stock up on cigarettes and tea and start maneuvering between the rakes. And there was a lot of rake. Actually, that's why I wrote this article - so that it would be easier for others.



Further there will be a lot of AT commands, not too much code and a lot of letters.



What was needed



It was necessary to write code that could initialize the GSM module, establish a connection with the server, receive and send arbitrary data, check the connection status and work without failures. And also be compact enough to fit in the limited memory of the microcontroller and leave space for the main functionality and a little more to spare.



What happened in the end



It turned out the code in C, which can all that was needed. Because of the requirements of compactness, it was necessary to parse the answers and generate the lines with the help of my code, which is even a shame to show honest people. Therefore, I recommend everyone to use regular expressions for these purposes. I am also going to translate my code into a lightweight regular expression engine, but after creating a fully functional firmware.

')

The code requires functions / macros for working with a serial port, as well as the presence of memset and memcpy functions. So it can be transferred with relative ease to another platform without hooking a bunch of libraries along the way.



And how does it look?



Programming and testing was carried out under Windows 7. The code obtained as a result became the main material for this article. I will not give the code completely and comment it, but instead I will show the algorithm for setting up and working with the GSM module.



Functions required by the code:



The functions that the code provides:



How to work with serial port



It is quite simple. Under the target microcontroller, there are macros for sending / receiving data via USART , but since it is easier to debug such code from a stationary computer, I was provided with a bundle of USB <-> USART adapter and GSM module. It only remained to learn how to work with a serial port under Windows. It turned out to be simple. In short, the serial port is represented in the OS as a regular file, information transfer is performed by the ReadFile and WriteFile functions . You only need to set some parameters using the SetCommTimeouts and SetCommState functions .



Here is the port initialization function:

 uint16_t init_serial_port(char *port_name) { COMMTIMEOUTS timeouts; DCB parameters; int result; serial_port_handle = CreateFile(port_name, // "\\\\.\\COMx" GENERIC_READ | GENERIC_WRITE, 0, //         NULL, OPEN_EXISTING, 0, NULL); if (serial_port_handle == INVALID_HANDLE_VALUE) { printf("Error opening a serial port!\n"); return 1; } //        timeouts.ReadIntervalTimeout = 100; //          timeouts.ReadTotalTimeoutMultiplier = 0; //     ,      //   timeouts.ReadTotalTimeoutConstant = 1000; //   ,      ,     . timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 1000; result = SetCommTimeouts(serial_port_handle, &timeouts); if (result == 0) { printf("Error setting timeouts for serial port!\n"); close_serial_port(); return 1; } //        -   // ,   , 1 -. memset(¶meters,0,sizeof(parameters)); parameters.DCBlength = sizeof(DCB); GetCommState(serial_port_handle, &parameters); parameters.BaudRate = (DWORD)BAUD_RATE; parameters.ByteSize = 8; parameters.Parity = NOPARITY; parameters.StopBits = ONESTOPBIT; parameters.fAbortOnError = TRUE; parameters.fDtrControl = DTR_CONTROL_DISABLE; parameters.fRtsControl = RTS_CONTROL_DISABLE; parameters.fBinary = TRUE; parameters.fParity = FALSE; parameters.fOutX = FALSE; parameters.fInX = FALSE; parameters.XonChar = (uint8_t)0x00; parameters.XoffChar = (uint8_t)0xff; parameters.fErrorChar = FALSE; parameters.fNull = FALSE; parameters.fOutxCtsFlow = FALSE; parameters.fOutxDsrFlow = FALSE; parameters.XonLim = 128; parameters.XoffLim = 128; result = SetCommState(serial_port_handle, &parameters); if (result == 0) { printf("Error setting serial port parameters!\n"); close_serial_port(); return 1; } return 0; } 




How does the communication with the GSM-module



After the serial port is configured, AT commands can be sent to it. The first command must be the sequence "AT\r" , which allows the module to automatically adjust the baud rate on the serial port. The answer that can be obtained after this from the port will look like "AT\r\r\nOK\r\n" .



The command is a simple string of ASCII characters. In order for the command to accept the module, at the end of the command you need to put the carriage transfer character "\r" . In response, the module will transmit a string of characters consisting of two parts — a command to which the module responds with a response separated by the "\ r \ r \ n" characters, terminated by the characters "\r\n" . To make it easier to parse the answers, I created a macro that sets a pointer to the beginning of the response in the receive buffer. If you want to output the answer to the console, you need to add a null character to the end of the received message.



 void at_send(char *cmd, uint16_t size) { uint16_t result; cmd[size - 1] = '\r'; result = puts_serial(cmd, size); return; } uint16_t at_recv(uint8_t *buffer, uint16_t size) { uint16_t result; result = gets_serial(buffer, size); return result; } 


Something like this and support functions for sending commands and receiving a response.



Module initialization



The largest function in the code is responsible for configuring the module. During initialization, many AT commands are sent. I will describe them in the order of sending the module. I do not specifically list the arguments and answer options in detail, for they can be found in the documentation.



After executing these commands, the module will be ready for operation. Well or not. Then how lucky.



Installation and disconnection



Creating a connection is done with the command "AT+CIPSTART=index,"mode","address","port"" .



I note that when using the UDP protocol, the datagrams will be sent and received only from one address by default. In order to use UDP to the fullest and send / receive data from any addresses, you can use the so-called extended UDP mode, configured by the command "AT+CIPUDPMODE" . For details, refer to the documentation.



In response to the command, you can get several answers. If everything is good, then after the standard "OK" after a short period of time, you can get one of three answers:



You can break the connection with the command "AT+CIPCLOSE=index" . To disconnect all connections and deactivate the GPRS interface, use the "AT+CIPSHUT" .



Data transfer



The data is transmitted by the command "AT+CIPSEND=index,length" , where index indicates the connection over which data is to be sent, and length specifies the length of the data packet. By the way, you can find the MTU for each connection using the command "AT+CIPSEND=?" .



If all is well, the module in response to the command will display the ">" prompt, after which you need to send data to the serial port. As soon as the module receives a number of bytes equal to length , it will say something like "index,SEND OK" . In general, you can not use the length parameter, however, in this case, the end of the data packet should be indicated explicitly with the 0x1A symbol, in the terminal the combination Ctrl + Z. This option is obviously not suitable for transmitting arbitrary data.



As you can see, data transfer is not a very complicated process. Therefore, we turn to the most interesting - data reception.



Receiving data



As soon as the GSM module receives data, it signals this by sending a string like "+CIPRXGET:1,index\r\n" to the serial port. I honestly do not know what unit means, because this function of the module is documented rather weakly, but in my case it appears in all messages about the reception of a packet.



I wasn’t happy about the thought of having to monitor the module’s messages one way or another. However, after playing a little with the debugger, I found out that the module does not send any other asynchronous messages, and also that after executing any AT command, this message appears at the beginning of the buffer. Since I made a macro to separate the response from the command by searching for the substring "\r\r\n" , this did not bother me in any way. So the data reception function was implemented quite simply.



So, you can receive data with the command "AT+CIPRXGET=2,index,length" . Two means reception mode, in this case the bytes are simply poured into the serial port. You can also set to receive data in the form of HEX-text, apparently for the sake of preventing conflicts with software flow control . I did not need this, because I do not use flow control at all. The length parameter specifies the size of the data packet that we wish to receive at one time.



In response, we get something like "+CIPRXGET:2,index,received,excess\r\n__DATA__\r\nOK\r\n" . The received field will contain the number of bytes in the __DATA__ data __DATA__ , and the excess field will contain the number of bytes waiting for their turn in the module's buffer. So if the received field is zero, you can declare with a clear conscience that there is nothing to receive. Actually, using this, I implemented a non-blocking function for receiving data.



Finally



I strongly recommend before writing code to get comfortable in AT-commands using PuTTY , which works fine with the serial port.



I hope the information from this article will help someone to write the code for your SIM900. It is possible that the principles of working with the GSM module described above can also be applied to modules of other models, and, possibly, manufacturers.

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



All Articles