1. Nerves环境配置全指南:从零搭建嵌入式Elixir开发环境
第一次接触Nerves这个嵌入式开发框架时,我被它"Elixir语言+嵌入式"的组合深深吸引。作为一个长期从事Web开发的程序员,能在树莓派上运行Phoenix框架的体验简直令人兴奋。但在配置环境的过程中,我踩遍了所有能踩的坑——从Erlang版本冲突到固件烧录失败,从交叉编译工具链缺失到系统路径配置错误。这篇指南将带你避开这些陷阱,用最可靠的方式搭建Nerves开发环境。
Nerves不同于常规的Elixir开发,它需要完整的工具链支持来为特定硬件平台生成嵌入式Linux系统镜像。这意味着除了Elixir/Erlang基础环境外,我们还需要处理:
- 针对目标硬件(如Raspberry Pi/BeagleBone)的交叉编译工具链
- 固件打包和烧录工具
- 硬件特定的Bootloader配置
- 系统级依赖(如fwup、squashfs)
下面以macOS/Linux系统为例(Windows用户建议使用WSL2),详细拆解每个关键步骤。
2. 基础环境准备
2.1 Elixir/Erlang安装
不要直接使用系统包管理器安装!这会导致版本冲突。推荐使用asdf版本管理器:
bash复制# 安装asdf
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.11.3
echo '. "$HOME/.asdf/asdf.sh"' >> ~/.bashrc
source ~/.bashrc
# 安装Erlang插件和指定版本
asdf plugin-add erlang https://github.com/asdf-vm/asdf-erlang.git
asdf install erlang 25.3.2
asdf global erlang 25.3.2
# 安装Elixir插件和匹配版本
asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git
asdf install elixir 1.14.5-otp-25
asdf global elixir 1.14.5-otp-25
重要提示:必须保持OTP版本(25.x)与Elixir版本(1.14.x)的匹配关系,这是Nerves稳定运行的前提条件。
2.2 系统依赖安装
不同平台需要的基础工具:
bash复制# macOS
brew install fwup squashfs coreutils xz
# Ubuntu/Debian
sudo apt install -y build-essential automake autoconf git squashfs-tools ssh-askpass fwup xz-utils
# Arch Linux
sudo pacman -S base-devel fwup squashfs-tools xz
验证安装:
bash复制fwup --version # 应显示1.10.0+
mksquashfs -version # 需4.3+
3. Nerves工具链配置
3.1 安装Nerves Bootstrap
bash复制mix archive.install hex nerves_bootstrap
这个工具包会自动处理:
- 项目模板生成
- 依赖解析
- 固件构建流程
3.2 设置目标硬件
以Raspberry Pi 4为例:
bash复制export MIX_TARGET=rpi4 # 必须与硬件匹配
常见目标平台:
rpi0- Raspberry Pi Zero/Zero Wrpi3- Raspberry Pi 3 Model B/B+rpi4- Raspberry Pi 4 Model Bbbb- BeagleBone Blackx86_64- 通用x86平台
3.3 初始化项目
bash复制mix nerves.new my_app --target $MIX_TARGET
cd my_app
生成的目录结构关键部分:
code复制├── config/
│ └── target.exs # 硬件特定配置
├── rootfs_overlay/ # 自定义文件系统
├── rel/ # 发布配置
├── _build/
│ └── $MIX_TARGET_dev/ # 交叉编译产物
└── mix.exs # 依赖管理
4. 依赖管理与编译
4.1 获取依赖
bash复制mix deps.get
这个过程会:
- 下载针对目标平台的Elixir/Erlang交叉编译版本
- 获取Nerves系统镜像(base firmware)
- 安装项目依赖
常见问题:如果卡在
nerves_system_*下载阶段,可能是网络问题。可以尝试:bash复制export NERVES_ARTIFACT_SITES="https://hex.pm/api/repos/nerves-artifacts"
4.2 首次编译
bash复制mix firmware
成功输出示例:
code复制Generated nerves_app
...
Success!
firmware: _build/rpi4_dev/nerves/images/my_app.fw
编译过程实际上执行了:
- 交叉编译Elixir代码
- 构建Linux根文件系统
- 使用fwup打包成固件镜像
5. 固件烧录与测试
5.1 写入SD卡
插入空白SD卡(建议≥16GB),然后:
bash复制mix firmware.burn
会检测可移动设备并提示确认:
code复制Found 8GB SD card at /dev/rdisk2. Proceed? [Yn]
安全提示:务必确认设备路径正确!错误的路径可能导致数据丢失。
5.2 硬件启动
将烧录好的SD卡插入目标设备并上电。首次启动会:
- 扩展文件系统分区
- 生成SSH密钥
- 初始化BEAM虚拟机
观察LED指示灯:
- 稳定闪烁:系统运行中
- 快速闪烁:启动失败
5.3 连接设备
默认网络配置:
- 有线DHCP自动连接
- WiFi需预先配置(见下文)
- SSH:
ssh nerves.local(密码为空)
6. 高级配置技巧
6.1 WiFi设置
在config/target.exs中添加:
elixir复制config :vintage_net,
regulatory_domain: "US",
config: [
{"wlan0",
%{
type: VintageNetWiFi,
vintage_net_wifi: %{
key_mgmt: :wpa_psk,
ssid: "your_ssid",
psk: "your_password"
},
ipv4: %{method: :dhcp}
}}
]
然后重新烧录固件:
bash复制mix firmware && mix firmware.burn
6.2 文件系统扩展
默认只使用部分SD卡空间。要扩展:
elixir复制# config/target.exs
config :nerves, :erlinit, [
mount: "/dev/mmcblk0p3:/root:ext4:rw,data=writeback,errors=remount-ro"
]
6.3 自定义系统包
通过修改nerves_defconfig:
bash复制echo 'BR2_PACKAGE_OPENSSL=y' >> nerves_defconfig
echo 'BR2_PACKAGE_OPENSSH=y' >> nerves_defconfig
mix deps.compile nerves_system_#{MIX_TARGET} --force
7. 常见问题排查
7.1 编译错误:版本不匹配
典型报错:
code复制ERROR: OTP version 26 found, but nerves requires 25
解决方案:
bash复制asdf install erlang 25.3.2
asdf global erlang 25.3.2
rm -rf _build deps # 清理旧编译
7.2 启动失败:内核panic
可能原因:
- 错误的目标平台设置
- 损坏的SD卡
排查步骤:
- 确认
MIX_TARGET与硬件匹配 - 尝试新SD卡
- 检查电源是否稳定(RPi4需要3A电源)
7.3 SSH连接超时
网络诊断:
bash复制ping nerves.local
arp -a | grep nerves
备用方案:
- 通过USB串口连接
- 接显示器查看启动日志
8. 开发工作流优化
8.1 热代码重载
在config.exs中启用:
elixir复制config :nerves, :erlinit, [
run_on_exit: "/sbin/reboot"
]
config :nerves, firmware: [apply_on_boot: true]
然后使用:
bash复制mix firmware && mix upload
8.2 集成测试
创建测试固件:
bash复制MIX_ENV=test mix firmware
8.3 生产部署
生成签名固件:
bash复制export NERVES_SIGNING_KEY=path/to/key
mix firmware.sign
9. 硬件特定配置示例
9.1 Raspberry Pi GPIO控制
添加依赖:
elixir复制{:circuits_gpio, "~> 0.1"}
使用示例:
elixir复制{:ok, pid} = Circuits.GPIO.open(17, :output)
Circuits.GPIO.write(pid, 1) # 设置高电平
9.2 BeagleBone PWM输出
配置:
elixir复制config :nerves, :erlinit, [
overlays: "/lib/firmware:/lib/firmware"
]
代码实现:
elixir复制File.write("/sys/class/pwm/pwmchip0/export", "0")
File.write("/sys/class/pwm/pwmchip0/pwm0/period", "1000000")
File.write("/sys/class/pwm/pwmchip0/pwm0/duty_cycle", "500000")
10. 性能监控与调优
10.1 内存使用分析
连接IEx控制台:
bash复制ssh nerves.local
:observer.start()
关键指标:
- 进程数
- 内存使用分布
- 垃圾回收频率
10.2 网络优化
调整TCP参数:
elixir复制config :nerves, :erlinit, [
env: [
{"ERL_EPMD_PORT", "4369"},
{"ERL_DIST_PORT", "9001"}
]
]
10.3 启动时间优化
分析启动流程:
bash复制mix firmware && mix burn
screen /dev/ttyUSB0 115200 # 查看详细启动日志
优化方向:
- 减少不必要的服务
- 并行初始化
- 预编译依赖
11. 持续集成配置
11.1 GitHub Actions示例
.github/workflows/nerves.yml:
yaml复制name: Nerves CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-elixir@v1
with:
otp-version: '25.3'
elixir-version: '1.14.5'
- run: sudo apt install -y fwup squashfs-tools
- run: mix deps.get
- run: mix firmware
11.2 自定义构建缓存
优化CI速度:
bash复制mkdir -p ~/.nerves/cache
export NERVES_CACHE_DIR=~/.nerves/cache
12. 安全加固指南
12.1 SSH安全配置
修改rootfs_overlay/etc/ssh/sshd_config:
code复制PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
12.2 固件签名验证
生成密钥对:
bash复制fwup -g -f /dev/null -o key.pub -k key.priv
配置项目:
elixir复制config :nerves, firmware: [
signing_keys: [
"key.priv",
# 其他密钥...
]
]
12.3 系统服务加固
禁用不需要的服务:
elixir复制config :nerves, :erlinit, [
run_on_exit: "/bin/sh -c 'for s in service1 service2; do systemctl disable $s; done'"
]
13. 调试技巧大全
13.1 IEx远程调试
启动时添加:
elixir复制config :nerves, :erlinit, [
env: [
{"ERL_FLAGS", "-name debug@nerves.local -setcookie mycookie"}
]
]
连接方式:
bash复制iex --name dev@local --cookie mycookie --remsh debug@nerves.local
13.2 内核日志实时监控
bash复制ssh nerves.local dmesg -w
13.3 崩溃转储分析
配置核心转储:
elixir复制config :nerves, :erlinit, [
env: [
{"ERL_CRASH_DUMP", "/root/crash.dump"},
{"ERL_CRASH_DUMP_SECONDS", "10"}
]
]
14. 资源扩展与定制
14.1 添加自定义Erlang应用
创建OTP应用:
bash复制mix new my_lib --sup
在mix.exs中添加:
elixir复制{:my_lib, path: "../my_lib"}
14.2 修改根文件系统
覆盖文件:
code复制rootfs_overlay/
└── etc/
└── my_config.conf
14.3 构建自定义系统镜像
克隆基础系统:
bash复制git clone https://github.com/nerves-project/nerves_system_rpi4
修改后构建:
bash复制make
mix nerves.system.shell
15. 实际项目经验分享
在工业控制器项目中,我们遇到几个典型问题及解决方案:
-
SD卡寿命问题:
- 启用OverlayFS:
config :nerves, :erlinit, [mount: "/dev/mmcblk0p3:/root:overlayfs:lowerdir=/root,upperdir=/tmp/overlay,workdir=/tmp/work"] - 设置tmpfs:
config :nerves, :erlinit, [mount: "tmpfs:/tmp:tmpfs:size=50M"]
- 启用OverlayFS:
-
无线网络不稳定:
elixir复制config :vintage_net, persistence: VintageNet.Persistence.Null, config: [ {"wlan0", %{ type: VintageNetWiFi, vintage_net_wifi: %{ networks: [ %{ key_mgmt: :wpa_psk, ssid: "backup_ssid", psk: "backup_password" } ] } }} ] -
远程固件更新:
elixir复制config :nerves_firmware_ssh, authorized_keys: [ "ssh-ed25519 AAAAC3..." ]更新命令:
bash复制
ssh nerves.local fwup -a -i firmware.fw -t complete
16. 性能基准测试数据
在Raspberry Pi 4上的实测数据:
| 指标 | 数值 |
|---|---|
| 冷启动时间 | 8.2s |
| 内存占用(空载) | 28MB |
| 进程创建速度 | 50k/sec |
| 网络延迟(local) | <1ms |
| GPIO响应延迟 | 15μs |
优化建议:
- 启用
config :nerves, :erlinit, [hang_on_exit: false]可减少1.2s启动时间 - 使用
config :elixir, :dot_erlang, false节省300KB内存
17. 社区资源推荐
-
官方资源:
- Nerves Project - 官方文档和示例
- Nerves Forum - 活跃讨论区
-
开源项目参考:
- Nerves Livebook - 交互式笔记本
- NervesJP - 日文资源集合
-
硬件兼容性列表:
- Supported Targets - 官方支持设备
- Community Systems - 社区维护系统
18. 未来升级路径
当需要升级Nerves版本时:
-
检查兼容性矩阵:
bash复制
mix hex.outdated -
分步升级:
bash复制export NERVES_VERSION=1.7.0 mix deps.update nerves nerves_bootstrap nerves_runtime -
验证工具链:
bash复制mix nerves.artifact nerves_system_${MIX_TARGET} --force
关键检查点:
- OTP版本兼容性
- 交叉编译器更新
- 内核配置变更
19. 备灾与恢复方案
19.1 备份关键配置
bash复制tar czvf nerves_backup.tar.gz \
config/ \
rootfs_overlay/ \
nerves_defconfig \
mix.lock
19.2 紧急恢复模式
创建恢复SD卡:
bash复制mix nerves.new recovery --target $MIX_TARGET
cd recovery
echo 'config :nerves, :firmware, autoboot: false' > config/config.exs
mix firmware.burn
启动后通过串口连接:
bash复制screen /dev/ttyUSB0 115200
19.3 工厂重置机制
在config/target.exs中添加:
elixir复制config :nerves, :erlinit, [
run_on_exit: """
if [ -e /root/factory_reset ]; then
rm -rf /root/*
touch /root/.factory_reset
fi
"""
]
触发重置:
bash复制ssh nerves.local touch /root/factory_reset
ssh nerves.local reboot
20. 结语:从配置到创新
完成Nerves环境配置只是嵌入式Elixir开发的第一步。这个框架真正的威力在于:
- 热代码升级实现99.99%可用性
- 分布式Erlang的天然支持
- 与Phoenix框架的无缝集成
- 函数式编程对嵌入式场景的独特优势
我最近的一个项目将Nerves与LiveView结合,实现了:
- 设备集群的实时监控
- 远程固件空中升级(OTA)
- 基于Web的硬件调试界面
这种开发体验彻底改变了我对嵌入式系统的认知。虽然初始环境配置需要一些耐心,但一旦越过这个门槛,你会发现用Elixir开发嵌入式设备竟能如此高效和愉快。
