Workaround for the Raspberry Pi's WiFi Driver Crashing - and other WiFi disconnects
When keeping the Raspberry Pi Zero or Zero 2 or even other Raspberry Pis plugged in for weeks or months at a time, it can happen that you lose connectivity and get this or a similar error message from a problem with the wifi chip driver:
Nov 10 03:03:54 genmon kernel: [125997.788183] WARNING: CPU: 0 PID: 6804 at net/wireless/sme.c:752 __cfg80211_connect_result+0x3c0/0x410 [cfg80211]
Nov 10 03:03:54 genmon kernel: [125997.788187] Modules linked in: 8021q garp stp llc rtc_ds1307 brcmfmac brcmutil sha256_generic cfg80211 raspberrypi_hwmon hwmon rfkill snd_bcm2835(C) snd_pcm snd_timer snd bcm2835_codec(C) v4l2_mem2mem bcm2835_v4l2(C) bcm2835_mmal_vchiq(C) i2c_bcm2835 videobuf2_dma_contig v4l2_common videobuf2_vmalloc videobuf_mem uio_pdrv_genirq fixed uio i2c_dev2_memops videobuf2_v4l2 videobuf2_common videodev media vc_sm_cma(C) rpivid ip_tables x_tables ipv6
Nov 10 03:03:54 genmon kernel: [125997.788333] CPU: 0 PID: 6804 Comm: kworker/u8:2 Tainted: G WC 4.19.75-v7l+ #1270
Nov 10 03:03:54 genmon kernel: [125997.788336] Hardware name: BCM2835
This issue is also mentioned in several forums, including here.
If you had a screen and keyboard connected to the Rasbperry Pi, you'd notice that you lost your wifi address and with that connectivity to the internet or your local network. All you'd have to do to get wifi fully working again is
sudo ifconfig wlan0 down
sudo ifconfig wlan0 up
Older attempt at fixing it, which still lost wifi connection once in a while:
I automated this with a cronjob that runs once every 3 minutes. You can copy paste the following section into your Raspberry Pi shell, and it will set it up for you. Note that this is assuming your local ip address starts with 10. As in 10.0.1.22 for instance. If that's not the case, replace the 10. with 192. or whatever number is the beginning of a valid IP address in your wifi network:
sudo tee /usr/local/bin/wifi_fix.sh > /dev/null << 'EOF'
#!/bin/bash
status=`/sbin/ifconfig wlan0 | grep "inet 10."`
if [ ! -z "$status" ]; then exit 0; fi
echo "Detected wifi failure"
date
dmesg | grep __cfg80211_connect_result
/sbin/ifconfig wlan0 down
/sbin/ifconfig wlan0 up
echo "WiFi restored"
EOF
sudo chmod a+x /usr/local/bin/wifi_fix.sh
(sudo crontab -u root -l 2>/dev/null; echo "*/3 * * * * /usr/local/bin/wifi_fix.sh >> /var/log/wifi_fix.log 2>&1") | sudo crontab -u root -
Update: For a more comprehensive way, run these lines instead of using the instructions above:
# --- Keep Wi-Fi reliable: power save off, never give up reconnecting ---
WIFI=$(nmcli -t -f NAME,TYPE con show | awk -F: '$2=="802-11-wireless"{print $1; exit}')
sudo nmcli con modify "$WIFI" connection.auth-retries 0 connection.autoconnect-retries 0 802-11-wireless.powersave 2
# --- Keep the system log across reboots (max 200 MB) ---
sudo mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nStorage=persistent\nSystemMaxUse=200M\n' | sudo tee /etc/systemd/journald.conf.d/50-persistent.conf > /dev/null
sudo systemctl restart systemd-journald
# --- Wi-Fi watchdog: reconnects if the router stops answering ---
sudo tee /usr/local/sbin/wifi-watchdog.sh > /dev/null <<'EOF'
#!/bin/bash
# Runs every 2 min. If the router does not answer: reconnect (twice),
# then reload the Wi-Fi driver, then reboot (max once per 6 h, not in the first 30 min).
# Events: /var/log/wifi-watchdog.log
IFACE=wlan0
STATE=/run/wifi-watchdog.strikes
LAST=/var/lib/wifi-watchdog/last-reboot
LOG=/var/log/wifi-watchdog.log
log() { echo "$(date '+%F %T') $*" >> "$LOG"; logger -t wifi-watchdog "$*"; }
[ -f "$LOG" ] && [ "$(stat -c %s "$LOG")" -gt 1048576 ] && mv -f "$LOG" "$LOG.old"
CON=$(nmcli -t -f NAME,TYPE con show | awk -F: '$2=="802-11-wireless"{print $1; exit}')
[ -z "$CON" ] && exit 0
GW=$(ip -4 route show default dev "$IFACE" 2>/dev/null | awk '{print $3; exit}')
if [ -n "$GW" ] && ping -c3 -W2 -I "$IFACE" "$GW" >/dev/null 2>&1; then
[ -f "$STATE" ] && log "recovered after strike $(cat "$STATE")"
rm -f "$STATE"; exit 0
fi
n=$(( $(cat "$STATE" 2>/dev/null || echo 0) + 1 )); echo "$n" > "$STATE"
log "router ${GW:-(no route)} unreachable on $IFACE - strike $n, NetworkManager: $(nmcli -t -f GENERAL.STATE dev show "$IFACE" 2>/dev/null | cut -d: -f2-)"
reconnect() { log "reconnecting: nmcli connection up '$CON'"; nmcli -w 60 con up "$CON" >/dev/null 2>&1; }
case $n in
1|2) reconnect ;;
3) log "reloading Wi-Fi driver"
modprobe -r $(lsmod | awk '$1 ~ /^brcmfmac_/{print $1}') brcmfmac 2>/dev/null
sleep 3; modprobe brcmfmac; sleep 10; reconnect ;;
*) up=$(cut -d. -f1 /proc/uptime); last=$(cat "$LAST" 2>/dev/null || echo 0); now=$(date +%s)
if [ "$up" -gt 1800 ] && [ $((now - last)) -gt 21600 ]; then
mkdir -p "$(dirname "$LAST")"; echo "$now" > "$LAST"; log "rebooting"; sync; systemctl reboot
else
reconnect
fi ;;
esac
EOF
sudo chmod 755 /usr/local/sbin/wifi-watchdog.sh
printf '[Unit]\nDescription=Wi-Fi watchdog\nAfter=NetworkManager.service\n\n[Service]\nType=oneshot\nExecStart=/usr/local/sbin/wifi-watchdog.sh\nTimeoutStartSec=5min\n' | sudo tee /etc/systemd/system/wifi-watchdog.service > /dev/null
printf '[Unit]\nDescription=Run the Wi-Fi watchdog every 2 minutes\n\n[Timer]\nOnBootSec=5min\nOnUnitActiveSec=2min\nAccuracySec=10s\n\n[Install]\nWantedBy=timers.target\n' | sudo tee /etc/systemd/system/wifi-watchdog.timer > /dev/null
sudo systemctl daemon-reload
sudo systemctl enable --now wifi-watchdog.timer