After multiple verifications, it has been completed to reset the network back to AP mode and reconfigure the WiFi information by connecting GPIO26 and GND through a jumper cap or switch.
You can quickly re-enter Wi-Fi configuration in the following situations:
-
Incorrect Wi-Fi configuration, resulting in a long wait for the reset timer to expire
-
Unstable Wi-Fi, resulting in an inability to connect to my.farm.bot
-
Changing Wi-Fi requires reconfiguration to avoid reflashing the system
-
Changing location requires reconfiguration to avoid reflashing the system
/lib/os/ext/ap_trigger.ex code as below:
defmodule FarmbotOS.APTrigger do
@moduledoc """
通过 GPIO 引脚触发进入 AP 模式。
- 默认使用 GPIO26 (BCM 编号),可在 @gpio_pin 修改。
- 检测到按键低电平(falling edge)时,进入 AP 模式。
- LED 闪烁表示触发。
- 防止重复触发。
"""
use GenServer
require FarmbotOS.Logger
@gpio_pin 26
@led_flash_ms 500
# 启动 GenServer
def start_link(_args) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
@impl true
def init(_state) do
# 打开 GPIO 输入模式
{:ok, gpio} = Circuits.GPIO.open(@gpio_pin, :input)
# 设置上拉
:ok = Circuits.GPIO.set_pull_mode(gpio, :pullup)
# 设置下降沿触发
Circuits.GPIO.set_interrupts(gpio, :falling)
IO.puts("APTrigger: 监听 GPIO#{@gpio_pin},低电平触发进入 AP 模式。")
FarmbotOS.Logger.debug(1, "APTrigger: 监听 GPIO#{@gpio_pin},低电平触发进入 AP 模式。")
# first 表示第一次触发
# processing 表示正在处理
{:ok, %{gpio: gpio, first: true, processing: false}}
end
# 防止重复触发:如果正在处理,忽略新消息
@impl true
def handle_info({:circuits_gpio, pin, ts, 0}, %{processing: true} = state) do
IO.puts("GPIO #{pin} 触发被忽略,正在处理上一条触发,ts=#{ts}")
{:noreply, state}
end
# 第一次正常触发
@impl true
def handle_info({:circuits_gpio, pin, ts, 0}, %{first: true} = state) do
IO.puts("GPIO #{pin} 触发消息到达 APTrigger, 第一次!, ts=#{ts}")
FarmbotOS.Logger.debug(1, "APTrigger: GPIO #{pin} 触发 → 进入 AP 模式。")
# 设置正在处理标记
new_state = %{state | first: false, processing: true}
# 异步执行 factory_reset,同时闪烁 LED
Task.start(fn ->
# LED 闪烁一次
FarmbotOS.SystemLed.on()
Process.sleep(@led_flash_ms)
FarmbotOS.SystemLed.off()
# 执行出厂重置
FarmbotOS.System.factory_reset("GPIO trigger", true)
# 通知 GenServer 完成处理
GenServer.cast(__MODULE__, :done)
end)
{:noreply, new_state}
end
# 处理完成后,清除 processing 标记
@impl true
def handle_cast(:done, state) do
{:noreply, %{state | processing: false}}
end
# 其他 GPIO 消息
# @impl true
# def handle_info(msg, state) do
# IO.inspect(msg, label: "APTrigger 收到其他消息")
# {:noreply, state}
# end
end
/lib/os/ext/system_led.ex code as below:
# 定义 LED 控制模块
defmodule FarmbotOS.SystemLed do
@led_path "/sys/class/leds/ACT/brightness"
# 点亮
def on, do: File.write!(@led_path, "1")
# 熄灭
def off, do: File.write!(@led_path, "0")
# 闪烁
def blink(ms \\ 500) do
on()
Process.sleep(ms)
off()
end
end
/lib/farmbot_os.ex add # GPIO Trigger :
children = [
FarmbotOS.Asset.Repo,
FarmbotOS.EctoMigrator,
FarmbotOS.BotState.Supervisor,
FarmbotOS.Bootstrap,
{FarmbotOS.Configurator.Supervisor, []},
{FarmbotOS.Init.Supervisor, []},
FarmbotOS.Leds,
FarmbotOS.Celery.Scheduler,
FarmbotOS.FirmwareEstopTimer,
{FarmbotOS.Platform.Supervisor, []},
FarmbotOS.Asset.Supervisor,
FarmbotOS.Firmware.UARTObserver,
{Task.Supervisor, name: FarmbotOS.Task.Supervisor},
# GPIO Trigger
{FarmbotOS.APTrigger, []}
]