title: How to build concatenated sms pdu? Getting junk chars
body: <p>I'm trying to build some PHP code to send SMS through telnet to a SIM server, but I'm having trouble in sending concatenated messages.</p>
  
  <p>I've read some things about using padding bits to make the coded message septets into octets, but I don't fully understand how it works.</p>
  
  <p>I have a class that receives the phone number, the message (already split in 153chars maximum), the total number of SMSs and the order number of the present part of text.</p>
  
  <p>It works as long as I have that '20' added before the $hexmessage. But I get a junk char in the beginning of the first part (before the 1st letter of my msg), and the same junk char replacing the first letter of the second part! (using '20' so it would show a blank space, but it shows a triangle)</p>
  
  <p>I can't understand why, or what I'd had to change for it to work properly.</p>
  
  <p>I hope that someone can help me understand what am I doing wrong.</p>
  
  <p>Here's what I've got so far:</p>
  
  <pre><code>&lt;?php
  
   // Generate PDU string
  public function generatePDUm($receiverNumber,$message, $sms_count, $msg_nr) {
  
      //Append filler digit if number is national
      if( strlen($receiverNumber)==9){
          $nacional=1;
          $receiverNumber = $receiverNumber."F";                              
          $network = substr($receiverNumber, 0, 2);                       //NETWORK code, used to decide the SIM Card to be used
  
      //Check for international flags and set the number type accordingly
      }else{
          $nacional=0;
          if(substr($receiverNumber, 0, 1)=='+'){
              $network = substr($receiverNumber, 4, 2);                   //NETWORK code, used to decide the SIM Card to be used 
              $receiverNumber = substr($receiverNumber, 1, 12);           //remove international indicator    
          } 
          else if(substr($receiverNumber, 0, 2)== '00'){
              $network = substr($receiverNumber, 5, 2);                   //NETWORK code, used to decide the SIM Card to be used 
              $receiverNumber = substr($receiverNumber, 2, 12);           //remove international indicator
          }
  
      }
  
      /* Flag the network to be used */
      switch ($network){
          case  "92":
              $network="TMN";
              break;
  
          case  "96":
              $network="TMN";
              break;
  
          case "91":
              $network="VODAFONE";
              break;
  
          case "93":
              $network="OPTIMUS";
              break;
      }
      // Receiver number must be 10 characters long ('national nr' + filler digit) or less than 13 ('351'+'national nr'). (Portugal)
      if( strlen($receiverNumber) &lt; 10 || strlen($receiverNumber) &gt; 12) {
          // Error, not 10 or over 12 numbers long (Code 1)
          $this-&gt;setErrorCode(1);
          return false;
      }
  
      // Message must be 2 characters long at least
      if( strlen($message) &lt; 2 ) {
          // Error, message too short (Code 2)
          $this-&gt;setErrorCode(2);
          return false;
      }
  
      // Message can't be longer than 153 characters. 3SMS.
      if( strlen($message) &gt; 153 ) {
          // Error, message too long (Code 3)
          $this-&gt;setErrorCode(3);
          return false;
      }
  
      // Length of servicecenter number (00 = automatically fixed by phone)
      $serviceCenterNumberLength = '00';
  
      // SMS-? : 04=sms-deliver(recieve), 11=sms-submit, 01 = dont know but it works, 41 = SMS-SUBMIT + UDH bit (for extended/concatenated SMS)
      // You can try to change this if your phone does not work with 01 command try to use 11 command
      $smsType = '41';
  
      // TP Message Reference: (placeholder), let the phone set the message reference number itself
      $messageRef = '00';
  
      // Number length. If national -&gt; 9, if international -&gt; 12
      if($nacional==1){
          $numberLength = '09';
      }else{
          $numberLength = '0C';
      }
  
  
      // Type of phone adress: (81=Unknown=10dec, 91=InternationalFormat, 92=National?)
      if($nacional==1){
          $numberType = '81';
      }else{
          $numberType = '91';
      }
  
      // Get the PDU version of the number
      $number = $this-&gt;getNumberAsPDU( $receiverNumber );
  
      // TP-PID (Protocol Identifier)
      $protocolId = '00';
  
      // TP-DCS (Data coding scheme)
      $dataCodingScheme = '00';
  
      // TP-Validity-Period (timestamp), AA=4days expiry, disabled for SonyEricsson support.
     // $validityPeriod = 'A0';
      // $validityPeriod = 'AA'; // Add this if the PDU command fails
  
      /*user data header information (05 - User Data header info length 
      *                               00 - Information element identifier for a concatenated short message
      *                               03 - Information element data length
      *                               00 - Reference number, auto
      *                               0.$sms_count - total SMS nr
      *                               0.$msg_nr    - current SMS order */
      $udhi = '050003000'.$sms_count.'0'.$msg_nr;
     // echo 'UDHinfo: '.$udhi."\n";
  
      // Data length of message (in hex format)
      $dataLength = $this-&gt;strToHexLen($message);
     // echo 'DATA LENGHT: '.$dataLength."\n\n";
  
      // Convert message, string &gt; 7bits &gt; 8bits &gt; hex
      $hexMessage = $this-&gt;bit7tohex( $this-&gt;strto7bit( $message ) );
  
  
      // Create the complete PDU string
      $pdu = $serviceCenterNumberLength . $smsType . $messageRef . $numberLength .
              $numberType . $number . $protocolId . $dataCodingScheme . $dataLength .
              $udhi . '20' .$hexMessage;
  
      /*
       * Generate the length of var $pdu (pdu/2 minus 1) as pdu format requests
       * The -1 is because we don't count the first two characters '00', needed for this command: 'cmgs=24'
       */
      $cmgslen = strlen($pdu)/2-1;
  
      // Build data array to return with required information
      $data = array();
      $data['pdu'] = $pdu;
      $data['cmgslen'] = $cmgslen;
      $data['rede'] = $network;
  
      // Return the data array with PDU information
      return $data;
  }
  
  
  // Generate PDU formatted cellphone number
  private function getNumberAsPDU($number) {
  
          // Length of number divided by 2 handle two characters each time
      $length = strlen( $number )/2;
      // Set counter to 1 for strlen
      $i = 1;
      $pduNumber = '';
  
      // Loop to handle every 2 characters of the phone number. 06 12 34 56 78
      while ($i &lt;= $length) {
          // Get 2 characters of the complete string depending on the number of the current loop.
          // Then reverse these 2 characters and put them in var $pduNumber (06 = 60)
          $pduNumber .= strrev( substr( $number,$i*2-2,2) );
          // Counter + 1
          $i++;
      }
  
      // Return the generated number
      return $pduNumber;
  }
  
  
  /* Function to convert ascii character to 8 bits
   * Much more efficient than holding a complete ASCII table
   * Thanks to Mattijs F.
   */
  private function asc2bin($input, $length=8) {
  
      $bin_out = '';
      // Loop through every character in the string
      for($charCount=0; $charCount &lt; strlen($input); $charCount++) {
          $charAscii = ord($input{$charCount}); // ascii value of character
          $charBinary = decbin($charAscii); // decimal to binary
          $charBinary = str_pad($charBinary, $length, '0', STR_PAD_LEFT);
          $bin_out .= $charBinary;
      }
  
      // Return complete generated string
      return $bin_out;
  }
  
  
  // String to 7 bits array
  private function strto7bit($message) {
      $message = trim($message);
      $length = strlen( $message );
      $i = 1;
      $bitArray = array();
  
      // Loop through every character in the string
      while ($i &lt;= $length) {
          // Convert this character to a 7 bits value and insert it into the array
          $bitArray[] = $this-&gt;asc2bin( substr( $message ,$i-1,1) ,7);
          $i++;
      }
  
  
      // Return array containing 7 bits values
      return $bitArray;
  }
  
  
  // Convert 8 bits binary string to hex values (like F2)
  private function bit8tohex($bin, $padding=false, $uppercase=true) {
      $hex = '';
      // Last item for counter (for-loop)
      $last = strlen($bin)-1;
      // Loop for every item
      for($i=0; $i&lt;=$last; $i++) {
          $hex += $bin[$last-$i] * pow(2,$i);
      }
  
      // Convert from decimal to hexadecimal
      $hex = dechex($hex);
      // Add a 0 (zero) if there is only 1 value returned, like 'F'
      if($padding &amp;&amp; strlen($hex) &lt; 2 ) {
          $hex = '0'.$hex;
      }
  
      // If we want the output returned as UPPERCASE do this
      if($uppercase) {
          $hex = strtoupper($hex);
      }
  
      // Return the hexadecimal value
      return $hex;
  }
  
  
  // Convert 7 bits binary to hex, 7 bits &gt; 8 bits &gt; hex
  private function bit7tohex($bits) {
  
      $i = 0;
      $hexOutput = '';
      $running = true;
  
      // For every 7 bits character array item
      while($running) {
  
          if(count($bits)==$i+1) {
              $running = false;
          }
  
          $value = $bits[$i];
  
          if($value=='') {
              $i++;
              continue;
          }
  
          // Convert the 7 bits value to the 8 bits value
          // Merge a part of the next array element and a part of the current one
  
          // Default: new value is current value
          $new = $value;
  
          if(key_exists(($i+1), $bits)) {
              // There is a next array item so make it 8 bits
              $neededChar = 8 - strlen($value);
              // Get the char;s from the next array item
              $charFromNext = substr($bits[$i+1], -$neededChar);
              // Remove used bit's from next array item
              $bits[$i+1] = substr($bits[$i+1], 0, strlen($bits[$i+1])-$neededChar );
              // New value is characters from next value and current value
              $new = $charFromNext.$value;
          }
  
          if($new!='') {
              // Always make 8 bits
              $new = str_pad($new, 8, '0', STR_PAD_LEFT);
              // The 8 bits to hex conversion
              $hexOutput .= $this-&gt;bit8tohex($new, true);
          }
  
          $i++;
      }
  
      // Return the 7bits-&gt;8bits-&gt;hexadecimal generated value
      return $hexOutput;
  }
  
  // String to length in Hex, String &gt; StringLength &gt; Hex
  private function strToHexLen($message) {
  
      // Length of the string (message)
      $length = strlen( $message )+7; //+7 for UDH. the UDH is a total of (number of octets x bit size of octets) 6 x 8 = 48 bits long. Therefore a single bit of padding has to be prepended to the message. The UDH is therefore (bits for UDH / bits per septet) = (48 + 1)/7 = 7 septets in length.
      // Hex value of this string length
      $hex = dechex($length);
  
      // Length of the hex value
      $hexLength = strlen($hex);
      // If the hex strng length is lower dan 2
      if($hexLength &lt; 2) {
          // Add a 0 (zero) before it
          $hex = '0'.$hex;
      }
  
      // Return the hex value in UPPERCASE characters
      return strtoupper($hex);
  }
  
  }
  ?&gt;
  </code></pre>