NuttX RTOS for PinePhone: Phone Calls and Text Messages

📝 4 May 2023

Apache NuttX RTOS makes a Phone Call from Pine64 PinePhone

What makes Pine64 PinePhone a phone? Because it will make Phone Calls and send Text Messages!

We’re porting Apache NuttX RTOS (Real-Time Operating System) to PinePhone. Today let’s turn NuttX into a Feature Phone

We begin with the 4G LTE Modem inside PinePhone…

Quectel EG25-G LTE Modem

Quectel EG25-G LTE Modem

§1 Quectel EG25-G LTE Modem

What’s this LTE Modem?

Inside PinePhone is the Quectel EG25-G LTE Modem for 4G LTE Voice Calls, SMS and Mobile Data, plus GPS (pic above)…

To control the LTE Modem, we send AT Commands

So to dial the number 1711, we send this AT Command…

ATD1711;

The LTE Modem has similar AT Commands for SMS and Mobile Data.

(EG25-G runs on Qualcomm MDM 9607 with a Cortex-A7 CPU inside)

How to send AT Commands to LTE Modem?

The LTE Modem accepts AT Commands in two ways…

So if we’re sending and receiving lots of 4G Mobile Data, USB is the better way.

Today we’ll talk about the UART Interface, which is sufficient for building a Feature Phone on NuttX.

Let’s power up the LTE Modem in PinePhone…

Note: Power Key and Reset are High-Low Inverted for PinePhone

Note: Power Key and Reset are High-Low Inverted for PinePhone

§2 Start LTE Modem

Before sending AT Commands…

How will we power up the LTE Modem?

In the previous article we spoke about starting the LTE Modem with NuttX (pic above)…

Which we have implemented in NuttX as pinephone_modem_init.

(pinephone_modem_init is called by pinephone_bringup)

At NuttX Startup, we see this …

Starting kernel...
Enable UART3 on PD0
Enable UART3 on PD1
Set PWR_BAT (PL7) to High
Set RESET_N (PC4) to Low
Set AP-READY (PH7) to Low to wake up modem
Set DTR (PB2) to Low to wake up modem
Set PWRKEY (PB3) to High
Wait 600 ms
Set PWRKEY (PB3) to Low
Set W_DISABLE (PH8) to High
Status=1
...
Status=0

NuttShell (NSH) NuttX-12.0.3
nsh> 

(See the Complete Log)

Which says that PinePhone’s LTE Modem is up and accessible at Port UART3!

Let’s send some AT Commands to UART3…

NuttX sends AT Commands to LTE Modem

NuttX sends AT Commands to LTE Modem

§3 Send AT Commands

LTE Modem has started successfully at UART3…

How will we send AT Commands to the modem?

On NuttX, this is how we send an AT Command to the LTE Modem over UART3: hello_main.c

// Open /dev/ttyS1 (UART3)
int fd = open("/dev/ttyS1", O_RDWR);
printf("Open /dev/ttyS1: fd=%d\n", fd);
assert(fd > 0);

// Repeat 5 times: Write command and read response
for (int i = 0; i < 5; i++) {

  // Write command
  const char cmd[] = "AT\r";
  ssize_t nbytes = write(fd, cmd, strlen(cmd));
  printf("Write command: nbytes=%ld\n%s\n", nbytes, cmd);
  assert(nbytes == strlen(cmd));

  // Read response
  static char buf[1024];
  nbytes = read(fd, buf, sizeof(buf) - 1);
  if (nbytes >= 0) { buf[nbytes] = 0; }
  else { buf[0] = 0; }
  printf("Response: nbytes=%ld\n%s\n", nbytes, buf);

  // Wait a while
  sleep(2);
}

// Close the device
close(fd);

The above NuttX App sends the command “AT” to the LTE Modem over UART3. (5 times)

Watch what happens when we run it…

Our NuttX App sends command “AT” to the LTE Modem over UART3…

Open /dev/ttyS1
Write command: AT\r

No response! At startup, the LTE Modem might take 30 seconds to become operational. Then this appears…

Response:
RDY

RDY” means that the LTE Modem is ready for AT Commands!

(EG25-G AT Commands, Page 297)

Our NuttX App sends command “AT” again…

Write command: AT\r
Response:
+CFUN: 1
+CPIN: READY
+QUSIM: 1
+QIND: SMS DONE
+QIND: PB DONE

LTE Modem replies…

Our NuttX App sends command “AT” once more…

Write command: AT\r
Response:
AT
OK

LTE Modem echoes our command “AT”…

And responds to our command with “OK”.

Which means that our LTE Modem is running AT Commands all OK!

(See the Complete Log)

The actual log looks kinda messy…

Yeah we’ll talk about the proper AT Modem API in a while.

First let’s check the network and make some phone calls…

§4 Check the LTE Network

Before making a Phone Call…

How to check if the 4G LTE Network is OK?

To check if the LTE Modem has connected successfully to the 4G LTE Network, we send these AT Commands…

This is how we do it: hello_main.c

// Check the Network Status: AT+CREG?
const char cmd[] = "AT+CREG?\r";
write(fd, cmd, strlen(cmd));

// Read response
static char buf[1024];
nbytes = read(fd, buf, sizeof(buf) - 1);
if (nbytes >= 0) { buf[nbytes] = 0; }
else { buf[0] = 0; }
printf("Response: nbytes=%ld\n%s\n", nbytes, buf);

// Wait 2 seconds
sleep(2);

// Get the Network Operator
const char cmd[] = "AT+COPS?\r";
write(fd, cmd, strlen(cmd));

// Omitted: Read response and wait 2 seconds

And here’s the output (pic below)…

NuttShell (NSH) NuttX-12.0.3
nsh> hello

// Check Network Status
Command: AT+CREG?
Response:
+CREG: 0,1
// Network is ready

// Get Network Operator (SGP-M1)
Command: AT+COPS?
Response:
+COPS: 0,0,"SGP-M1",7

+CREG: 0,1” says that the 4G LTE Network is ready.

SGP-M1” is the name of our 4G LTE Network Operator.

We’re all set to make some calls!

(See the Complete Log)

NuttX makes a Phone Call from PinePhone

NuttX makes a Phone Call from PinePhone

§5 Outgoing Phone Call

In NuttX, this is how we dial a Phone Number to make an Outgoing Phone Call: dial_number

// Get Range of PCM Parameters for Digital Audio
const char cmd[] = "AT+QDAI=?\r";
write(fd, cmd, strlen(cmd));
// Omitted: Read response and wait 2 seconds

// Get Current PCM Configuration for Digital Audio
const char cmd[] = "AT+QDAI?\r";
write(fd, cmd, strlen(cmd));
// Omitted: Read response and wait 2 seconds

We begin by configuring the PCM Digital Audio for our Voice Call.

(More about “AT+QDAI” in a while)

Suppose we’re dialing the (fictitious) number “+1234567890”.

We send the command “ATD+1234567890;”…

// Phone Number to dial
#define PHONE_NUMBER "+1234567890"

// Make Outgoing Phone Call
// ATD+1234567890;
const char cmd[] = 
  "ATD"
  PHONE_NUMBER
  ";\r";
write(fd, cmd, strlen(cmd));
// Omitted: Read response

(EG25-G AT Commands, Page 114)

We wait 20 seconds for the Called Number to answer…

// Wait 20 seconds for receiver to answer
sleep(20);

Finally we hang up the call with “ATH”…

// Hang up Phone Call
const char cmd[] = "ATH\r";
write(fd, cmd, strlen(cmd));
// Omitted: Read response and wait 2 seconds

(EG25-G AT Commands, Page 116)

Here’s the output (pic above)…

// Get Range of PCM Parameters for Digital Audio
Command: AT+QDAI=?
Response: +QDAI: (1-4),(0,1),(0,1),(0-5),(0-2),(0,1)(1)(1-16)

// Get Current PCM Configuration for Digital Audio
Command: AT+QDAI?
Response: +QDAI: 1,1,0,1,0,0,1,1

// Make Outgoing Phone Call
Command: ATD+1234567890;
Response:
OK

// Receiver has hung up
Response:
NO CARRIER

// After 20 seconds, hang up Phone Call
Command: ATH
Response: OK

And the phone rings for the called Phone Number! (Pic above)

(See the Complete Log)

But how will we talk to the called Phone Number?

Aha! That’s why we need the “AT+QDAI” commands, for the PCM Digital Audio setup. We’re still working on it…

Now we send an SMS Text Message…

NuttX sends an SMS in Text Mode

NuttX sends an SMS in Text Mode

§6 Send SMS in Text Mode

To send an SMS Message (in Text Mode), use these AT Commands…

  1. AT+CMGF=1

    Select Text Mode for SMS

    (Instead of PDU Mode)

    (EG25-G AT Commands, Page 146)

  2. AT+CSCS="GSM"

    Select (7-bit ASCII-like) GSM Character Set

    (Instead of 16-bit UCS2 Unicode)

    (EG25-G AT Commands, Page 25)

  3. AT+CMGS="+1234567890"

    Send an SMS to the (imaginary) Phone Number “+1234567890

    (Also works without country code, like “234567890”)

    (EG25-G AT Commands, Page 159)

  4. Wait for Modem to respond with “>

  5. Enter SMS Message in Text Format, terminate with Ctrl-Z…

    Hello from Apache NuttX RTOS on PinePhone!<Ctrl-Z>
    

    (Ctrl-Z is Character Code 0x1A)

  6. Modem sends our SMS and responds with the Message ID

    +CMGS: 22
    

    (EG25-G AT Commands, Page 159)

This is how we send an SMS (in Text Mode) with NuttX: send_sms_text

// Select Text Mode for SMS (instead of PDU Mode)
const char cmd[] = "AT+CMGF=1\r";
write(fd, cmd, strlen(cmd));
// Omitted: Read response and wait 2 seconds

// Select GSM Character Set (instead of UCS2)
const char cmd[] = "AT+CSCS=\"GSM\"\r";
write(fd, cmd, strlen(cmd));
// Omitted: Read response and wait 2 seconds

// Phone Number that will receive the SMS.
// Also works without country code, like "234567890"
#define PHONE_NUMBER "+1234567890"

// Send an SMS to the (imaginary) Phone Number "+1234567890"
// AT+CMGS="+1234567890"
const char cmd[] = 
  "AT+CMGS=\""
  PHONE_NUMBER
  "\"\r";
write(fd, cmd, strlen(cmd));
// Omitted: Read response and wait 2 seconds

// Wait for Modem to respond with ">"
for (;;) {
  // Read response
  static char buf[1024];
  ssize_t nbytes = read(fd, buf, sizeof(buf) - 1);
  if (nbytes >= 0) { buf[nbytes] = 0; }
  else { buf[0] = 0; }
  printf("Response: nbytes=%ld\n%s\n", nbytes, buf);

  // Stop if we find ">"
  if (strchr(buf, '>') != NULL) { break; }
}

// Enter SMS Message in Text Format, terminate with Ctrl-Z
const char cmd[] = 
  "Hello from Apache NuttX RTOS on PinePhone!"
  "\x1A";  // End of Message (Ctrl-Z)
write(fd, cmd, strlen(cmd));

// Omitted: Read response and wait 2 seconds
// Modem responds with the Message ID

Here’s the log (pic above)…

// Select Text Mode for SMS (instead of PDU Mode)
Command: AT+CMGF=1
Response: OK

// Select GSM Character Set (instead of UCS2)
Command: AT+CSCS="GSM"
Response: OK

// Send an SMS to the (imaginary) Phone Number "+1234567890"
// Also works without country code, like "234567890"
Command:
AT+CMGS="+1234567890"

// Wait for Modem to respond with ">"
Response:
> 

// Enter SMS Message in Text Format, terminate with Ctrl-Z
Command:
Hello from Apache NuttX RTOS on PinePhone!<Ctrl-Z>

// Modem responds with the Message ID
Response:
+CMGS: 22
OK

And the SMS Message will be sent to the Phone Number! (Pic above)

(See the Complete Log)

What if we get Error 350?

+CMS ERROR: 350

This means that the Telco’s SMS Centre has rejected our SMS Message.

Check that the SMS Centre is correct (like this)

// Get SMS Centre
Command: AT+CSCA?
Response:
+CSCA: "+6587614701",145

Also check that the SIM Card works OK on another phone.

We might have a problem with the LTE IP Multimedia Subsystem (IMS)…

There’s another way to send SMS…

Send SMS in PDU Mode

§7 Send SMS in PDU Mode

Sending an SMS Message looks easy!

Ah but there’s another (preferred and precise) way: PDU Mode.

(Protocol Data Unit, works like a Data Packet)

In PDU Mode, we encode the SMS Messages as Hexadecimal Numbers. These are the official docs…

Let’s walk through the steps to send an SMS in PDU Mode

  1. AT+CMGF=0

    We select PDU Mode for SMS (instead of SMS Mode)

    (EG25-G AT Commands, Page 146)

  2. Phone Numbers are a little odd in PDU Mode.

    Suppose we’re sending an SMS to this Phone Number (Country Code is mandatory)…

    #define PHONE_NUMBER    "+1234567890"
    #define PHONE_NUMBER_PDU "2143658709"
    

    Note that we flip the nibbles (half-bytes) from the Original Phone Number to produce the PDU Phone Number.

    If the number of nibbles is odd, insert “F” into the PDU Phone Number, like this…

    #define PHONE_NUMBER    "+123456789"
    #define PHONE_NUMBER_PDU "214365870F9"
    
  3. Let’s assume there are 10 Decimal Digits in the destination Phone Number: “+1234567890” (Country Code is mandatory)

    This is the AT Command to send an SMS

    // Send an SMS with 41 bytes (excluding SMSC)
    const char cmd[] = 
      "AT+CMGS="
      "41"  // TODO: PDU Length in bytes, excluding the Length of SMSC
      "\r";
    

    (More about PDU Length in a while)

    (EG25-G AT Commands, Page 159)

  4. Wait for Modem to respond with “>

  5. Enter SMS Message in PDU Format, like this…

    // SMS Message in PDU Format
    const char cmd[] = 
      "00"  // Length of SMSC information (None)
      "11"  // SMS-SUBMIT message
      "00"  // TP-Message-Reference: 00 to let the phone set the message reference number itself
      "0A"  // TODO: Address-Length: Length of phone number (Number of Decimal Digits in Phone Number)
      "91"  // Type-of-Address: 91 for International Format of phone number
      PHONE_NUMBER_PDU  // TODO: Phone Number in PDU Format
      "00"  // TP-PID: Protocol identifier
      "08"  // TP-DCS: Data coding scheme
      "01"  // TP-Validity-Period
      "1C"  // TP-User-Data-Length: Length of Encoded Message Text in bytes
      // TP-User-Data: Encoded Message Text "Hello,Quectel!"
      "00480065006C006C006F002C005100750065006300740065006C0021"
      "\x1A";  // End of Message (Ctrl-Z)
    

    (More about these fields in a while)

    (Remember to set “Address-Length” according to the destination Phone Number)

  6. Modem sends our SMS and responds with the Message ID

    +CMGS: 23
    

    (EG25-G AT Commands, Page 159)

Let’s look at the NuttX Code: send_sms_pdu

// Select PDU Mode for SMS (instead of SMS Mode)
const char cmd[] = "AT+CMGF=0\r";
write(fd, cmd, strlen(cmd));
// Omitted: Read response and wait 2 seconds

// Send an SMS with 41 bytes (excluding SMSC)
const char cmd[] = 
  "AT+CMGS="
  "41"  // TODO: PDU Length in bytes, excluding the Length of SMSC
  "\r";
write(fd, cmd, strlen(cmd));
// Omitted: Read response and wait 2 seconds

// Wait for Modem to respond with ">"
for (;;) {
  // Read response
  static char buf[1024];
  ssize_t nbytes = read(fd, buf, sizeof(buf) - 1);
  if (nbytes >= 0) { buf[nbytes] = 0; }
  else { buf[0] = 0; }
  printf("Response: nbytes=%ld\n%s\n", nbytes, buf);

  // Stop if we find ">"
  if (strchr(buf, '>') != NULL) { break; }
}

// Enter SMS Message in PDU Format, terminate with Ctrl-Z
const char cmd[] = 
  "00"  // Length of SMSC information (None)
  "11"  // SMS-SUBMIT message
  "00"  // TP-Message-Reference: 00 to let the phone set the message reference number itself
  "0A"  // TODO: Address-Length: Length of phone number (Number of Decimal Digits in Phone Number)
  "91"  // Type-of-Address: 91 for International Format of phone number
  PHONE_NUMBER_PDU  // TODO: Phone Number in PDU Format
  "00"  // TP-PID: Protocol identifier
  "08"  // TP-DCS: Data coding scheme
  "01"  // TP-Validity-Period
  "1C"  // TP-User-Data-Length: Length of message in bytes
  // TP-User-Data: Encoded Message Text "Hello,Quectel!"
  "00480065006C006C006F002C005100750065006300740065006C0021"
  "\x1A";  // End of Message (Ctrl-Z)
write(fd, cmd, strlen(cmd));

// Omitted: Read response and wait 2 seconds
// Modem responds with the Message ID

Here’s the log…

// Select PDU Mode for SMS (instead of SMS Mode)
Command: AT+CMGF=0
Response: OK

// Send an SMS with 41 bytes (excluding SMSC)
Command: AT+CMGS=41

// Wait for Modem to respond with ">"
Response: > 

// Enter SMS Message in PDU Format, terminate with Ctrl-Z
Command:
0011000A9121436587090008011C00480065006C006C006F002C005100750065006300740065006C0021<Ctrl-Z>

// Modem responds with the Message ID
Response: 
+CMGS: 23
OK

And our SMS Message “Hello,Quectel!” appears at the destination Phone Number!

(See the Complete Log)

How to encode the SMS in PDU Format? What’s the PDU Length?

The PDU Encoding for SMS is explained here…

What if we get Error 304?

+CMS ERROR: 304

The LTE Modem tells us that the PDU Encoding is invalid.

Check the docs above to verify the PDU Encoding for our SMS Message.

Let’s find out why we prefer PDU Mode over Text Mode…

(For Error 305: Check the SMSC and SIM)

§8 SMS Text Mode vs PDU Mode

Why send SMS in PDU Mode instead of Text Mode?

Sending SMS Messages in Text Mode looks easier. But we should use PDU Mode instead. Here’s why…

Text Mode: This is how we send an SMS…

// Text Mode: How many characters in this SMS?
AT+CMGS="+1234567890"

// Followed by Message Text...

PDU Mode: This is how we do it…

// PDU Mode: 41 bytes in this SMS (excluding SMSC)
AT+CMGS=41

// Followed by SMS Message encoded as PDU...

See the difference? PDU Mode is more precise because we state exactly how many bytes there are in the SMS.

With Text Mode, there’s a risk of garbled messages when characters are dropped during UART Transmission.

(Which happens!)

But what if characters are dropped in PDU Mode?

The LTE Modem will say it’s an Invalid PDU

+CMS ERROR: 304

Our app should catch this error and resend.

§9 AT Modem API

What about receiving Phone Calls and SMS Messages?

We handle Incoming Phone Calls and SMS like this…

And it looks messy. LTE Modem will dump a Notification whenever there’s an Incoming Call or SMS…

// Incoming Call Notification
RING
// We answer the call with ATA
...
// Incoming SMS Notification
+CMT: "+8615021012496",,"13/03/18,17:07:21+32",145,4,0,0,"+8613800551500",145,28
// Followed by Message Text

Which is totally Asynchronous. And tricky to handle over UART.

Any other UART problems with LTE Modem?

Our UART Output looks all jumbled up because our NuttX App didn’t wait for the response of every AT Command…

NuttX App writesLTE Modem responds
AT
RDY
AT
+CFUN: 1
AT
AT
OK
+CPIN: READY
+QUSIM: 1
+QIND: SMS DONE
AT+CREG?
(Where’s the CREG response?)
AT
OK
AT+COPS?
AT+CREG?
+CREG: 0,1
OK
(Oops CREG response is delayed!)

(See the Complete Log)

Our NuttX App should have waited for the response of “AT+CREG”… Before sending “AT+COPS”!

So we wait for OK or ERROR. Piece of cake right?

But hold up! Remember that UART might drop characters

Our NuttX App needs a robust parser for responses.

And our app needs to timeout gracefully if we don’t get a timely response. (Then retry)

Is there a proper Modem API for AT Commands?

nRF Connect Modem Library has a nifty AT Interface. We might adapt it for NuttX…

The AT Modem API uses printf and scanf to handle AT Commands…

// Send command "AT+CFUN"
err = modem_at_printf("AT+CFUN=%d", mode);

// Check commnd result
if (err = 0) {
  // "OK" success
} else if (err > 0) {
  // Response is not "OK"
  switch(modem_at_err_type(err)) {
    // Modem returned "ERROR"
    case NRF_MODEM_AT_ERROR: ...

(Source)

And it handles AT Notifications as Callbacks. (Like this)

(Very nice!)

Anything we might reuse from NuttX?

The NuttX Sample Apps for LoRaWAN and ESP8266 will do some (very limited) AT Command Handling. We might copy bits of these…

Wow this looks tedious. If only we had reliable, non-lossy UART…

We do! The LTE Modem supports reliable communication over USB Serial.

Which we’ll explore later when our USB Serial Driver is up!

(UART Hardware Flow Control won’t work on PinePhone)

But first we need to upstream these to NuttX Mainline

TODO: Disable UART2 and /dev/ttyS2 so that UART3 maps neatly to /dev/ttyS3. (See this)

§10 What’s Next

I hope this article was helpful for understanding the internals of Phone Calls and Text Messaging on PinePhone…

We’ll share more details when the AT Modem API is up on NuttX!

Meanwhile please check out the other articles on NuttX for PinePhone…

Many Thanks to my GitHub Sponsors for supporting my work! This article wouldn’t have been possible without your support.

Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…

lupyuen.github.io/src/lte2.md

Ring Indicator is connected to GPIO Pin PL6

Ring Indicator is connected to GPIO Pin PL6

§11 Appendix: Receive Phone Call and SMS

How do we receive Phone Calls and SMS Messages with the LTE Modem?

We receive an Incoming Phone Call like this…

// Notification for Incoming Voice Call
RING

// List the Current Calls
AT+CLCC

// Outgoing Call: Call Type is Packet Switched Call (LTE Mode)
+CLCC: 1,0,0,1,0,"",128

// Incoming Call: Voice, Non-Multiparty, Phone Number, Unknown Number Type
+CLCC: 2,1,4,0,0,"02154450290",129
OK

// Accept the Voice Call
ATA
OK

(EG25-G AT Commands, Page 114)

And we receive an SMS (in Text Mode) like this…

// Select Message Service 3GPP TS 23.040 and 3GPP TS 23.041
AT+CSMS=1
+CSMS: 1,1,1
OK

// Set SMS Event Reporting Configuration
AT+CNMI=1,2,0,0,0
OK

// Notification for Incoming SMS
+CMT: "+8615021012496",,"13/03/18,17:07:21+32",145,4,0,0,"+8613800551500",145,28
This is a test from Quectel.

// Send ACK to the network
AT+CNMA
OK

(EG25-G AT Commands, Page 167)

Receiving an SMS in PDU Mode will look slightly different.

How does the Ring Indicator work with Incoming Call and SMS?

The LTE Modem sets Ring Indicator (GPIO Pin PL6) to High when there’s an Incoming Call or SMS. (Pic above)

Which we configure like this…

// For Incoming Calls: Signal the Ring Indicator
AT+QCFG="urc/ri/ring"

// For Incoming SMS: Signal the Ring Indicator
AT+QCFG="urc/ri/smsincoming" 

(EG25-G AT Commands, Page 46)

(See “AT+QCFG=urc/ri”, Page 20)

LTE Modem is connected to Port PCM0 for Digital Audio

LTE Modem is connected to Port PCM0 for Digital Audio

§12 Appendix: PCM Digital Audio

Earlier we made an Outgoing Voice Call…

How will we talk to the called Phone Number?

LTE Modem is connected to Allwinner A64 Port PCM0 for the PCM Digital Audio Input and Output. (Pic above)

We send the “AT+QDAI” commands for the PCM Digital Audio setup. We’re still working on it…

// Get Range of PCM Parameters for Digital Audio
Command: AT+QDAI=?
Response: +QDAI: (1-4),(0,1),(0,1),(0-5),(0-2),(0,1)(1)(1-16)

// Get Current PCM Configuration for Digital Audio
Command: AT+QDAI?
Response: +QDAI: 1,1,0,1,0,0,1,1

(EG25-G AT Commands, Page 233)

The above PCM Digital Audio Configuration for the LTE Modem says…

This (excellent) article explains how we’ll program Port PCM0 to transmit and receive the Digital Audio Stream…

But how do we actually wire up PCM0 to PinePhone’s Microphone and Speaker?

Yep it looks complex, let’s walk through the connections…

  1. From above, the LTE Modem is connected to Allwinner A64 Port PCM0 for PCM Audio

  2. A64 Port PCM0 is documented in the Allwinner A64 User Manual, “I2C/PCM”, Page 621

  3. So we need to route the PCM Audio from PCM0 to PinePhone’s Microphone / Speaker

According to the PinePhone Schematic

  1. The Audio Connections are HPOUT, MIC1 (MICIN1), MIC2 (MICIN2), EAROUT, LINEOUT

  2. HPOUT and MIC2 (MICIN2) are connected to the Headphone Jack

  3. MIC1 (MICIN1) is connected to the Digital Video Connector (What’s this?)

  4. EAROUT is connected to the Earpiece Speaker

  5. LINEOUT is connected to the Loudspeaker

A64 Ports HPOUT, MICIN1, MICIN2, EAROUT, LINEOUT are documented in the Allwinner A64 User Manual, “Audio Codec”, Page 278.

I hope this will get us started :-)

(Kudos to WhiteHexagon)

§13 Appendix: Troubleshoot LTE IP Multimedia Subsystem

What’s LTE IMS?

An LTE Mobile Network handles Phone Calls and SMS Messages in two ways…

LTE IMS (IP Multimedia Subsystem) is the newer Packet-Switched way to handle Phone Calls and SMS Messages in an LTE Network.

Is there a problem with LTE IMS?

We tested with PinePhone two quirky SIM Cards

But when we swap the Second SIM Card (SIMBA) back to the First SIM Card (M1)…

SMS magically works for the First SIM Card (M1)! How bizarre.

Is this caused by LTE IMS?

Some folks suggested we should force LTE IMS to be Disabled

// Force LTE IMS to be Disabled
AT+QCFG="ims",2

(Source)

(See “AT+QCFG=ims”, Page 102)

But maybe for some SIM Cards, we should force LTE IMS to be Enabled

// Force LTE IMS to be Enabled
AT+QCFG="ims",1

(See “AT+QCFG=ims”, Page 102)

That’s because Quectel says we should Force-Enable LTE IMS to support VoLTE (Voice-over-LTE)

“You can use AT+QCFG="ims",1 to restart the module…”

“And query AT+QCFG="ims" again to use the VoLTE function”

(Source)

TODO: Run AT+QCFG="ims" on both SIM Cards to show IMS state

TODO: Run AT+QCFG="ims",1 or AT+QCFG="ims",2 to enable / disable IMS on both SIM Cards

TODO: Fetch Mobile Operator and auto-enable / disable LTE IMS according to Mobile Operator

§14 Appendix: SMS PDU Format

Earlier we saw this command for sending SMS in PDU Mode…

What’s the PDU Length?

// Send an SMS with PDU Length of 41 bytes (excluding SMSC)
Command: AT+CMGS=41

Our SMS Message PDU has 42 total bytes (assuming 5 bytes in our PDU Phone Number)…

"00"  // Length of SMSC information (None)
"11"  // SMS-SUBMIT message
"00"  // TP-Message-Reference: 00 to let the phone set the message reference number itself
"0A"  // TODO: Address-Length: Length of phone number (Assume 10  Decimal Digits in Phone Number)
"91"  // Type-of-Address: 91 for International Format of phone number
PHONE_NUMBER_PDU  // TODO: Assume 5 bytes in PDU Phone Number (10 Decimal Digits)
"00"  // TP-PID: Protocol identifier
"08"  // TP-DCS: Data coding scheme
"01"  // TP-Validity-Period
"1C"  // TP-User-Data-Length: Length of Encoded Message Text in bytes
// TP-User-Data: Assume 28 bytes in Encoded Message Text
"00480065006C006C006F002C005100750065006300740065006C0021"

But PDU Length excludes the SMS Centre Information. (First Byte)

Thus our PDU Length is 41 bytes

// Send SMS Command
const char cmd[] = 
  "AT+CMGS="
  "41"  // TODO: PDU Length in bytes, excluding the Length of SMSC
  "\r";

Remember to update the PDU Length according to your phone number and message text.

What do the PDU Fields mean?

"00"  // Length of SMSC information (None)
"11"  // SMS-SUBMIT message
"00"  // TP-Message-Reference: 00 to let the phone set the message reference number itself
"0A"  // TODO: Address-Length: Length of phone number (Assume 10 Decimal Digits in Phone Number)
"91"  // Type-of-Address: 91 for International Format of phone number
PHONE_NUMBER_PDU  // TODO: Assume 5 bytes in PDU Phone Number (10 Decimal Digits)
"00"  // TP-PID: Protocol identifier
"08"  // TP-DCS: Data coding scheme
"01"  // TP-Validity-Period
"1C"  // TP-User-Data-Length: Length of Encoded Message Text in bytes
// TP-User-Data: Assume 28 bytes in Encoded Message Text
"00480065006C006C006F002C005100750065006300740065006C0021"

Let’s talk about the Message Text Encoding…

§15 Appendix: SMS PDU Message Encoding

How do we encode the Message Text in PDU Mode?

From the previous section we see that the Message Text is encoded with UCS2 Character Set

The UCS2 Encoding is actually Unicode UTF-16

“the SMS standard specifies UCS-2, but almost all users actually implement UTF-16 so that emojis work”

(Source)

So this Encoded Message Text…

// TP-User-Data: Message Text encoded with UCS2 Character Set
"00480065006C006C006F002C005100750065006300740065006C0021"

Comes from the Unicode UTF-16 Encoding of the Message Text “Hello,Quectel!”…

CharacterUTF-16 Encoding
H0048
e0065
l006C
l006C
o006F
,002C
Q0051
u0075
e0065
c0063
t0074
e0065
l006C
!0021

(These are 7-Bit ASCII Characters, so the UTF-16 Encoding looks identical to ASCII)