Arduino sends SMS messages

This is a code which enables an Arduino board to send SMS messages in PDU format. It was tested with a Siemens C45 mobile phone, but should work also with other models which have a built-in modem and a serial interface.

For those who don't want only to copy a code but also to understand it, I've prepared some explanatory text between the lines. I hope that you will find this little tutorial useful.

Creating an SMS message in PDU format involves a simple rearrangement of the recipient's phone number and a slightly more complex rearrangement of the message text. The rearranged phone number and text are then joined together with a few additional parameters to yield the final PDU message.

The centerpiece of a PDU message is the reduction in size of the original text characters, which are in 8-bit format, to 7 bits by removing the most significant bit (MSB). The MSB of ASCII characters is always a "0" and therefore does not hold any useful information. The resulting 7-bit sequences then need to be rearranged into new 8-bit sequences. This is done by taking the number of bits needed to create a new full 8-bit sequence from the subsequent 7-bit series. This makes it possible to fit 160 characters (maximum length of an ordinary SMS) into just 140 bytes.

 

To get a better idea about all this please read some more detailed documents about this subject, e.g. http://www.gsm-modem.de/sms-pdu-mode.html 

and 

http://archive.sierrawireless.com/resources/AirPrime/GT47-GT48/Application_Notes/Gx4x%20App%20note%20Construction%20of%20SMS%20PDUs.pdf

Now let's have a look at the code:

String message=("This is my first ARDUINO message"); //here you can type in the text you want to send

String phone_nr=("+441234567");   //recipient's phone number in international format

byte original_byte;

byte newbyte;

int pdu_length;

int counter;

String newphone_nr;

int numlength;

 

void setup() {

  delay(1000);

  Serial.begin(19200);  

  delay(1000);

  

Now we rearrange the phone number:

First we need to know the length of the phone number (number of digits). To determine the length of an array there is the special function    string.length(). We use this function to get the length of the string "phone_nr" and store the value in the variable "numlength". Since the string contains also the character "+", which is not strictly part of the number, we reduce the obtained length by 1.

  

   numlength=phone_nr.length()-1;

The GSM protocol states that if the length of phonenumber is uneven, the  character "F" needs to be added at the end of it. In our example,  441234567 becomes 441234567F.

How to check if the length of the number is uneven? By dividing the value by two. If the division ends without any remainder, the value was even, else it was uneven.  This is exactly what the modulo division does. It calculates the remainder when one number is divided by another.

So we perform a modulo division on the length of the phone number: we divide it by 2 and check if there is any remainder. To be precise, we check if the remainder is different from zero. If the remainder is zero, the value is even, else it is uneven.

  % is the symbol for a modulo-division

  != 0 means different from zero.

  phone_nr += "F";  is the same as  phone_nr=phone_nr+"F";

  

 if (numlength% 2 != 0 ) {

   phone_nr += "F"; 

   }

Now we have to bring the phone number into a new shape. The GSM protocol requires to split the number in pairs of two and then to invert the order of the digits in each single pair. In our example: 441234567F -> split in pairs -> 44 12 34 56 7F -> invert the order of the digits ->44214365F7

This is done by the following piece of code, which essentially says:

Starting from zero, in steps of 2, until you reach the end of the number, take the original number's second digit to the right and add it to the string "newphone_nr", then take the first digit to the right and add it as well. So we'll take the original number's digits in then following order and assemble them to obtain the new number; 2-1-4-3-6-5-8-7-10-9-etc.

The function String.charAt(x) takes a character at a specified position within the string.

The function String.concat(x) adds the specified character to an existing String.

Each time we take a digit from the original string, we immediately add it to the new string.

 

    for(int x = 0; x < numlength; x = x+2){

    newphone_nr.concat(phone_nr.charAt(x+2));

    newphone_nr.concat(phone_nr.charAt(x+1));

    }

    

  //getting ready to send the SMS

   Serial.println("AT");  //optional: say hello to the modem

   delay(3000);

Now we need to determine the length of the final PDU message, because we need to pass this information to the mobile phone before passing the actual PDU message. The PDU  message which is passed to the mobile phone consists of a number of parameters (16 characters), the rearranged  recipients phone number and the encoded message text. The total length is the sum of these components and needs to be expressed as the total number of characters in the message divided by 2.  We have already determined the number of characters (digits) in the phone number. So let's try to determine  the length of the message text in PDU format. According to what explained earlier, the number of bytes in the encoded message will be the result of the number of characters in the original text mulitplied by 7 and  then divided again by 8. If there are any remaining bits, they will form a new byte which needs also to be counted.

 

     int textlength= message.length()*7/8;   // text message length: number of characters*7/8

     if (message.length()*7%8 != 0) {textlength++;} 

     if (numlength%2 != 0) {   //if the length of phone number was uneven, it now increases by 1 because of the added "F"

      pdu_length = (8 + (numlength+1)/2 + textlength);} else { //8 bytes of parameters + (length of phonenumber)/2 + number of bytes in the text

      pdu_length = (8 + numlength/2 + textlength);}

  

Now it's time for the mobile to do some work. We send the command which tells the mobile to send an SMS message of a specified length. With the command AT+CMGW the SMS will not be sent to the recipient, but stored in the mobile's memory. You will find it later in the SMS outbox. So use this command for your first experiments, as it will not create any costs for the test messages. If you really want to send the message, use the command AT+CMGS instead.

   

      Serial.print("AT+CMGW="); //message will be stored in the mobile phone - AT+CMGS will send it off.

      Serial.println(pdu_length);

      delay(3000);

      Serial.print("001100");  //these are some parameters which we don't need to bother about

      if (numlength<16){       //now we have to send the byte containing the length of the phone number in HEX format

      Serial.print(0);}        //the byte in HEX format needs to be 2 characters long; if needed we add a 0 in front, e.g. "C" will become "0C"

      Serial.print(numlength,HEX); //length telnr - this time any additional F is not to be considered

      Serial.print("91");     //international format of phone number //81 for national format

      Serial.print(newphone_nr); //now we send the rearranged phone number

      Serial.print("0000AA"); //some more parameters 

      if (message.length()<16){

      Serial.print(0);}       //now a byte in HEX format indicating the length of the text. Again the byte needs to be made of 2 characters

      Serial.print(message.length(),HEX); 

      

Now the most exciting part: we encode the text of the message into PDU format. To achieve this, we disassemble the original bytes one by one, throw away the MSB, and add the remaining bits to form new bytes (series of 8 bits). We use the function String.charAt() which returns the byte of a string in the position defined by the number in the brackets. message.charAt(3) would return the 4th byte of the String message. This is because the first byte is counted as 0, the byte in our example would therefore be the "s" of "This". Out of each single byte obtained in this way we take the bits 0 to 6 (and leave away the 7th bit, the MSB). This is achieved with the function bitRead which returns the bit at a specified position within a byte. Immediately we start assembling new bytes by creating series of 8 bits as the process goes on. As soon as a new byte is finished, we send it to the mobile in HEX format.

We need also to understand, how the new bytes are created. This requires some knowledge of bit maths. Please  read the following document http://playground.arduino.cc/code/BitMath#bit_pack to learn the basics.

We obtain the new bytes by using the bitwise OR and the left-shift operators, expressed by the symbols | and <<, respectively. Essentially, we use it to add binary numbers. Lets have a look in slow motion: we start with the first letter of the text, the character "T". It is expressed by the binary sequence 01010100. The next letter is "h", or bin 01101000. The leftmost bit is the MSB, which we'll leave away. So we will take 1010100 from the first byte and add the first bit of the next byte, which is 0, as MSB to obtain our first new byte: 01010100. This will be sent to the mobile as "54", which is the HEX format of this byte. Not a very lucky example, because  here the resulting byte by coincidence is the same as the one we started with... 

Now back to the mechanism of adding the single bits with bitwise OR and left-shift. This is what happens, in slow motion:

the first bit, "0", is taken, not shifted anywhere (counter=0), and added to the variable "newbyte" which initially was set to 0. This is now the least significant bit of the new byte being created. The next bit is again "0". Now the counter has moved to 1. So this 0 is shifted one position to the left and added to the existing variable "newbyte". At the next step the counter=2, the next taken bit is 1. This bit is shifted 2 positions to the left, thus obtainig the binary value 100. Again, it is added to "newbyte". This goes on for 8 times, until the counter reaches the value 7. These 8 loops will yield the following values:

     

      bit1        0       the addition at each loop produces-->          0

      bit2       00                                                     00

      bit3      100                                                    100

      bit4     0000                                                   0100

      bit5    10000                                                  10100

      bit6   000000                                                 010100

      bit7  1000000                                                1010100

      bit8 00000000                                               01010100

      _____________

           01010100

      

    

 

    counter=0;

    newbyte=0;

    for (int j=0; j<message.length(); j++) {

    original_byte = message.charAt(j); //one by one we take each byte out of the original text

   

    for (int i=0; i<7; i++) {

    newbyte=newbyte|bitRead(original_byte, i)<<counter;  //take the bits 0 to 6 of the old byte, shift as required for the new byte and add to the new byte

    counter++;

    if (counter==8) {

    counter=0;          //if a new byte is complete, set the counter to 0

    if (newbyte<16){

    Serial.print(0);} //each byte in HEX format should consist of 2 characters, if necessary add 0 in front

    Serial.print (newbyte, HEX); //when the new byte is finished, send it to the mobile phone

    newbyte=0; // the next new byte will start again from 0

  }

  }

  }

    if (message.length()*7%8!=0) {    //if there are remaining bits (not enough to form a new byte), these are sent out as the last byte

    if (newbyte<16){

    Serial.print(0);} //each byte in HEX format should consist of 2 characters, if necessary add 0 in front

    Serial.print (newbyte, HEX);}

    delay (3000);

    Serial.write(26); //this is a command telling the mobile phone that the end of the transmission has been reached

  }

void loop() {

  

  }

The final output which is sent to the mobile phone for this given phone number and message text is:

AT+CMGW=41

001100099144214365F70000AA2054747A0E4ACF41ED3CC89C96CFE9A0A094584D3A9FA076793E0F9FCB

 

HAVE FUN!

Below there is the code for download