为 Milkv Duo 控制的 MMDVM 热点盒子加上液晶显示与 Wifi 连接

2024-03-27

在本地没有联网的数字中继或本地数字中继较远的情况下, MMDVM 盒子是数字电台全球通联的常用设备。
MMDVM 盒子通常使用树莓派作为上位机,控制 MMDVM 单/双工热点板。相对 MMDVM 热点板来说,树莓派更为昂贵,故尝试使用便宜得多的 Milkv Duo 替代树莓派。

上次的文章中, Milkv Duo 配合 MMDVMHost 成功实现了对 MMDVM 单/双工热点板的控制,但是依然遗留了一些问题:

  1. 没有屏幕显示
  2. 官方镜像 RAM 空闲过少
  3. 无法通过 Wifi 连接

而这次采用了 1602 液晶作显示, RTL8188 系列无线网卡实现 Wifi 联网。

MMDVMHost 的液晶驱动需要适配,本文只给出适配完成后的仓库;另外疑似会有开源协议冲突,并不提供担保。

本文只给出部署的过程,工具链同样使用了 RUYI 包管理器安装的工具链,具体安装方法参考上次的文章。

构建 wiringX

wiringX 用于驱动 GPIO。 Milkv Duo 的 wiringX 支持在最近被合入主线,而主线分支和 Milkv Duo 的文档会有些许不同,故 wiringX 需要使用最新主线。 DESTDIR 变量请按实际情况修改。

$ git clone --depth=1 https://github.com/wiringx/wiringX.git
$ cd wiringX
$ mkdir build
$ cd build
$ cmake ..  -DCMAKE_C_COMPILER=riscv64-plct-linux-gnu-gcc
$ make -j4
$ make install DESTDIR=<ruyi_venv_dir>/venv-duo/wiringx

构建 MMDVMHost

本身 MMDVMHost 被设计为运行在树莓派上,故其使用 wiringPi 作为 GPIO 驱动。为了使其能够在 Milkv Duo 上驱动 1602 液晶,这里做了 wiringX 的适配,并开源在如下所示的仓库里。

在适配过程中,一些 wiringPi 的库被稍加修改拿来用了,故疑似有源码协议冲突的问题,暂时不知道如何解决。

$ git clone https://github.com/weilinfox/MMDVMHost_Duo.git -b duo
$ cd MMDVMHost_Duo

修改 Makefile,工具链名称以及依赖库所在的位置要根据实际情况修改,这里扔一个 patch 作为描述。

--- Makefile.Duo.HD44780        2024-03-24 23:38:17.995092253 +0800
+++ Makefile.Duo.HD44780.use    2024-03-20 23:18:25.395561053 +0800
@@ -1,9 +1,9 @@
 # This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed.
 
-CC      = riscv64-unknown-linux-musl-gcc
-CXX     = riscv64-unknown-linux-musl-g++
-CFLAGS  = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -I/usr/local/include/
-LIBS    = -lwiringx -lpthread -lutil -lsamplerate -L/usr/local/lib/
+CC      = riscv64-plct-linux-gnu-gcc
+CXX     = riscv64-plct-linux-gnu-g++
+CFLAGS  = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -I/home/hachi/Documents/Working/ruyi-venv/venv-duo/libsamplerate/usr/local/include/ -I/home/hachi/Documents/Working/ruyi-venv/venv-duo/wiringx/usr/local/include/
+LIBS    = -lwiringx -lpthread -lutil -lsamplerate -L/home/hachi/Documents/Working/ruyi-venv/venv-duo/libsamplerate/usr/local/lib/ -L/home/hachi/Documents/Working/ruyi-venv/venv-duo/wiringx/usr/local/lib/
 LDFLAGS = -g -static
 
 OBJECTS = \

构建。

make -f Makefile.Duo.HD44780 -j4

安装 1602 液晶

由于不想使用 duo-pinmux 命令手动修改引脚功能,这里选了默认设为 GPIO 功能的引脚用于连接 1602 液晶。实测 5V 液晶可以被正常驱动。

首先给出 MMDVM.ini 中与 1602 相关的配置。

[General]
Display=HD44780

[HD44780]
Rows=2
Columns=16
# rs, strb, d0, d1, d2, d3
Pins=16,17,18,19,20,21

硬件连接如下表所示。

Milkv Duo 1602
GND VSS
VBUS VDD
- VO
GP16 RS
GND RW
GP17 E
- D0
- D1
- D2
- D3
GP18 D4
GP19 D5
GP20 D6
GP21 D7
VBUS A
GND K

其中 VO 为显示偏压,可以串一电位器到 GND 用于调整;另外由于 MMDVMHost 采用了 4 线连接的方案, D0-D3 悬空。

MMDVMHost 还提供了基于 PWM 的动态背光。尽管 wiringX 似乎并没有 PWM 支持, MMDVMHost 还提供了基于软件的 PWM 背光控制。不过这些都需要附属电路来实现,有需要的可以自行解决。

构建新的 Milkv Duo 镜像

重构镜像的目的在于增加可用 RAM 以及带 RTL8188 无线网卡驱动的内核。

首先克隆最新的主线分支。

$ git clone https://github.com/milkv-duo/duo-buildroot-sdk.git
$ cd duo-buildroot-sdk

注意最新主线分支要求在 Ubuntu 22.04 LTS 环境下构建镜像,这需要自行准备。

这里给出一个 patch,把所有 SDK 中可用的 Realtek USB 无线网卡驱动选项都被打开了,顺便把根目录大小提升到 4G。

diff --git a/build/boards/cv180x/cv1800b_milkv_duo_sd/linux/cvitek_cv1800b_milkv_duo_sd_defconfig b/build/boards/cv180x/cv1800b_milkv_duo_sd/linux/cvitek_cv1800b_milkv_duo_sd_defconfig
index 1f387e7cd..c16084651 100644
--- a/build/boards/cv180x/cv1800b_milkv_duo_sd/linux/cvitek_cv1800b_milkv_duo_sd_defconfig
+++ b/build/boards/cv180x/cv1800b_milkv_duo_sd/linux/cvitek_cv1800b_milkv_duo_sd_defconfig
@@ -346,3 +346,23 @@ CONFIG_EPOLL=y
 CONFIG_ELF_CORE=y
 CONFIG_COREDUMP=y
 CONFIG_PROC_SYSCTL=y
+
+# wifi
+CONFIG_CFG80211=y
+CONFIG_WIRELESS=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_RFKILL=y
+
+CONFIG_WLAN=y
+CONFIG_WLAN_VENDOR_REALTEK=y
+
+CONFIG_RTL8187=y
+
+CONFIG_RTLWIFI=y
+CONFIG_RTL8192C_COMMON=y
+CONFIG_RTL8192CU=y
+
+CONFIG_RTL8XXXU=y
+
+CONFIG_RTL8188FU=y
diff --git a/build/boards/cv180x/cv1800b_milkv_duo_sd/memmap.py b/build/boards/cv180x/cv1800b_milkv_duo_sd/memmap.py
index 84161267a..388895702 100644
--- a/build/boards/cv180x/cv1800b_milkv_duo_sd/memmap.py
+++ b/build/boards/cv180x/cv1800b_milkv_duo_sd/memmap.py
@@ -40,7 +40,7 @@ class MemoryMap:
     # =================
     # Multimedia buffer. Used by u-boot/kernel/FreeRTOS
     # =================
-    ION_SIZE = 26.80078125 * SIZE_1M
+    ION_SIZE = 0
     H26X_BITSTREAM_SIZE = 0 * SIZE_1M
     H26X_ENC_BUFF_SIZE = 0
     ISP_MEM_BASE_SIZE = 0 * SIZE_1M
diff --git a/device/milkv-duo/genimage.cfg b/device/milkv-duo/genimage.cfg
index 71938363b..943583bd3 100644
--- a/device/milkv-duo/genimage.cfg
+++ b/device/milkv-duo/genimage.cfg
@@ -13,7 +13,7 @@ image rootfs.ext4 {
        ext4 {
                label = "rootfs"
        }
-       size = 768M
+       size = 4096M
 }
 
 image milkv-duo.img {

注意尽管 staging 目录下有一个 RTL8188EU 驱动,但是是坏的,至少不能驱动 RTL8188EUS。本文最后的章节给出了 RTL8188EUS 可用的树外驱动。

经过测试, RTL8188CU 和 RTL8188FTV 模块都没有问题,板载 type-c 接口配合 OTG 转接头亦可以直接插 RTL8188CU USB 网卡使用。

开始构建镜像,注意如果用普通用户建立镜像需要将 /usr/sbin 加入 PATH 环境变量。

$ export PATH=/usr/sbin:$PATH
$ ./build.sh milkv-duo

INFO: hdimage(milkv-duo.img): writing MBR
gnimage for milkv-duo success!
~/duo-buildroot-sdk/build
~/duo-buildroot-sdk
Create SD image successful: out/milkv-duo-20240327-1236.img

编辑镜像,启用 USB 的主模式,并将 Wifi 连接和 MMDVMHost 启动的脚本放入。

$ sudo mkdir /mnt/sd
$ sudo mount -o loop,offset=134218240 ./duo-buildroot-sdk/out/milkv-duo-20240327-1236.img /mnt/sd
$ rm /mnt/sd/mnt/system/usb.sh
$ ln -sf /mnt/system/usb-host.sh /mnt/sd/mnt/system/usb.sh
$ cp ethtool /mnt/sd/usr/local/bin/
$ mkdir /mnt/sd/opt/MMDVMHost
$ cp MMDVMHost RemoteCommand /mnt/sd/opt/MMDVMHost/
$ vim /mnt/sd/opt/MMDVMHost/start_mmdvm

这里给出 start_mmdvm 脚本的内容,这次将 mmdvm 用户的添加写进了脚本。

#/bin/sh

echo Check Internet Connection
while ! ping -c 1 bing.com 2>&1 > /dev/null; do
        sleep 5s
done

echo Check NTP time update
wt=1
while [ `date '+%Y'` = 1970 ]; do
        wt=`expr $wt + 1`
        if [ $wt -ge 30 ]; then
                echo Wait NTP timeout 30s
                break
        fi
        try $wt
        sleep 1s
done

if ! grep mmdvm /etc/shadow; then
        echo No mmdvm user and group found, add them
        addgroup mmdvm
        adduser -H -D -S -G mmdvm -s /bin/false mmdvm
fi

echo Start MMDVMHost
[ -d /var/log/mmdvm ] || mkdir /var/log/mmdvm
chown mmdvm:mmdvm /var/log/mmdvm /dev/ttyS4
/opt/MMDVMHost/MMDVMHost

使其可执行。

$ chmod +x /mnt/sd/opt/MMDVMHost/start_mmdvm

建立自启动脚本。

$ vim /mnt/sd/etc/init.d/S99zuser

这里给出脚本内容,主要添加了 Wifi 连接的功能。

#!/bin/sh
${CVI_SHOPTS}
#
# Control eth0 speed and start wpa_supplicant
#

set_network() {
  if [ -f /mnt/system/ko/8188eu.ko ]; then
    echo Insmod 8188eu.ko
    insmod /mnt/system/ko/8188eu.ko
    sleep 1s
  fi
  if /sbin/ip link | grep eth0; then
    echo Set eth0 to 10Mbps
    ethtool -s eth0 speed 10
  fi
  if /sbin/ip link | grep wlan0; then
    echo Check wifi config
    [ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && wpa_supplicant -B -Dnl80211 -iwlan0 -c/etc/wpa_supplicant/wpa_supplicant.conf
  fi
}

case "$1" in
  start)
        set_network 2>&1 > /var/log/mylog
        /opt/MMDVMHost/start_mmdvm >> /var/log/mylog &
        ;;
  stop)
        ;;
  restart|reload)
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit $?

这里使用 ethtool 限制有线网络到 10Mbps,主要是自制底板走线不科学导致的连接不稳定。如果 100Mbps 速率下没有发现问题则不需要。

关于 Wifi 配置,尽管默认在 /etc/wpa_supplicant.conf 有一个配置文件,这里并不打算使用它,脚本将根据 /etc/wpa_supplicant/wpa_supplicant.conf 中的配置来启动 wpa_supplicant

同样,使其可执行。

$ chmod +x /mnt/sd/etc/init.d/S99zuser

现在可以开始 MMDVMHost 和无线局域网 Wifi 的配置。当然这里并不涉及 MMDVM.ini 的具体内容。

$ cp MMDVM.ini /mnt/sd/etc
$ mkdir /mnt/sd/etc/wpa_supplicant
$ vim /mnt/sd/etc/wpa_supplicant/wpa_supplicant.conf

wpa_supplicant.conf 则可以参考下面的内容。

network={
    ssid="youmu46"
    psk="youmu464646."
}

至此已经完成,将镜像 dd 到 Micro SD 卡并启动 Milkv Duo。

$ sudo umount /mnt/sd
$ sudo sync
$ sudo sync
$ sudo sync
$ sudo dd if=./duo-buildroot-sdk/out/milkv-duo-20240327-1236.img of=/dev/mmcblk0 bs=4M status=progress

在路由器的管理界面查看是否正常连接,如果没有则连接有线网检查情况。

最后在启动了 MMDVMHost 的情况下查看可用 RAM。

# free -m
              total        used        free      shared  buff/cache   available
Mem:             54          16          26           0          12          35
Swap:             0           0           0

可见非常充裕。

RTL8188EU 驱动

GitHub 现存多个驱动仓库,经过测试有一个确认可用。

$ git clone https://github.com/ivanovborislav/rtl8188eu.git
$ cd rtl8188eu

注意由于是构建内核模块,需要使用和构建镜像相同的工具链,这里是 riscv64-unknown-linux-musl-gcc,其下载地址可以从 duo-buildroot-sdk 的构建脚本中获知。

首先编辑 Makefile,这里扔一个 patch。

diff --git a/Makefile b/Makefile
index 3d08dac..1398980 100644
--- a/Makefile
+++ b/Makefile
@@ -142,7 +142,7 @@ CONFIG_LAYER2_ROAMING = y
 #bit0: ROAM_ON_EXPIRED, #bit1: ROAM_ON_RESUME, #bit2: ROAM_ACTIVE
 CONFIG_ROAMING_FLAG = 0x3
 ###################### Platform Related #######################
-CONFIG_PLATFORM_I386_PC = y
+CONFIG_PLATFORM_I386_PC = n
 CONFIG_PLATFORM_RPI_ARM = n
 CONFIG_PLATFORM_RPI_ARM64 = n
 CONFIG_PLATFORM_ANDROID_X86 = n
@@ -206,6 +206,7 @@ CONFIG_PLATFORM_NV_TK1_UBUNTU = n
 CONFIG_PLATFORM_RTL8197D = n
 CONFIG_PLATFORM_AML_S905 = n
 CONFIG_PLATFORM_ZTE_ZX296716 = n
+CONFIG_PLATFORM_MILKV_DUO = y
 ########### CUSTOMER ################################
 CONFIG_CUSTOMER_HUAWEI_GENERAL = n
 
@@ -2329,6 +2330,11 @@ endif
 
 endif
 
+ifeq ($(CONFIG_PLATFORM_MILKV_DUO), y)
+EXTRA_CFLAGS += -DCONFIG_LITTLE_ENDIAN
+EXTRA_CFLAGS += -DCONFIG_IOCTL_CFG80211 -DRTW_USE_CFG80211_STA_EVENT
+endif
+
 ########### CUSTOMER ################################
 ifeq ($(CONFIG_CUSTOMER_HUAWEI_GENERAL), y)
 CONFIG_CUSTOMER_HUAWEI = y

构建之。

$ KSRC=~/duo-buildroot-sdk/linux_5.10/build/cv1800b_milkv_duo_sd CROSS_COMPILE=~/duo-buildroot-sdk/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl- make -j4

最后得到 8188eu.ko,将其安装在 Milkv Duo 镜像的 /mnt/system/ko 目录下。前文给出的 S99zuser 脚本在发现该模块存在时将自动载入之。

尾声

至此一个完整且独立的 MMDVM 盒子就完成了。作为手搓的实验版本,已经稳定运行了两周。

这里是桜風の狐,将美好的 73 送上。

2 Likes