📝 4 May 2023
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…
Outgoing and Incoming Phone Calls over 4G
Send and receive SMS Text Messages
Why we prefer Encoded PDU Messages for SMS
Programming the 4G LTE Modem with Apache NuttX RTOS
Doing all these over UART vs USB
We begin with the 4G LTE Modem inside PinePhone…
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…
Via the UART Port (Serial)
Which is Slower: Up to 921.6 kbps
Via the USB Port (USB Serial)
Which is Faster: Up to 480 Mbps
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
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>
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
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…
“+CFUN: 1
”
LTE Modem is fully operational
“+CPIN: READY
”
4G SIM Card is all ready, no PIN needed
“+QUSIM: 1
”
Identifies the SIM Card Type
“+QIND: SMS DONE
”
SMS Storage is ready
“+QIND: PB DONE
”
Phonebook Storage is ready (for SIM Contacts)
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!
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…
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…
“AT+CREG?
”
Check Network Status
“AT+COPS?
”
Get Network Operator
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!
NuttX makes a Phone Call from PinePhone
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)
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
To send an SMS Message (in Text Mode), use these AT Commands…
“AT+CMGF=1
”
Select Text Mode for SMS
(Instead of PDU Mode)
“AT+CSCS="GSM"
”
Select (7-bit ASCII-like) GSM Character Set
(Instead of 16-bit UCS2 Unicode)
“AT+CMGS="+1234567890"
”
Send an SMS to the (imaginary) Phone Number “+1234567890
”
(Also works without country code, like “234567890
”)
Wait for Modem to respond with “>
”
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
)
Modem sends our SMS and responds with the Message ID…
+CMGS: 22
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)
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…
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…
Quectel GSM AT Commands Application Note
(Section 9.3.2 “Send SMS in PDU mode”, Page 26)
(For AT Commands)
(For PDU Format)
Let’s walk through the steps to send an SMS in PDU Mode…
“AT+CMGF=0
”
We select PDU Mode for SMS (instead of SMS Mode)
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"
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)
Wait for Modem to respond with “>
”
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)
Modem sends our SMS and responds with the Message ID…
+CMGS: 23
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!
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)
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.
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 writes | LTE 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!) |
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…
What if the LTE Modem never received our AT Command?
What if the OK or ERROR response is missing or corrupted?
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: ...
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…
Allwinner A64 UART Driver (for UART3)
Quectel EG25-G LTE Modem Driver (for PinePhone)
TODO: Disable UART2 and /dev/ttyS2 so that UART3 maps neatly to /dev/ttyS3. (See this)
I hope this article was helpful for understanding the internals of Phone Calls and Text Messaging on PinePhone…
Outgoing and Incoming Phone Calls over 4G
Send and receive SMS Text Messages
Why we prefer Encoded PDU Messages for SMS
Programming the 4G LTE Modem with Apache NuttX RTOS
Doing all these over UART vs USB
Upcoming AT Modem API for robust parsing, graceful timeout (plus retry) and notification callbacks
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…
Ring Indicator is connected to GPIO Pin PL6
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"
(See “AT+QCFG=urc/ri
”, Page 20)
LTE Modem is connected to Port PCM0 for 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…
io = 1
Digital PCM Output
mode = 1
Slave Mode
fsync = 0
Primary Mode (short-synchronization)
clock = 1
Clock Frequency is 256 kHz
format = 0
Data Format is 16-bit linear
sample = 0
Sampling Rate is 8 kHz
num_slots = 1
Number of Slot is 1
slot_mapping = 1
Slot Mapping Value is 1
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…
From above, the LTE Modem is connected to Allwinner A64 Port PCM0 for PCM Audio
A64 Port PCM0 is documented in the Allwinner A64 User Manual, “I2C/PCM”, Page 621
So we need to route the PCM Audio from PCM0 to PinePhone’s Microphone / Speaker
According to the PinePhone Schematic…
The Audio Connections are HPOUT, MIC1 (MICIN1), MIC2 (MICIN2), EAROUT, LINEOUT
HPOUT and MIC2 (MICIN2) are connected to the Headphone Jack
MIC1 (MICIN1) is connected to the Digital Video Connector (What’s this?)
EAROUT is connected to the Earpiece Speaker
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 :-)
What’s LTE IMS?
An LTE Mobile Network handles Phone Calls and SMS Messages in two ways…
Packet Switching is the Newer Way, created for 4G LTE
(Works like routing IP Packets over internet)
Circuit Switching is the Older Way, inherited from 3G UMTS
(Somewhat like plain old landline telephone point-to-point wires)
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…
First SIM Card (M1): OK for Phone Calls, Fail for SMS
// Outgoing SMS fails with Error 350
AT+CMGS="yourphonenumber"
...
+CMS ERROR: 350
Second SIM Card (SIMBA): Fail for Phone Calls, OK for SMS
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
But maybe for some SIM Cards, we should force LTE IMS to be Enabled…
// Force LTE IMS to be Enabled
AT+QCFG="ims",1
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”
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
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"
Length of SMSC information: “00
”
We use the default SMS Centre (SMSC), so the SMSC Info Length is 0.
(“AT+CSCA?
” shows the SMSC)
Short Message Transfer Protocol (SM-TL) Transfer Protocol Data Unit (TPDU) is SMS-SUBMIT Message: “11
”
TP-Message-Type-Indicator (TP-MTI, Bits 0 and 1) = 0b01
(SMS-SUBMIT):
Submit a message to SMSC for transmission.
TP-Validity-Period-Format (TP-VPF, Bits 3 and 4) = 0b10
(Relative Format):
Message Validity Period is in Relative Format.
(Value of Message Validity Period is in TP-Validity-Period below)
TP-Message-Reference (TP-MR): “00
”
“00
” will let the phone generate the Message Reference Number itself
Address-Length: “0A
”
Length of phone number
(Number of Decimal Digits in Phone Number, excluding “F
”)
Type-of-Address: “91
”
“91
” for International Format of Phone Number
Numbering Plan Identification (NPI, Bits 0 to 3) = 0b0001
(ISDN / Telephone Numbering Plan)
Type Of Number (TON, Bits 4 to 6) = 0b001
(International Number)
(Hence the mandatory Country Code)
EXT (Bit 7) = 1
(No Extension)
PHONE_NUMBER_PDU: Phone Number in PDU Format (nibbles swapped)
#define PHONE_NUMBER "+1234567890"
#define PHONE_NUMBER_PDU "2143658709"
Remember to insert “F
” if Phone Number has odd number of nibbles…
#define PHONE_NUMBER "+123456789"
#define PHONE_NUMBER_PDU "214365870F9"
Country Code is mandatory because Type Of Number (above) is International Number.
TP-Protocol-Identifier (TP-PID): “00
”
Default Store-and-Forward Short Message
TP-Data-Coding-Scheme (TP-DCS): “08
”
Message Text is encoded with UCS2 Unicode Character Set
TP-Validity-Period (TP-VP): “01
”
Message is valid for 10 minutes, relative to current time:
("01" + 1) x 5
minutes
(See TP-Validity-Period-Format above)
TP-User-Data-Length (TP-UDL): “1C
”
Length of Encoded Message Text (in bytes)
TP-User-Data (TP-UD): Encoded Message Text
Message Text is encoded with UCS2 Unicode Character Set
(Because of TP-Data-Coding-Scheme above)
Let’s talk about the Message Text 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…
TP-Data-Coding-Scheme (TP-DCS): “08
”
Message Text is encoded in UCS2 Unicode 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”
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!
”…
Character | UTF-16 Encoding |
---|---|
H | 0048 |
e | 0065 |
l | 006C |
l | 006C |
o | 006F |
, | 002C |
Q | 0051 |
u | 0075 |
e | 0065 |
c | 0063 |
t | 0074 |
e | 0065 |
l | 006C |
! | 0021 |
(These are 7-Bit ASCII Characters, so the UTF-16 Encoding looks identical to ASCII)