User Guide¶
Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.
This user guide aims to provide users with comprehensive usage instructions for the Quectel Pi M1 / L1 smart main control board, covering device specification introduction, power supply, display, and functional descriptions, operation methods, and precautions for various peripherals (camera, Wi-Fi/BT, cellular network, Ethernet, audio, USB, etc.). It helps users quickly understand the device performance, master the usage process, solve application problems, and provide clear and detailed reference for the debugging, deployment, operation and maintenance of the device.
Specification Introduction¶
Please go to the Specification Introduction page for details.
Display¶
MIPI¶
This document details the usage of the MIPI—DSI display for the Quectel Pi M1 / L1 smart main control board, and takes the Waveshare 8-inch DSI interface IPS capacitive touch screen as an example for detailed explanation.
MIPI Screen Connection¶
Hardware Interface Connection¶
This screen has 4 power supply interfaces, you can choose any one of them. The example selects the “1” interface for power supply.
Type-C interface.
I2C communication and power supply interface.
5V input and output interface.
I2C communication and power supply interface. Align the screw holes with the Quectel Pi M1 / L1 smart main control board and tighten them to ensure a firm connection between the pogo pins and the 40-pin header.
Power On and Boot Up¶
After powering on and booting up, you will enter the system desktop.
Function Usage¶
How to Use the Touch Function¶
After connecting the MIPI display and powering on the device, you can use the touch function directly.
Adjust Audio and Display¶
For specific audio configuration, please go to: Android Desktop → Menu → Settings → Sound & Vibration to adjust audio settings.
For specific display configuration, please go to: Android Desktop → Menu → Settings → Display to adjust display settings.
Note: Only the operation diagrams for screen and sound are shown here. The operation steps for other settings such as battery, storage, wallpaper, location information, etc. are consistent with those for screen and sound.
Video Playback Function¶
Video Playback Function on Android Desktop
Use the adb tool to push the video file to the smart main control board.
adb push "E:\FeiShu\Animal World.mp4" /sdcard/DCIM # Push the relevant video to the DCIM directory of the SD card on the smart main control board
# Enter the smart main control board
su # Switch to the root user
am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/DCIM/Animal World.mp4 # Rescan this file in the media library
For specific operations, please go to:
Android Desktop → File Manager → Video File → Play Video.
Audio Playback Function¶
Audio Playback Function on Android Desktop:
Note: To play audio, you need to connect a speaker to the Quectel Pi M1 / L1 smart main control board.
Use the adb tool to push the audio file to the smart main control board.
adb push E:\eage\test.mp3 /sdcard/Music # Push the audio to the Music directory of the SD card on the smart main control board
# Enter the smart main control board
su # Switch to the root user
am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/Music/test.mp3 # Rescan this file in the media library
For specific operations, please go to:
Android Desktop → File Manager → Play Audio.
HDMI¶
This section details the usage of the Micro HDMI display for the Quectel Pi M1 / L1 smart main control board, and takes the 10.1-inch HDMI display as an example for detailed explanation.
Micro HDMI Screen Connection¶
Hardware Interface Connection¶
Power On and Boot Up¶
After powering on and booting up, you will enter the system desktop.
Function Usage¶
How to Use the Touch Function¶
After the HDMI display is powered on and booted up, you need to connect the USB port to the HDMI screen.
Note: This function requires an HDMI screen with touch function.
Connect according to the following figure:
Adjust Audio and Display¶
Note: After connecting to an HDMI monitor, the resolution is adaptive and cannot be changed. Currently supported HDMI resolutions are 3840 × 2160, 1920 × 1080, 1280 × 720, 720 × 480
For specific audio configuration, please go to: Android Desktop → Menu → Settings → Sound & Vibration to adjust audio settings.
For specific display configuration, please go to: Android Desktop → Menu → Settings → Display to adjust display settings.
Video Playback Function¶
Video Playback Function on Android Desktop
Use the adb tool to push the video file to the smart main control board.
adb push "E:\FeiShu\Animal World.mp4" /sdcard/DCIM # Push the relevant video to the DCIM directory of the SD card on the smart main control board
# Enter the smart main control board
su # Switch to the root user
am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/DCIM/Animal World.mp4 # Rescan this file in the media library
For specific operations, please go to:
Android Desktop → File Manager → Video File → Play Video.
Audio Playback Function¶
Audio playback function on Android Desktop:
Note: To play audio, the speaker needs to be connected to the Quectel Pi M1 / L1 intelligent main control board.
Use the adb tool to push the audio file to the intelligent main control board.
```SHELL
adb push E:\eage\test.mp3 /sdcard/Music #Push the audio to the Music directory of the SD card on the intelligent main control board
#Access the intelligent main control board
su #Switch to root user
am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/Music/test.mp3 #Rescan this file in the media library
1. For specific configurations, follow the path: `Android Desktop -> File Manager -> Play Audio`.
```{image} images/image_VMpJbv4X0omvUCxCQxVcOCS9nYb.webp
:width: 2548px
:height: 1485px
:align: center
Advanced Functions¶
Hot Plugging of HDMI Screen¶
HDMI screens support hot plugging function. You can plug and unplug the HDMI screen while the system is running, and the system will automatically recognize it and display the picture.
How to Use Hot Plugging¶
When the system is running, directly insert the HDMI screen into the Micro HDMI interface of the intelligent main control board.
The system will automatically detect the HDMI device and start the display output.
If you need to disconnect the HDMI screen, you can directly pull out the Micro HDMI cable, and the system will automatically switch the display output.
Notes on Hot Plugging¶
The hot plugging function only supports displays connected through the Micro HDMI interface.
Screens connected using a DP to Micro HDMI adapter may not support hot plugging normally.
Please avoid frequent operations during plugging and unplugging. It is recommended that the interval between each plugging and unplugging be no less than 5 seconds.
Camera¶
This document takes the IMX219 camera module as an example, introduces in detail the identification and connection methods of the MIPI CSI camera interface of the Quectel Pi M1 / L1 intelligent main control board, and provides guidance on function usage and solutions to common problems.
Hardware Interface¶
The Quectel Pi M1 / L1 intelligent main control board supports a total of two MIPI CSI interfaces for cameras, as shown in the following figure:
Quick Start¶
Connect the camera as shown in the figure below. When connecting, pull up the black latch part of the connector, insert the FPC cable into the first CSI card slot, note that the gold fingers of the cable face the metal contact surface inside the board, press down the latch, and confirm that the FPC cable is stable and not loose.
Refer to the display section to connect the display screen.
Function Usage¶
Preview¶
The configurations and parameters required for the IMX219 camera have been pre-integrated in the kernel. The specific operation steps are as follows: Connect the camera to **Quectel-Pi-M1 / L1 , Power on and boot -> Android Desktop -> Camera -> Camera Interface`.
Video Recording¶
Specific operation steps: Camera Interface -> Click Record -> Start recording video -> Save to album. The recorded video files can also be found through the file system.
Photo Taking¶
Specific operation steps: Camera Interface -> Click Photo -> Start taking photos -> Save to album. The photo files can also be found through the file system.
Wi-Fi/BT¶
The Quectel Pi M1 / L1 intelligent main control board supports Wi-Fi and Bluetooth (BT) connections. Users need to connect a MIPI display, boot into the Android desktop, and then select the target network or device to connect to, so as to realize wireless communication.
Wi-Fi Function Usage¶
Enable and Connect Wi-Fi¶
The specific operation steps are as follows: Connect MIPI display -> Power on and boot -> Android Desktop -> Pull down the desktop -> Click the Wi-Fi icon -> Enter the Wi-Fi interface.
Bluetooth Function Usage¶
The Quectel Pi M1 / L1 intelligent main control board has a built-in Bluetooth function and supports connecting Bluetooth peripherals.
Enable Bluetooth¶
The specific operation steps are as follows: Connect MIPI display -> Power on and boot -> Android Desktop -> Pull down the desktop -> Click the BT icon -> Enter the Bluetooth (BT) interface.
Cellular Network¶
Cellular Network is a wide area network architecture based on radio communication, widely used in mobile communication and Internet of Things devices. Its core feature is dividing the coverage area into multiple “cells”, each of which is provided with wireless access by a base station. Terminal devices connect to the operator’s core network through the base station to realize voice, SMS and data services.
This section mainly introduces the method of using USIM to connect to the network.
Supports dual SIM dual standby. This document only introduces USIM.
Only supports 4G LTE network.
Hardware Connection¶
SIM card slot
Nano SIM pluggable card holder.
SIM card antenna interface
IPEX-4TH antenna holder.
Quick Start¶
Boot Up¶
After entering the system, you can check whether the SIM card is normally recognized. The normal recognition is shown in the following figure.
Network Connection Confirmation¶
Use adb to enter the system and ping 8.8.8.8 to confirm whether the network connection is normal.
Function Usage¶
Turn on Cellular Network¶
Swipe down from the top of the screen, the drop-down menu will display the Internet option. Click to select the corresponding LTE toggle switch, and it will light up to indicate the on state.
Turn off Cellular Network¶
Swipe down from the top of the screen, the drop-down menu will display the Internet option. Click to select the corresponding LTE toggle switch, and it will dim to indicate the off state.
Frequently Asked Questions¶
What may be the reason why the wireless network card is activated successfully but network communication cannot be performed?
Generally, the following two situations may cause this problem:
SIM card arrears For some SIM cards in arrears, the module can still successfully register to the network and activate the cellular wireless network card, but the operator will restrict the module from performing actual network communication.
APN configuration problem The SIM card supports accessing a special network through a specific APN, and the user has also configured the APN for accessing the special network. At this time, the device accesses the special network instead of the Internet. If the user attempts to access the Internet in this situation, the access will fail. For specific APN configuration, please go to Settings -> Network & Internet -> Internet -> CMCC (connected/LTE) -> Access Point network in sequence, and then configure a new APN for network connection.
Ethernet¶
The Quectel Pi M1 / L1 smart main control board is equipped with 1 standard RJ45 interface, supporting Ethernet with a communication rate of 10/100/1000 Mbps.
Requirements for Ethernet cables: Only supports straight-through cables, does not support crossover cables.
Hardware Interface¶
View Network Configuration¶
The system enables DHCP to automatically obtain network configuration by default.
Enter the ifconfig command in the terminal to view the network interface address:
ifconfig
Output example: The 192.168.90.116 after inet addr: is the local IP address.
Enter the ip route command in the terminal to view the network routing information:
ip route
Output example:
View DNS server address:
dumpsys connectivity | grep -o ' DnsAddresses: \[[^]]*\]'
Output example:
Test Network¶
**Execute theping**command to test the network connection
Execute the
ping 8.8.8.8command. If the ping is successful (response is received), it indicates that the external network connection is normal.Execute the
ping www.example.comcommand. If the ping is successful, it indicates that the external network connection is normal and the DNS resolution function is normal.
Audio Function¶
This document provides a concise guide to the operation of the audio system. The system adopts the dual-layer audio architecture of ALSA + PulseAudio, providing a complete audio solution from the underlying driver to the application layer.
At the hardware level, the system supports:
External microphone input (external SH1.0-4P audio to headphone interface)
External speaker/headphone output, which can realize full-duplex communication of headphone playback and microphone input
At the software level, the system provides a set of standard audio configuration and debugging commands for completing common operations such as device inspection, recording, playback and volume control.
With this document, you can quickly locate and solve most audio-related problems without in-depth complex configuration.
Hardware Interface¶
External SH1.0-4P audio to headphone interface
On-board MEMS microphone
View Sound Card Information¶
You can check the loading status of the system sound card and PCM devices through the following commands:
View the sound card list:
cat /proc/asound/cards
View the list of assigned pcm streams:
cat /proc/asound/pcm
Note: If the system fails to detect sound card information or PCM streams, both ALSA and PulseAudio cannot work properly.
Function Usage¶
Audio Recording¶
Recording under Android system
tinymix "TX DEC0 MUX" "SWR_MIC"
tinymix "ADC2 ChMap" "SWRM_TX2_CH1"
tinymix "TX SMIC MUX0" "SWR_MIC4"
tinymix "TX_AIF1_CAP Mixer DEC0" "1"
tinymix "ADC2_MIXER Switch" "1"
tinymix "ADC2 MUX" "INP3"
tinymix 'TX_AIF1_CAP Mixer DEC2' '1'
agmcap ./testMic3.wav -D 100 -d 101 -c 1 -r 48000 -i "CODEC_DMA-LPAIF_RXTX-TX-3"
End recording
Press “Ctrl” + “Z” to end the recording:
Headset Microphone Recording¶
Recording under Android system
tinymix "TX DEC0 MUX" "SWR_MIC"
tinymix "TX SMIC MUX0" "SWR_MIC5"
tinymix "DEC0_BCS Switch" "1"
tinymix "TX_AIF1_CAP Mixer DEC0" "1"
tinymix "ADC2 ChMap" "SWRM_TX2_CH2"
tinymix "MBHC ChMap" "SWRM_TX3_CH3"
tinymix "ADC2_MIXER Switch" "1"
tinymix "ADC2 MUX" "INP2"
tinymix "BCS Channel" "CH10"
agmcap ./rec2.wav -D 100 -d 101 –c 1 -r 48000 -i "CODEC_DMA-LPAIF_RXTX-TX-3"
Note: During operation, make sure that the current directory has write permission.
Audio Playback¶
The onboard speaker is not soldered, so use the external headset channel for playback.
Headset Playback
Playback under Android system (3.5 mm headset)
adb root
adb shell
cd /data/local/tmp
tinymix "RX_MACRO RX0 MUX" "AIF1_PB"
tinymix "RX_MACRO RX1 MUX" "AIF1_PB"
tinymix "RX INT0_1 MIX1 INP0" "RX0"
tinymix "RX INT1_1 MIX1 INP0" "RX1"
tinymix "RX INT0 DEM MUX" "CLSH_DSM_OUT"
tinymix "RX INT1 DEM MUX" "CLSH_DSM_OUT"
tinymix "RX_COMP1 Switch" "1"
tinymix "RX_COMP2 Switch" "1"
tinymix "HPHL_RDAC Switch" "1"
tinymix "HPHR_RDAC Switch" "1"
agmplay ./vvdn_ring.wav -D 100 -d 100 -i CODEC_DMA-LPAIF_RXTX-RX-0 -dkv 0xA2000002
Troubleshooting¶
Problem |
Possible Causes |
|---|---|
No recording file generated |
|
Audio playback failed |
|
USB¶
USB host refers to the system or hardware that can control and manage USB devices. It is responsible for providing power, data transmission and management functions for connected USB devices (such as USB flash drives, keyboards, mice, etc.).
The Quectel Pi M1/L1 intelligent main control board comes with 2 standard USB 3.0 Type-A interfaces, 2 standard USB 2.0 Type-A interfaces and 1 USB 3.1 Type-C interface. The maximum data transfer rate of the USB 2.0 interface can reach 480 Mbps, and that of the USB 3.1 interface can reach 5 Gbps.
Function Usage¶
Connect a USB Flash Drive¶
Insert a USB flash drive into the Type-A port.
The USB device is displayed in the notification bar.
Pull down to expand the notification bar.
Click the EXPLORE button to browse the contents of the USB flash drive.
SD Card¶
SD card (Secure Digital Card) is a widely used portable storage medium, which has the characteristics of small size, large capacity, low power consumption and strong ease of use. It is often used in mobile phones, cameras, embedded devices and single-board computers to store system files, applications and data, and is one of the most mainstream storage solutions in modern mobile and embedded devices.
Hardware Interface¶
The Quectel Pi M1/L1 intelligent main control board is equipped with a micro SD + Nano SIM 2-in-1 card slot, which supports the SD 3.0 protocol.
Note: The upper slot is the micro SD card slot, and the lower one is the Nano SIM card slot.
Function Usage¶
System Recognition¶
After inserting the SD card, you can use the following command to confirm whether the system has recognized the device:
fdisk -l
By comparing the output information before and after inserting the SD card, you can see that the system has successfully recognized the SD card:
Mounting and Unmounting¶
View the mount path:
df -h
As shown below, the device has been automatically mounted to /mnt/sdcard:
View the files in the mount directory:
ls /mnt/sdcard
Mount to another directory:
sudo mkdir -p /media/sdcard
sudo mount /dev/mmcblk1p1 /media/sdcard
View the mount result:
ls /media/sdcard
Unmount after use:
sudo umount /media/sdcard
Tip: Unmount the SD card before removing it to prevent data corruption.
File Operations and Permissions¶
When ordinary users access the SD card, they may encounter the problem of insufficient permissions. You can change the ownership of the mount point with the following command:
sudo chown <username> /mnt/sdcard
sudo chmod 755 /mnt/sdcard
Replace <username> with the currently logged in username. After the modification, you can read and write files normally in the /mnt/sdcard directory.
ADC¶
The ADC subsystem consists of a signal multiplexer and a voltage analog-to-digital converter (VADC). It is used to route internal and external analog signals to the VADC through multiplexing, and complete the conversion from analog quantity to digital quantity.
ADC Module Hardware Architecture Diagram¶
ADC Supported by M1 Module¶
The ADCs supported by the module are listed below:
Interface |
Pin Number |
Pin Name |
PM4250 Pin Number |
Voltage Domain |
MPM Interrupt |
Pin Description |
|---|---|---|---|---|---|---|
ADC |
128 |
ADC0 |
GPIO4 |
1.8 V |
- |
PMU_GPIO04 |
ADC |
185 |
ADC1 |
GPIO7 |
1.8 V |
- |
PMU_GPIO07 |
ADC Location¶
The location of the ADC interface on the smart main control board is shown in the figure below:
Location of ADC on the smart main control board
ADC Node Usage Example¶
The ADC node is mainly used to view the ADC value input to this node.
Usage Method¶
Use an adjustable DC power supply as the input signal source during testing:
Connect the positive pole of the power supply to the ADC0 or ADC1 pin.
Connect the power supply GND to the smart main control board GND.
The corresponding relationships and input characteristics of each channel are as follows:
ADC0:
Connected to GPIO4
Node:
in_voltage_adc_gpio4_inputSupported input voltage range: approximately 0~5.25 V
ADC1:
Connected to GPIO7
Node:
in_voltage_adc_gpio7_inputSupported input voltage range: approximately 0~5.25 V
After setting the DC power supply to output the target voltage, you can read the ADC sampling result (unit: μV) with the following commands:
adb shell
cd /sys/bus/iio/devices/iio:device0
cat in_voltage_adc_gpio4_input
cat in_voltage_adc_gpio7_input
Test Code¶
The following sample code demonstrates the basic method of reading ADC voltage values in an application. The program reads the original ADC value (unit: μV) by accessing the node file corresponding to the ADC, and converts it to an integer for subsequent processing.
Click to expand/collapse: ADC reading test code (can be compiled with NDK by yourself)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main()
{
int fd = -1;
int adc_value;
char buff[64] = {0};
// Open the ADC node file (ADC0 corresponds to GPIO4)
fd = open("/sys/bus/iio/devices/iio:device0/in_voltage_adc_gpio4_input", O_RDONLY);
if(fd < 0){
perror("open error\n");
return fd;
}
// Read ADC value
int n_read = read(fd, buff, sizeof(buff));
if(n_read < 0) {
perror("read error\n");
close(fd);
return -1;
}
// Convert the read string to an integer (unit: μV)
adc_value = atol(buff);
printf("read %d bytes, content: %s\n", n_read, buff);
printf("ADC value: %d μV\n", adc_value);
close(fd);
return 0;
}
Code Description:
Use the
open()function to open the ADC node file/sys/bus/iio/devices/iio:device0/in_voltage_adc_gpio4_input.Use the
read()function to read the original ADC value (in string format, unit: μV).Use the
atol()function to convert the string to a long integer value.The unit of the read ADC value is microvolt (μV), which can be converted to a voltage value as needed.
Compilation and Execution:
# Compile with NDK
# Push the compiled executable file to the device and run it
adb push test_adc /data/local/tmp/test_adc
adb shell "chmod +x /data/local/tmp/test_adc"
adb shell "/data/local/tmp/test_adc"
Test Data¶
ADC Pin Specifications¶
The ADC pin definitions on the module are as follows:
Interface |
Module Pin Number |
Module Pin Name |
Module I/O |
NET Name (Multiplexing Function) |
Description |
|---|---|---|---|---|---|
ADC |
128 |
ADC0 |
I |
ADC0 |
1) Connector input voltage range: 0–5.25 V. |
ADC |
185 |
ADC1 |
I |
ADC1 |
Same electrical characteristics as ADC0 (0–5.25 V input range, RH = 18 kΩ, RL = 10 kΩ, ADC pin voltage = Vin × 10/28). |
ADC0 Test Data¶
Voltage Source |
ADC0 (μV) |
Theoretical Value (V) |
|---|---|---|
1.8 V |
638408 |
1.8 V×10/28 = 0.642857 |
3 V |
1066414 |
3 V×10/28 = 1.07143 |
5 V |
1784623 |
5 V×10/28 = 1.78571 |
ADC1 Test Data¶
Voltage Source |
ADC1 (μV) |
Theoretical Value (V) |
|---|---|---|
1.8 V |
640549 |
1.8 V×10/28 = 0.642857 |
3 V |
1069269 |
3 V×10/28 = 1.07143 |
5 V |
1789814 |
5 V×10/28 = 1.78571 |
ADC Supported by L1 Module¶
Interface |
Pin Number |
Pin Name |
PM4250 Pin Number |
Voltage Domain |
MPM Interrupt |
Pin Description |
|---|---|---|---|---|---|---|
ADC |
128 |
ADC0 |
GPIO4 |
1.8 V |
- |
PMU_GPIO04 |
ADC Location¶
The location of the ADC interface on the smart main control board is shown in the figure below:
ADC Node Usage Example¶
The ADC node is mainly used to view the ADC value input to this node.
Usage Method¶
Use an adjustable DC power supply as the input signal source during testing:
Connect the positive pole of the power supply to the ADC0 or ADC1 pin.
Connect the power supply GND to the smart main control board GND.
The corresponding relationships and input characteristics of each channel are as follows:
ADC0:
Connected to GPIO4
Node:
in_voltage_adc_gpio6_inputSupported input voltage range: approximately 0~5.25 V
After setting the DC power supply to output the target voltage, you can read the ADC sampling result (unit: μV) with the following commands:
adb shell
cd /sys/bus/iio/devices/iio:device0
cat in_voltage_adc_gpio6_input
ADC reading example output
Test Code¶
The following sample code demonstrates the basic method of reading ADC voltage values in an application. The program reads the original ADC value (unit: μV) by accessing the node file corresponding to the ADC, and converts it to an integer for subsequent processing.
Click to expand/collapse: ADC reading test code (can be compiled with NDK by yourself)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main()
{
int fd = -1;
int adc_value;
char buff[64] = {0};
// Open the ADC node file (ADC0 corresponds to GPIO4)
fd = open("/sys/bus/iio/devices/iio:device0/in_voltage_adc_gpio6_input", O_RDONLY);
if(fd < 0){
perror("open error\n");
return fd;
}
// Read ADC value
int n_read = read(fd, buff, sizeof(buff));
if(n_read < 0) {
perror("read error\n");
close(fd);
return -1;
}
// Convert the read string to an integer (unit: μV)
adc_value = atol(buff);
printf("read %d bytes, content: %s\n", n_read, buff);
printf("ADC value: %d μV\n", adc_value);
close(fd);
return 0;
}
Code Description:
Use the
open()function to open the ADC node file/sys/bus/iio/devices/iio:device0/in_voltage_adc_gpio6_input.Use the
read()function to read the original ADC value (in string format, unit: μV).Use the
atol()function to convert the string to a long integer value.The unit of the read ADC value is microvolt (μV), which can be converted to a voltage value as needed.
Compilation and Execution:
# Compile with NDK
# Push the compiled executable file to the device and run it
adb push test_adc /data/local/tmp/test_adc
adb shell "chmod +x /data/local/tmp/test_adc"
adb shell "/data/local/tmp/test_adc"
Test Data¶
ADC Pin Specifications¶
The ADC pin definitions on the module are as follows:
Interface |
Module Pin Number |
Module Pin Name |
Module I/O |
NET Name (Multiplexing Function) |
Description |
|---|---|---|---|---|---|
ADC |
128 |
ADC0 |
I |
ADC0 |
1) Connector input voltage range: 0–5.25 V. |
ADC0 Test Data¶
Voltage Source |
ADC0 (μV) |
Theoretical Value (V) |
|---|---|---|
1.8 V |
638408 |
1.8 V×10/28 = 0.642857 |
3 V |
1066414 |
3 V×10/28 = 1.07143 |
5 V |
1784623 |
5 V×10/28 = 1.78571 |
40-Pin Expansion¶
The Quectel Pi M1 smart main control board provides a standard 40-pin GPIO expansion interface, supporting multiple peripheral interfaces such as GPIO, I2C, SPI, UART, and PWM. The following describes how to test the functions of these interfaces.
Pin Definition¶
M1 GPIO pin correspondence list
L1 GPIO pin correspondence list
GPIO Test¶
This section takes testing the pin7 of the M1 40-Pin as an example to demonstrate how to use the GPIO function. The gpio_num corresponding to pin7 is 83.
The GPIO test code for L1 can be reused, but you need to modify the GPIO value corresponding to the pin in the code.
Hardware Connection¶
Connection Method 1: Measure Voltage with a Multimeter
Connect the pin7 (GPIO_83) pin to the positive pole of the multimeter, and the pin6 (GND) pin to the negative pole of the multimeter. You can measure the voltage output by the pin through the multimeter to verify whether the GPIO function is normal.
Connection Method 2: Use the GPIO Expansion Board Indicator
You can also test the GPIO high and low levels by plugging a Raspberry Pi 4B/3B GPIO expansion board into the 40-Pin expansion interface. This expansion board leads out each GPIO pin and is equipped with a corresponding indicator light. You can configure the GPIO pin to be tested as an output and connect it to the expansion board. When the GPIO outputs a high level, the small light turns on, and when it outputs a low level, the small light turns off, so that you can intuitively observe whether the GPIO level change meets the expectations. For more information about this expansion board, please refer to the GPIO terminal expansion board entry in the Accessories chapter.
Test Method¶
The following examples are based on the Android platform. The C code needs to be cross‑compiled using the NDK before it can be used. pin_control_sysfs source code (can be compiled with NDK on your own)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
// GPIO pin mapping table (SC200U Gamma Linux project)
typedef struct {
int pin;
int gpio_num; // Linux sysfs GPIO number
} pin_map_t;
// GPIO mapping according to Bengal platform (consistent with Android project)
static const pin_map_t pin_mapping[] = {
{3, 494}, {5, 495}, {7, 481}, {8, 467}, {10, 468}, {11, 504}, {12, 496}, {13, 429},
{15, 469}, {16, 482}, {18, 423}, {19, 399}, {21, 398}, {22, 478}, {23, 400}, {24, 401},
{26, 480}, {27, 412}, {28, 413}, {29, 403}, {31, 402}, {32, 465}, {33, 396}, {35, 497},
{36, 414}, {37, 415}, {38, 499}, {40, 498}
};
#define PIN_COUNT (sizeof(pin_mapping) / sizeof(pin_map_t))
/**
* Get GPIO mapping information according to pin number
*/
const pin_map_t* get_pin_info(int pin) {
for (int i = 0; i < PIN_COUNT; i++) {
if (pin_mapping[i].pin == pin) {
return &pin_mapping[i];
}
}
return NULL;
}
/**
* Export GPIO to sysfs
*/
int export_gpio(int gpio_num) {
char path[64];
int fd;
// Check if GPIO has been exported
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d", gpio_num);
if (access(path, F_OK) == 0) {
return 0; // Already exported
}
// Export GPIO
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
printf("Failed to open /sys/class/gpio/export: %s\n", strerror(errno));
return -1;
}
char gpio_str[8];
snprintf(gpio_str, sizeof(gpio_str), "%d", gpio_num);
if (write(fd, gpio_str, strlen(gpio_str)) < 0) {
// If the GPIO has been exported, an error will be returned when writing, ignore this non-fatal issue
if (errno != EBUSY) {
printf("Failed to export GPIO%d: %s\n", gpio_num, strerror(errno));
close(fd);
return -1;
}
}
close(fd);
usleep(100000); // Delay 100ms, wait for sysfs node creation to complete
return 0;
}
/**
* Unexport GPIO from sysfs
*/
int unexport_gpio(int gpio_num) {
int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (fd < 0) {
return -1;
}
char gpio_str[8];
snprintf(gpio_str, sizeof(gpio_str), "%d", gpio_num);
write(fd, gpio_str, strlen(gpio_str));
close(fd);
return 0;
}
/**
* Set GPIO direction
*/
int set_gpio_direction(int gpio_num, const char *direction) {
char path[64];
int fd;
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", gpio_num);
fd = open(path, O_WRONLY);
if (fd < 0) {
printf("Failed to open %s: %s\n", path, strerror(errno));
return -1;
}
if (write(fd, direction, strlen(direction)) < 0) {
printf("Failed to set GPIO%d direction: %s\n", gpio_num, strerror(errno));
close(fd);
return -1;
}
close(fd);
return 0;
}
/**
* Set GPIO value
*/
int set_gpio_value(int gpio_num, int value) {
char path[64];
int fd;
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio_num);
fd = open(path, O_WRONLY);
if (fd < 0) {
printf("Failed to open %s: %s\n", path, strerror(errno));
return -1;
}
char value_str[2];
snprintf(value_str, sizeof(value_str), "%d", value);
if (write(fd, value_str, 1) < 0) {
printf("Failed to set GPIO%d value: %s\n", gpio_num, strerror(errno));
close(fd);
return -1;
}
close(fd);
return 0;
}
/**
* Read GPIO value
*/
int get_gpio_value(int gpio_num, int *value) {
char path[64];
int fd;
char val[2];
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio_num);
fd = open(path, O_RDONLY);
if (fd < 0) {
printf("Failed to open %s: %s\n", path, strerror(errno));
return -1;
}
if (read(fd, val, 1) < 0) {
printf("Failed to read GPIO%d: %s\n", gpio_num, strerror(errno));
close(fd);
return -1;
}
close(fd);
*value = (val[0] == '1') ? 1 : 0;
return 0;
}
/**
* Use sysfs to control GPIO
* action: 0=pull low, 1=pull high, 2=read
*/
int control_gpio_sysfs(int gpio_num, int action) {
// Ensure GPIO is exported
if (export_gpio(gpio_num) < 0) {
return -1;
}
if (action == 0 || action == 1) {
// Set direction to output
if (set_gpio_direction(gpio_num, "out") < 0) {
return -1;
}
// Set value
if (set_gpio_value(gpio_num, action) < 0) {
return -1;
}
printf("✓ GPIO%d has been set to %s level\n", gpio_num, action ? "high" : "low");
// Verify whether the setting takes effect
usleep(50000);
int verify_value;
if (get_gpio_value(gpio_num, &verify_value) == 0) {
printf(" Verification: GPIO%d actual status: %s level (value: %d)\n",
gpio_num, verify_value ? "high" : "low", verify_value);
}
} else if (action == 2) {
// Read GPIO value
int value;
if (get_gpio_value(gpio_num, &value) == 0) {
printf("✓ GPIO%d current status: %s level (value: %d)\n",
gpio_num, value ? "high" : "low", value);
} else {
return -1;
}
}
return 0;
}
/**
* Read all pin statuses
*/
int read_all_pins() {
printf("========================================\n");
printf("=== Read all pin statuses ===\n");
printf("========================================\n");
int success_count = 0;
for (int i = 0; i < PIN_COUNT; i++) {
printf("pin%d (GPIO%d): ", pin_mapping[i].pin, pin_mapping[i].gpio_num);
fflush(stdout);
if (control_gpio_sysfs(pin_mapping[i].gpio_num, 2) == 0) {
success_count++;
} else {
printf("✗ Read failed\n");
}
}
printf("========================================\n");
printf("Successfully read %d/%lu pins\n", success_count, PIN_COUNT);
printf("========================================\n");
return 0;
}
/**
* Control all pins
*/
int control_all_pins(int action) {
printf("========================================\n");
printf("Start to set all pins to %s level...\n", action ? "high" : "low");
printf("========================================\n");
int success_count = 0;
for (int i = 0; i < PIN_COUNT; i++) {
printf("pin%d (GPIO%d): ", pin_mapping[i].pin, pin_mapping[i].gpio_num);
fflush(stdout);
if (control_gpio_sysfs(pin_mapping[i].gpio_num, action) == 0) {
success_count++;
} else {
printf("✗ Control failed\n");
}
}
printf("========================================\n");
printf("Successfully controlled %d/%lu pins\n", success_count, PIN_COUNT);
printf("========================================\n");
return (success_count == PIN_COUNT) ? 0 : 1;
}
/**
* Display help information
*/
void show_help(const char *prog_name) {
printf("Usage: %s <pin_number|all> <up|down|get>\n", prog_name);
printf("Example: %s pin3 up # Pull pin3 high\n", prog_name);
printf("Example: %s pin5 down # Pull pin5 low\n", prog_name);
printf("Example: %s pin7 get # Read pin7\n", prog_name);
printf("Example: %s all up # Pull all pins high\n", prog_name);
printf("Example: %s all down # Pull all pins low\n", prog_name);
printf("Example: %s all get # Read all pin statuses\n", prog_name);
printf("\nSupported pins:\n");
for (int i = 0; i < PIN_COUNT; i++) {
printf("pin%d(GPIO%d) ", pin_mapping[i].pin, pin_mapping[i].gpio_num);
if ((i + 1) % 4 == 0) printf("\n");
}
printf("\n");
}
/**
* Main function
*/
int main(int argc, char *argv[]) {
if (argc < 3) {
show_help(argv[0]);
return 1;
}
char *pin_str = argv[1];
char *action_str = argv[2];
// Parse action parameter
int action;
if (strcmp(action_str, "up") == 0) {
action = 1;
} else if (strcmp(action_str, "down") == 0) {
action = 0;
} else if (strcmp(action_str, "get") == 0) {
action = 2;
} else {
printf("Error: Action must be up, down or get\n");
return 1;
}
// Process all command
if (strcmp(pin_str, "all") == 0) {
if (action == 2) {
return read_all_pins();
} else {
return control_all_pins(action);
}
}
// Parse pin number
int pin;
if (sscanf(pin_str, "pin%d", &pin) != 1) {
printf("Error: Invalid pin format, please use pin3, pin5, etc.\n");
return 1;
}
const pin_map_t *pin_info = get_pin_info(pin);
if (pin_info == NULL) {
printf("Error: Unsupported pin pin%d\n", pin);
return 1;
}
printf("Controlling pin%d (GPIO%d)...\n", pin, pin_info->gpio_num);
return control_gpio_sysfs(pin_info->gpio_num, action);
}
Push the cross‑compiled executable file to the development board and grant permissions:
adb push <local storage path>/pin_control_sysfs /data/local/tmp/pin_control_sysfs
adb shell "chmod +x /data/local/tmp/pin_control_sysfs"
adb shell "ls -lh /data/local/tmp/pin_control_sysfs"
Enter the device-side shell and run the tool to view the usage:
adb shell
cd /data/local/tmp
./pin_control_sysfs
After running, the available commands and the supported 40-pin pin mapping will be printed. An example output is as follows:
Common operation examples:
Pull a single pin high:
./pin_control_sysfs pin7 upPull a single pin low:
./pin_control_sysfs pin7 downRead a single pin:
./pin_control_sysfs pin7 getOperate all pins:
./pin_control_sysfs all up | all down | all get
Tip: When running, ensure that the target pin is not occupied by other functions (such as I2C/SPI/UART). It is recommended to confirm whether the level change meets expectations through the expansion board indicator light or a multimeter first.
I2C Test¶
Pin3 and pin5 of the 40-pin interface are the default I2C data and clock pins. To test the I2C interface, we need to connect an external I2C device. Here we use the Waveshare Environment Sensor Expansion Board, and the corresponding device node is /dev/i2c-1.
Test Preparation¶
IC |
I2C Addr |
ID Reg |
Expected ID Return Value |
|---|---|---|---|
WSL25911FN |
0x29 |
0x12 |
0x50 |
BME280 |
0x76 |
0xD0 |
0x60 |
MPU9250 |
0x68 |
0x75 |
0x71 |
LTR390-UV-1 |
0x53 |
0x06 |
0xB2 |
PS: When reading the ID register for WSL25911FN here, the command bit (i.e. 0xA0) needs to be included, which means that the register 0x12 is actually written as 0xA0 | 0x12 = 0xB2 during access, and this is not required for others.
This test uses the Waveshare Environment Sensor Expansion Board, connected via the 40-pin interface.
Hardware connection diagram:
Test Method¶
adb shell #Enter ADB shell
ls -al /dev/i2c* #Check if I2C-1 exists in the corresponding directory
i2cdetect -y 1 #Check how many devices are connected to the corresponding I2C bus
I2C Read/Write¶
i2cget -y 1 0x53 0x05 b #Read the value of register 0x05 of the device with address 0x53 on i2c1, the size is one byte
i2cset -y 1 0x53 0x05 0x03 b #Set the value of register 0x05 of the device with address 0x53 on i2c1 to 0x03, the size is one byte
SPI Test¶
The device chip select nodes corresponding to the SPI function in the 40-pin interface are /dev/spidev0.0 and /dev/spidev0.1. Enter the following content in adb to view these nodes:
adb shell "ls /dev/spidev*"
UART Test¶
Among the 40-pin interface,pin8 and pin10 are configured with UART function by default, and the corresponding device node is /dev/ttyHS0.
View serial port devices
You can use the following command to view all serial port devices in the system:
```Bash
ls /dev/tty*
UART Loopback Test¶
This test verifies whether the serial port transceiving function is normal by short-circuiting pin8 and pin10.
Hardware connection: Short-circuit pin8 (TX) and pin10 (RX) of the 40-pin connector.
Execution Step: Create the source file
Create a new uart_loopback.c file with the following content:
uart_loopback source code (you can use NDK or cross-compile by yourself)
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <termios.h>#include <errno.h>#include <signal.h>// Global variable for signal handlingstatic int serial_fd = -1;static volatile int running = 1;// Signal handler functionvoid signal_handler(int sig) {printf("\nUser interrupted the test\n");
running = 0;if (serial_fd >= 0) {close(serial_fd);printf("Serial port closed\n");}exit(0);}// Configure serial port parametersint configure_serial(int fd, int baudrate) {struct termios tty;if (tcgetattr(fd, &tty) != 0) {printf("Failed to get serial port attributes: %s\n", strerror(errno));return -1;}// Set baud ratespeed_t speed;switch (baudrate) {case 9600: speed = B9600; break;case 19200: speed = B19200; break;case 38400: speed = B38400; break;case 57600: speed = B57600; break;case 115200: speed = B115200; break;case 230400: speed = B230400; break;default: speed = B115200; break;}cfsetospeed(&tty, speed);cfsetispeed(&tty, speed);// Configure serial port parameters
tty.c_cflag &= ~PARENB; // No parity check
tty.c_cflag &= ~CSTOPB; // 1 stop bit
tty.c_cflag &= ~CSIZE; // Clear data bit setting
tty.c_cflag |= CS8; // 8 data bits
tty.c_cflag &= ~CRTSCTS; // Disable hardware flow control
tty.c_cflag |= CREAD | CLOCAL; // Enable reception and local connection// Configure input mode
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control
tty.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Raw mode// Configure output mode
tty.c_oflag &= ~OPOST; // Raw output// Configure local mode
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Raw mode// Set timeout
tty.c_cc[VTIME] = 10; // 1 second timeout
tty.c_cc[VMIN] = 0; // Non-blocking readif (tcsetattr(fd, TCSANOW, &tty) != 0) {printf("Failed to set serial port attributes: %s\n", strerror(errno));return -1;}return 0;}// UART loopback test functionint serial_loopback_test(const char* port, int baudrate) {char test_data[] = "Hello, Serial Loopback!";char received_data[256];ssize_t bytes_written, bytes_read;// Open serial port device
serial_fd = open(port, O_RDWR | O_NOCTTY | O_SYNC);if (serial_fd < 0) {printf("Failed to open serial port %s: %s\n", port, strerror(errno));return -1;}// Configure serial portif (configure_serial(serial_fd, baudrate) != 0) {close(serial_fd);return -1;}printf("Serial port %s is open, starting loopback test (press Ctrl+C to exit)...\n", port);while (running) {// Clear receive buffertcflush(serial_fd, TCIOFLUSH);// Send data
bytes_written = write(serial_fd, test_data, strlen(test_data));if (bytes_written < 0) {printf("Failed to send data: %s\n", strerror(errno));break;}printf("Sent: %s\n", test_data);// Wait for data receptionusleep(100000); // 100ms// Read loopback data
bytes_read = read(serial_fd, received_data, sizeof(received_data) - 1);if (bytes_read < 0) {printf("Failed to read data: %s\n", strerror(errno));break;}
received_data[bytes_read] = '\0'; // Add string terminator// Verify resultif (bytes_read == strlen(test_data) &&
strncmp(received_data, test_data, strlen(test_data)) == 0) {printf("Received: %s → Test passed\n\n", received_data);} else {printf("Reception exception: Sent[%zu] vs Received[%zd] → Test failed\n",
strlen(test_data), bytes_read);if (bytes_read > 0) {printf("Received content: %s\n\n", received_data);} else {printf("No data received\n\n");}}sleep(1); // Repeat test every 1 second}close(serial_fd);printf("Serial port %s closed\n", port);return 0;}int main(int argc, char* argv[]) {const char* port = "/dev/ttyHS0"; // Default serial portint baudrate = 115200; // Default baud rate// Parse command line parametersif (argc >= 2) {
port = argv[1];}if (argc >= 3) {
baudrate = atoi(argv[2]);}// Set signal handlingsignal(SIGINT, signal_handler);signal(SIGTERM, signal_handler);printf("UART Loopback Test Program\n");printf("Serial port used: %s\n", port);printf("Baud rate: %d\n", baudrate);printf("=====================================\n");// Execute loopback testint result = serial_loopback_test(port, baudrate);return result;}
Execution Steps:
adb root
adb push <local path>/uart_loopback_test /data/local/tmp/uart_loopback
adb shell chmod +x /data/local/tmp/uart_loopback
adb shell ls -la /data/local/tmp/
adb shell
cd /data/local/tmp/
./uart_loopback
Test Result:
When pin8 and pin10 are properly short-circuited, the program will continuously send data and verify whether the received data is consistent:
UART Loopback Test Program
Serial port used: /dev/ttyHS0
Baud rate: 115200
=====================================
Serial port /dev/ttyHS0 is open, starting loopback test (press Ctrl+C to exit)...
Sent: Hello, Serial Loopback!
Received: Hello, Serial Loopback! → Test passed
Sent: Hello, Serial Loopback!
Received: Hello, Serial Loopback! → Test passed
Sent: Hello, Serial Loopback!
Received: Hello, Serial Loopback! → Test passed
Press Ctrl+C to exit the test program. The program will automatically close the serial port and exit.
Heat Sink Control¶
Temperature-controlled Fan Principle¶
The background process detects /sys/class/thermal/thermal_zone*
It reads the temperatures of several cores in turn, takes the highest value, and configures the PWM duty cycle of the fan according to the highest temperature.
Temperature |
Duty Cycle/255 |
|---|---|
<30℃ |
0 |
30~40℃ |
64 |
40~50℃ |
128 |
50~60℃ |
192 |
>60℃ |
255 |
Fan Pin |
M1 Development Board Pin (40PIN) |
|---|---|
Red wire (power positive) |
PIN2 |
Black wire (power negative) |
PIN5 |
Blue wire (PWM) |
PIN33 |
Test Process¶
The fan function is disabled by default. After booting, enter the corresponding command to check the status of the fan service.
/system/bin/sh /vendor/bin/40Pin-ctrl fan status #Check the current status of the fan service. If the fan service is not enabled, it shows disabled; if enabled, it shows enable
/system/bin/sh /vendor/bin/40Pin-ctrl fan enable #Enable the fan service. After enabling, you can check whether the status is enable
/system/bin/sh /vendor/bin/40Pin-ctrl fan disable #Disable the fan service. You only need to enter this command when stopping the test
Enter the following commands in the first terminal:
/system/bin/sh /vendor/bin/40Pin-ctrl fan status
/system/bin/sh /vendor/bin/40Pin-ctrl fan enable
#SC200U
watch -n 1 'PWM=/sys/class/pwm/pwmchip0/pwm0; p=$(cat "$PWM/period"); d=$(cat "$PWM/duty_cycle"); echo $(( (d * 255 + p / 2) / p ))'
#SC200E
watch -n 1 'PWM=/sys/bus/platform/devices/1c40000.qcom,spmi:qcom,pm2250@1:qcom,pwms@be00/pwm/pwmchip2/pwm0; p=$(cat "$PWM/period"); d=$(cat "$PWM/duty_cycle"); echo $(( (d * 255 + p / 2) / p ))'
Open a second terminal to increase the temperature:
for i in 1 2 3 4; do
yes > /dev/null &
done
If the temperature increase in step 3 is not sufficient, you can increase the load:
for i in $(seq 1 $(nproc)); do
yes > /dev/null &
done
Check whether the temperature and the PWM duty cycle of the fan in the two terminals match the values in the table.
Stop the load after the test is completed:
killall yes
Application Scenarios¶
Media Entertainment Terminal: Using the Quectel Pi M1 intelligent main control board as a media entertainment terminal, you can play videos, music, view pictures, etc.
Industrial Data Visualization Platform: Using the Quectel Pi M1 intelligent main control board as an industrial data visualization platform, you can display industrial data, monitor equipment operation, perform data processing, etc.
Intelligent Interactive Terminal: Using the Quectel Pi M1 intelligent main control board as an intelligent interactive terminal, it can be applied to various human-computer interaction applications.
Troubleshooting¶
How to View System Logs¶
To view system logs, you need to connect the serial cable to the computer and use a serial port tool to view the logs.
Black Screen Issue¶
After the device is connected normally, if the screen displays black after booting, it may be caused by the following situations:
Power Connection Problem:
Check whether the power supply interface of the display screen is correctly connected, confirm that at least one of the 4 power supply interfaces is correctly connected, ensure that the power supply screws are tightened, and the thimble is in good contact with the 40-pin interface.
Connection Line Problem:
Check whether the MIPI flat cable is correctly inserted and firmly connected, confirm whether the direction of the flat cable is correct to avoid reverse insertion, and check whether the connector has physical damage.
System Startup Problem:
Confirm that the system has started normally and loaded the display service, and check the system logs to confirm whether the display driver is loaded normally.
Screen Distortion Issue¶
After the device is connected normally, if the screen displays distorted images after booting, it may be caused by the following situations:
Signal Interference:
Check whether the MIPI flat cable is too long or of poor quality, confirm whether there are strong electromagnetic interference sources around the flat cable, and try to replace it with a high-quality MIPI flat cable.
Hardware Compatibility Problem:
Confirm that the display model used is fully compatible with the Quectel Pi M1, and check whether the display firmware version is the latest.
Abnormal Touch Function¶
Abnormal touch function may be caused by the following situations:
Driver Problem:
Confirm that the system has correctly loaded the touch screen driver, and check whether the I2C communication is established normally.