Custom Linux image built with Buildroot for Raspberry Pi Compute Module 4, carrier board dotstartech/wall-panel and 4 inch MIPI DSI LCD panel with ST7703 controller
nfc-lvgl-app.webm
- Board: Raspberry Pi Compute Module 4 (RAM: 1GB, Storage: 8GB eMMC, WiFi/BT: None)
- Carrier: dotstartech/wall-panel
- Display: GX040HD-30MB-A1 (720x720 IPS LCD with ST7703 controller)
- Touch: FocalTech FT6336U (I2C0 @ 0x48)
- NFC: NXP PN7150 (I2C1 @ 0x28)
- RTC: DS3231 (I2C1 @ 0x68)
- Microphone: Adafruit I2S MEMS SPH0645LM4H
- Linux Kernel 6.12 (Raspberry Pi fork)
- ST7703 display panel driver
- FT6336U touchscreen support
- PN7150 NFC with kernel driver (pn5xx_i2c) and libnfc-nci
- DS3231 RTC support
- I2S MEMS microphone support (Adafruit SPH0645LM4H)
- RAUC OTA updates with A/B rootfs partition scheme
- I2C interface enabled (I2C0 and I2C1)
- USB Ethernet (smsc95xx for PoE backplate)
nfc-terminal-image/
├── buildroot/ # Buildroot source (submodule, 2026.02.x)
├── board/nfc-terminal/ # Board-specific files
│ ├── config.txt # Raspberry Pi boot configuration
│ ├── cmdline.txt # Kernel command line
│ ├── genimage.cfg # Disk image layout (A/B partitions)
│ ├── linux.config.fragment # Kernel config additions
│ ├── logo-mid.png # Boot splash image
│ ├── nfc-diag.sh # NFC diagnostics script
│ ├── overlays/ # Device tree overlays
│ │ └── nfc-pn7150.dts # NFC PN7150 overlay
│ ├── rauc/ # RAUC OTA update configuration
│ │ ├── system.conf # RAUC system config (slots, bootloader)
│ │ ├── rauc-boot-handler # Custom A/B bootloader backend script
│ │ ├── certgen.sh # Development certificate generator
│ │ └── certs/ # Signing certificates (dev only)
│ ├── post-build.sh # Post-build rootfs customization
│ └── post-image.sh # Image generation script
├── configs/ # Buildroot defconfigs
│ └── nfc_terminal_cm4_defconfig
├── docs/ # Reference documentation
│ └── AN11697.pdf # NXP PN7150 Linux integration guide
├── external/ # Buildroot external tree hooks
├── package/ # Custom Buildroot packages
│ ├── libnfc-nci/ # NFC userspace library (NXP)
│ ├── nfc-lvgl-app/ # LVGL touchscreen UI application
│ │ ├── src/ # Application source (C, LVGL, fonts)
│ │ ├── images/ # UI image assets
│ │ └── nfc-console # Init wrapper script
│ ├── pn5xx-i2c/ # PN7150 NFC kernel driver (out-of-tree)
│ └── st7703-gx040hd/ # ST7703 MIPI DSI panel driver package
├── patches/ # Buildroot package patches
│ └── buildroot/
│ └── 0001-paho-mqtt-c-enable-MQTT5.patch
├── .github/
│ ├── instructions/ # Copilot agent instructions
│ └── workflows/build.yml # CI build workflow
├── build.sh # Main build script
├── external.desc # External tree descriptor
├── external.mk # External tree makefile
├── Config.in # External tree Kconfig
├── ROADMAP.md # Future development plans
├── LICENSE # Project license
└── README.md # This file
Install build dependencies (Debian/Ubuntu):
sudo apt-get update
sudo apt-get install -y build-essential git wget cpio unzip rsync bc \
libncurses-dev libssl-dev python3 python3-dev python3-setuptools file- Clone Buildroot:
git clone https://github.com/buildroot/buildroot.git --branch 2026.02.x --depth 1- Build the image:
./build.shThis configures Buildroot with the external tree and builds everything.
- Build script commands:
| Command | Description |
|---|---|
./build.sh |
Build the complete image (default, without demo app) |
./build.sh build --demo-app |
Build the image with nfc-lvgl-app demo included |
./build.sh config |
Apply NFC Terminal defconfig without building |
./build.sh menuconfig |
Open Buildroot interactive configuration menu |
./build.sh linux-menuconfig |
Open Linux kernel configuration menu |
./build.sh savedefconfig |
Save current config back to defconfig file |
./build.sh clean |
Remove build artifacts (keeps config) |
./build.sh distclean |
Remove ALL build artifacts and configuration |
./build.sh rebuild-kernel |
Rebuild only the Linux kernel |
./build.sh rebuild-driver |
Rebuild only the ST7703 display driver |
./build.sh flash /dev/sdX |
Flash built image to SD card or eMMC device |
./build.sh rpiboot |
Start rpiboot for CM4 eMMC programming mode |
./build.sh desktop-build |
Build nfc-lvgl-app for desktop (x86_64) with SDL2 for UI testing |
./build.sh dist [version] |
Create signed RAUC bundle + version file (reads nfc-terminal.version if no version given) |
- Output files will be in
buildroot/output/images/:nfc-terminal.img- Complete bootable imageImage- Linux kernelrootfs.ext4- Root filesystembcm2711-rpi-cm4.dtb- Device tree
- Install boot jumpers on IO board to disable eMMC boot
- Connect IO board USB to host
- Flash using rpiboot:
sudo ./buildroot/output/host/bin/rpiboot
# Wait for eMMC to appear as mass storage, then:
# IMPORTANT: Unmount any auto-mounted partitions first!
sudo umount /dev/sdX1 /dev/sdX2 2>/dev/null || true
sudo dd if=buildroot/output/images/nfc-terminal.img of=/dev/sdX bs=4M status=progress
syncNote: The
./build.sh flashcommand will automatically detect and offer to unmount any mounted partitions.
Alternatively Raspberry Pi Imager can be used to flash the image.
- Disconnect IO board USB from host
- Switch boot jumper back to eMMC boot
- Connect LAN cable to PoE switch
The image can be tested in QEMU without real hardware for basic validation of boot and rootfs.
Install QEMU for ARM64:
# Debian/Ubuntu
sudo apt-get install qemu-system-arm
# Fedora
sudo dnf install qemu-system-aarch64./qemu-run.shPress Ctrl-A X to exit QEMU.
QEMU doesn't have Raspberry Pi 4/CM4 machine emulation, so the script uses the generic ARM64 virt machine with a Cortex-A72 CPU. It performs direct kernel boot with the rootfs mounted as a virtio block device.
What works in QEMU:
- Kernel boot and init system
- Network via user-mode networking (SSH available on
localhost:2222) - Basic userspace and shell access
What doesn't work (requires real hardware):
- NFC (
/dev/pn544won't exist - nfc-console will timeout after 30s) - Display (ST7703) and touch
- I2C, GPIO, and other Pi-specific peripherals
SSH into the QEMU instance:
ssh -p 2222 root@localhost
# Password: nfc-terminalKey display, I2C, I2S and peripheral settings in /boot/config.txt:
# I2C
dtparam=i2c_arm=on # I2C1 (GPIO 2/3) - RTC, NFC
dtparam=i2c_vc=on # I2C0 (GPIO 0/1) - Touch controller
# I2S
dtparam=i2s=on
# Display
dtoverlay=vc4-kms-v3d
dtoverlay=st7703-gx040hd
dtoverlay=ft6336u-gx040hd
# DS3231 RTC on I2C1
dtoverlay=i2c-rtc,ds3231
# PN7150 NFC on I2C1 with kernel driver
dtoverlay=nfc-pn7150
# SPH0645LM4H mic uses the Google VoiceHAT soundcard driver via I2S
dtoverlay=googlevoicehat-soundcard| Bus | Address | Device | Driver |
|---|---|---|---|
| I2C0 (i2c_vc) | 0x48 | FT6336U Touch Controller | edt_ft5x06 |
| I2C1 (i2c_arm) | 0x28 | PN7150 NFC Controller | pn5xx_i2c |
| I2C1 (i2c_arm) | 0x68 | DS3231 RTC | rtc-ds1307 |
- I2C0 (GPIO 0/1): Reserved for DSI display peripherals (touch controller)
- I2C1 (GPIO 2/3): General peripherals (RTC, NFC, user devices)
The NFC subsystem uses NXP's PN7150 chip interfaced via I2C, with kernel-level GPIO control as recommended by NXP's Linux integration guide (AN11697, sections 3.1 and 4.2).
┌─────────────────────────────────────────────┐
│ nfcDemoApp (poll mode) │ User Application
├─────────────────────────────────────────────┤
│ libnfc-nci (R2.4) │ NFC Middleware
│ (dotstartech/linux_libnfc-nci) │
├─────────────────────────────────────────────┤
│ /dev/pn544 (char device) │ Device Node
├─────────────────────────────────────────────┤
│ pn5xx_i2c.ko (kernel) │ Kernel Driver
│ (GPIO control, I2C communication) │
├─────────────────────────────────────────────┤
│ i2c1 @ 0x28 │ GPIO5 (INT) │ Hardware
│ │ GPIO6 (VEN) │
├───────────────────┴─────────────────────────┤
│ NXP PN7150 Chip │
└─────────────────────────────────────────────┘
Key design choice: The --enable-alt flag for libnfc-nci (userspace GPIO control via sysfs) does not work on modern kernels/CM4 because the GPIO sysfs interface fails for GPIOs managed by pinctrl. The kernel driver approach handles GPIO control internally, which is more reliable.
The pn5xx_i2c driver from NXP required several modifications to compile on Linux kernel 6.x:
| Original Code | Fixed Code | Reason |
|---|---|---|
pr_warning(...) |
pr_warn(...) |
pr_warning removed in kernel 5.x |
no_llseek |
noop_llseek |
no_llseek removed in kernel 6.x |
of_get_named_gpio_flags(node, name, 0, &flags) |
of_get_named_gpio(node, name, 0) |
API simplified, flags parameter removed |
static int pn54x_probe(struct i2c_client *client, const struct i2c_device_id *id) |
static int pn54x_probe(struct i2c_client *client) |
i2c_device_id parameter removed in 6.3+ |
static int pn54x_remove(...) |
static void pn54x_remove(...) |
Return type changed to void in 6.1+ |
The patched source is located at package/pn5xx-i2c/pn5xx_i2c.c.
The NFC overlay (board/nfc-terminal/overlays/nfc-pn7150.dts) configures:
pn7150@28 {
compatible = "nxp,pn547"; // Driver compatible string
reg = <0x28>; // I2C address
interrupt-gpios = <&gpio 5 0>; // GPIO5 for data-ready interrupt
enable-gpios = <&gpio 6 0>; // GPIO6 for chip enable (VEN)
interrupts = <5 1>; // GPIO5, rising edge trigger
};
GPIO pin functions:
- GPIO5 (BCM): Interrupt - signals when NFC data is ready to read
- GPIO6 (BCM): Enable (VEN) - powers on/off the NFC chip
The nfcDemoApp poll command starts automatically at boot via the init script /etc/init.d/S95nfc. The script:
- Waits for
/dev/pn544device node (up to 15 seconds) - Sets appropriate permissions on the device
- Launches
nfcDemoApp pollin background
Logs are written to /var/log/nfc.log.
The touchscreen uses a FocalTech FT6336U controller connected via I2C0 at address 0x48. The kernel driver edt_ft5x06 handles the hardware, exposing touch events through the Linux input subsystem.
| Parameter | Value |
|---|---|
| Controller | FocalTech FT6336U |
| I2C Bus | I2C0 (i2c_vc) |
| I2C Address | 0x48 |
| Interrupt GPIO | GPIO25 (falling edge) |
| Reset GPIO | GPIO24 (active-low) |
| Kernel Driver | edt_ft5x06 |
| Input Device | /dev/input/event4 |
The touch overlay (ft6336u-gx040hd.dtbo) configures:
- I2C address and compatible string (
focaltech,ft6236) - Interrupt on GPIO25, falling edge trigger
- Reset control on GPIO24
Key implementation details for integrating with LVGL v9.x:
-
Input Device Setup: Open
/dev/input/event4withO_RDONLY | O_NONBLOCK -
Multitouch Protocol B: The FT6336U uses Linux MT Protocol B with slots:
ABS_MT_POSITION_X/ABS_MT_POSITION_Yfor coordinatesABS_MT_TRACKING_ID >= 0indicates touch down,-1indicates touch up- Also sends
BTN_TOUCHkey events
-
Coordinate Scaling: Query touch range using
EVIOCGABS(ABS_MT_POSITION_X/Y)and scale to display resolution (720x720) -
Display Refresh: After UI changes from touch events, call:
lv_obj_invalidate(obj)to mark objects for redrawlv_refr_now(display)to force immediate refresh
FT6336U Hardware
│ (I2C0)
▼
edt_ft5x06 Kernel Driver
│ (GPIO25 interrupt)
▼
Input Subsystem (/dev/input/event4)
│ (read() with O_NONBLOCK)
▼
LVGL touch_read_cb()
│ (parse EV_ABS events)
▼
LVGL Input Device (LV_INDEV_TYPE_POINTER)
│
▼
LVGL Event System (LV_EVENT_CLICKED, etc.)
The nfc-lvgl-app uses the Eclipse Paho C MQTT library with asynchronous operation and automatic reconnection.
| Parameter | Value |
|---|---|
| Broker | mqbase.io |
| Client ID | nfc-term-/ |
| Protocol | MQTT 5.0 (async) |
| QoS | 2 |
| Auto-Reconnect | Enabled (1-60s backoff) |
| Topic | Description | Retained |
|---|---|---|
data/<MAC>/nfc |
NFC tag events (tagId, type) | No |
data/<MAC>/state |
Device state (on/off) | Yes |
The <MAC> is the device's eth0 MAC address without colons (e.g., DCDCA284B7C8).
On connection, the client registers an LWT message:
- Topic:
data/<MAC>/state - Payload:
{"state":"off"} - Retained: Yes
This ensures the broker publishes the offline state if the device disconnects unexpectedly.
The async MQTT client automatically handles reconnection:
- Min retry interval: 1 second
- Max retry interval: 60 seconds
- Backoff: Exponential between min and max
- Reconnection trigger: Automatic on connection loss (no manual intervention required)
When connection is restored, the client automatically publishes {"state":"on"} to the state topic.
The image supports the Adafruit I2S MEMS Microphone Breakout using the Google VoiceHAT soundcard driver.
- Device tree overlay:
googlevoicehat-soundcard(enables I2S and registers ASoC card) - Kernel modules:
snd-soc-bcm2835-i2s(I2S controller),snd-soc-googlevoicehat-codec(codec driver),snd-soc-rpi-simple-soundcard(machine driver) - config.txt:
dtparam=i2s=onanddtoverlay=googlevoicehat-soundcard - Sample rate: Fixed at 48 kHz
The microphone captures audio at 24 kHz / 16-bit mono WAV (~2.9 MB/min, a 4× reduction vs. the native 48 kHz / 32-bit format). Recording starts automatically when roles are checked in and stops when all roles go idle. Files are named <TAGID>-YYYYMMDDTHHMMSS.wav (e.g. 04a23b1c2d3e4f-20260310T143022.wav) and saved to /tmp/.
Record manually from the command line:
arecord -D dmic -c1 -r 24000 -f S16_LE -t wav -V mono -v recording.wavThe image uses RAUC for robust over-the-air (OTA) updates with an A/B partition scheme. Updates are atomic — the system either boots the new version or automatically stays on the old one.
┌────────────┬────────────┬────────────┬────────────┐
│ boot │ rootfs_a │ rootfs_b │ data │
│ (FAT32) │ (ext4) │ (ext4) │ (ext4) │
│ 64 MB │ 96 MB │ 96 MB │ 16 MB │
│ /dev/ │ /dev/ │ /dev/ │ /dev/ │
│ mmcblk0p1 │ mmcblk0p2 │ mmcblk0p3 │ mmcblk0p4 │
├────────────┼────────────┼────────────┼────────────┤
│ Active │ Slot A │ Slot B │ Persistent│
│ kernel, │ rootfs + │ rootfs + │ state, │
│ DTBs, │ kernel, │ kernel, │ rauc. │
│ overlays, │ DTBs, │ DTBs, │ status, │
│ firmware, │ overlays │ overlays │ ota-url. │
│ boot.ini, │ (factory │ (empty │ conf │
│ cmdline │ image) │ initially)│ │
└────────────┴────────────┴────────────┴────────────┘
- boot: Shared boot partition with firmware (
start4.elf,fixup4.dat), boot state (boot.ini,cmdline.txt), and a copy of the active slot's kernel, DTB, and overlays. The boot partition is 64 MB to accommodate the kernel image (~22 MB) plus a temporary copy during RAUC updates. - rootfs_a / rootfs_b: A/B root filesystem slots. Each slot contains the complete rootfs plus the kernel (
/boot/Image), device tree (/boot/bcm2711-rpi-cm4.dtb), and overlays (/boot/overlays/). This makes RAUC bundles fully self-contained — a single rootfs update carries the matching kernel. The factory image populates slot A; slot B is left empty for the first OTA update. - data: Persistent partition mounted at
/data, storesrauc.status,ota-url.conf, and survives updates.
- The RPi CM4 firmware reads
cmdline.txtfrom the boot partition at power-on — this selects which rootfs partition to mount via theroot=parameter. - RAUC detects the currently booted slot by reading
rauc.slot=A|Bfrom/proc/cmdline. - When an update bundle is installed, RAUC writes the new rootfs image to the inactive slot.
- RAUC calls the custom boot handler (
rauc-boot-handler) to set the new slot as primary — this rewritescmdline.txtto point to the new partition and syncs the kernel (Image), device tree (bcm2711-rpi-cm4.dtb), and overlays from the new slot's/boot/directory to the shared boot partition. - On reboot, the firmware boots into the updated slot with the matching kernel and device trees.
- An init script (
S99rauc) callsrauc status mark-goodafter a successful boot, confirming the update.
Since the RPi CM4 uses the Raspberry Pi firmware bootloader (not U-Boot or Barebox), a custom bootloader backend script manages slot selection by rewriting cmdline.txt and tracking state in boot.ini on the FAT boot partition.
RAUC system config is at /etc/rauc/system.conf on the target:
[system]
compatible=nfc-terminal-cm4
bootloader=custom
statusfile=/data/rauc.status
bundle-formats=-plain
max-bundle-download-size=100663296
[keyring]
path=/etc/rauc/ca.cert.pem
[handlers]
bootloader-custom-backend=/usr/lib/rauc/rauc-boot-handler
[slot.rootfs.0]
device=/dev/mmcblk0p2
type=ext4
bootname=A
[slot.rootfs.1]
device=/dev/mmcblk0p3
type=ext4
bootname=BKey settings:
- compatible: Bundles must match
nfc-terminal-cm4or they are rejected - bundle-formats=-plain: Only verity-signed bundles accepted (plain format disabled for security)
- bootloader=custom: Uses the shell script backend at
/usr/lib/rauc/rauc-boot-handler
RAUC uses X.509 certificates for bundle verification. The target device has a CA certificate (keyring) installed at /etc/rauc/ca.cert.pem, and only bundles signed with a certificate chaining to this CA are accepted.
Generate development certificates for testing:
cd board/nfc-terminal/rauc
./certgen.shThis creates board/nfc-terminal/rauc/certs/:
ca.key.pem/ca.cert.pem— Certificate Authority (keyring)signing.key.pem/signing.cert.pem— Bundle signing key/cert
Security: The
certs/directory is gitignored. Never commit private keys. For production, use a proper PKI with an HSM or secure key storage.
For production deployments:
- Generate a production CA with a strong key stored in an HSM
- Create separate signing certificates per release channel
- Replace
board/nfc-terminal/rauc/certs/ca.cert.pemwith your production CA certificate before building the image - Keep signing keys on a secure build server only
Update bundles are SquashFS images containing the new rootfs, signed with the signing certificate.
./build.shCreate a temporary directory with the manifest and rootfs:
mkdir -p /tmp/rauc-bundle
cat > /tmp/rauc-bundle/manifest.raucm << 'EOF'
[update]
compatible=nfc-terminal-cm4
version=1.0.0
[bundle]
format=verity
[image.rootfs]
filename=rootfs.ext4
type=ext4
EOF
cp buildroot/output/images/rootfs.ext4 /tmp/rauc-bundle/buildroot/output/host/bin/rauc bundle \
--cert=board/nfc-terminal/rauc/certs/signing.cert.pem \
--key=board/nfc-terminal/rauc/certs/signing.key.pem \
/tmp/rauc-bundle \
buildroot/output/images/nfc-terminal.raucbbuildroot/output/host/bin/rauc info \
--keyring=board/nfc-terminal/rauc/certs/ca.cert.pem \
buildroot/output/images/nfc-terminal.raucbCopy the bundle to the device and install:
# Copy bundle to device
scp buildroot/output/images/nfc-terminal.raucb root@<device-ip>:/tmp/
# SSH into device and install
ssh root@<device-ip>
rauc install /tmp/nfc-terminal.raucb
# Reboot to activate the update
rebootHost the bundle on an HTTP server and install directly from the URL:
# On the build host — serve bundles
cd buildroot/output/images
python3 -m http.server 8080
# On the device — install from network
rauc install http://<host-ip>:8080/nfc-terminal.raucbFor production, use an HTTPS server with a proper TLS certificate.
# Show full slot status
rauc status
# Example output:
# === System Info ===
# Compatible: nfc-terminal-cm4
# Booted from: rootfs.0 (A)
#
# === Slot Status ===
# o [rootfs.0] (/dev/mmcblk0p2, ext4, booted)
# bootname: A
# boot status: good
# o [rootfs.1] (/dev/mmcblk0p3, ext4, inactive)
# bootname: B
# boot status: bad
# Show only the booted slot
rauc status --detailed
# Mark current slot as good (done automatically by S99rauc init script)
rauc status mark-good
# Mark current slot as bad (triggers rollback on next reboot)
rauc status mark-bad# 1. Flash the factory image to eMMC
# 2. Boot the device — it starts on slot A
# On device:
rauc status
# → Booted from rootfs.0 (A), rootfs.1 (B) is inactive/bad
# 3. Make a change to the rootfs (e.g., bump version) and create a new bundle
# 4. Install the update
rauc install /tmp/nfc-terminal.raucb
# → RAUC writes to slot B, sets B as primary
# 5. Reboot
reboot
# → Device boots from slot B
# 6. Verify
rauc status
# → Booted from rootfs.1 (B), rootfs.0 (A) is still good# On device (booted from slot B):
rauc status mark-bad
reboot
# → Device boots back to slot A (the last known-good slot)# Check boot state file on the boot partition
cat /boot/boot.ini
# → Shows PRIMARY, A_OK, A_ATTEMPTS, B_OK, B_ATTEMPTS
# Check kernel command line
cat /proc/cmdline
# → Shows root=/dev/mmcblk0p2 or p3, and rauc.slot=A or BThe bundle's compatible field must exactly match the system config. Check:
rauc info /tmp/update.raucb # on host or device
cat /etc/rauc/system.conf # on deviceThe bundle must be signed with a certificate that chains to the CA keyring on the device:
# Verify the keyring matches
openssl x509 -in /etc/rauc/ca.cert.pem -noout -subject
# Compare with the CA used to sign the bundleCheck that the boot handler is executable and functional:
# Test handler commands manually
/usr/lib/rauc/rauc-boot-handler get-current # Should print A or B
/usr/lib/rauc/rauc-boot-handler get-primary # Should print A or B
/usr/lib/rauc/rauc-boot-handler get-state A # Should print good or bad
# Check boot partition is mounted
mountpoint /boot
# Check boot state
cat /boot/boot.iniManually fix the boot state:
# Mount boot partition if needed
mount -t vfat /dev/mmcblk0p1 /boot
# Manually set primary slot to A
/usr/lib/rauc/rauc-boot-handler set-primary A
# Verify cmdline.txt was updated
cat /boot/cmdline.txt
# → root=/dev/mmcblk0p2 ... rauc.slot=A
reboot# Check rootfs usage (must fit in 64MB)
df -h /
# Check data partition
df -h /data- Username: root
- Password: nfc-terminal
Edit configs/nfc_terminal_cm4_defconfig or use make menuconfig.
Modify board/nfc-terminal/linux.config.fragment for kernel options.
Edit board/nfc-terminal/config.txt for Raspberry Pi boot parameters.
# Check kernel messages
dmesg | grep -E "(st7703|dsi|panel|vc4)"
# Verify overlays in device tree
ls /proc/device-tree/soc/dsi@*/# Check I2C devices
i2cdetect -y 0 # Should show 0x48
# Check touch input device exists
ls -la /dev/input/event*
# Read raw touch events (hex dump)
cat /dev/input/event0 | hexdump -C# List I2C buses
ls /dev/i2c-*
# Scan for devices
i2cdetect -y 1# Check if kernel module loaded
lsmod | grep pn5xx
# Verify device node exists
ls -la /dev/pn544
# Check kernel messages for NFC driver
dmesg | grep -i "pn54x\|nfc"
# View autostart log
cat /var/log/nfc.log
# Verify NFC chip on I2C bus (should show 28)
i2cdetect -y 1
# Manually test NFC polling
/etc/init.d/S95nfc stop
nfcDemoApp poll
# Check GPIO status in sysfs
cat /sys/kernel/debug/gpio | grep -E "gpio-5|gpio-6"





