A long long time ago, in galaxy far far away (no, scratch that bit), a standard evolved for talking to modems, well, specifically Hayes modems, but it was widely adopted.
It works over a serial link by sending commands starting AT for (attention). Now, I am pretty sure they were reasonably consistent, back in the day, but like many things this standard has evolved and been bastardised.
Now, some AT commands are consistent. You send AT and some options, and get some data followed by OK. Good.
There is a reasonably consistent format for commands and settings and queries, mostly. There are then AT+ functions which are used for a lot of the mobile stuff.
But it is far from consistent. The AT+CIPSEND, after the > sends "SEND OK" not "SENT OK" or "OK", and AT+CIPSHUT sends "SHUT OK". Why are these not just "OK" like the rest? I am also pretty sure I had one case of a response with no OK (but cannot find it now).
Then we have the asynchronous messages from the device. Most start with a +, like "+CREG: 0,1", so can be recognised if you are expecting another response. But some are just text like "SMS Ready". These smack of debug messages left in the code.
Half the interface was clearly designed for a person sat at a terminal typing stuff, and the other half was designed for talking to a machine!
But when you get to some of the higher level functions, like establishing a UDP "connection", it gets even more special. For a start, you look for "CONNECT OK" not simply "OK", and mostly at that point it seems to avoid sending asynchronous messages (though I cannot be sure). Some still can be sent, like "+PDP: DEACT" when it loses the connection. What you do get though, with no prompt, header, or indication of length is the raw binary content of any UDP packets you receive on the connection. So send a packet containing "+PDP: DEACT" and I'll think the connection has dropped!
What would have been sensible would have been a line something like "+DATA=N" and then N bytes of data, or some such, but no, it is simply the raw UDP packet.
Then we have some interesting little bugs and discrepancies. One that took a while to work but was that the length of UDP data I can send is limited. Not to 65535 which is what IP limits to, but to 1472 which is the available UDP in a 1500 byte IPv4 packet. Except it is not. It is actually 1460, any more says "ERROR". Oh, and it won't allow me to send a zero length UDP packet - no idea why, and as NAT is often used this is something I may want to send as a keep alive. I ended up having to send one byte.
But there is an even more special feature. When you receive a UDP packet, as I say, it arrives as the raw binary data via serial. I am relieved to see no mangling of any bytes, which is good. Except it truncates the last byte of the message. Yep, I have to send an extra dummy byte on my packets to the device. WTF?
I could save myself from a lot of this by coding PPP, pretending to dial on an old style modem and talking PPP which the device fakes and converts to packet data. This is a serious consideration, to be honest.
However, after a lot of messing about, my GPS trackers are sending (and receiving, when needed) tracking data over UDP over mobile - yay!
P.S. I may have maligned the SIM800 as incoming UDP looks like it may be my bug losing a byte, and there is an option for a header with length for incoming UDP. But the Quectel M95 does look a bit nicer.