不久前 Raspberry Pi Trading 推出了树莓派 Pico W开发板,该板与原始的树莓派 Pico 开发板拥有基本相同的设计。它们都带有 RP2040 双核 Cortex-M0+ 微控制器,比较不同的是 Pico W 增加了一个带 WiFi 4 和蓝牙 LE 5.2 的无线模块,虽然目前其蓝牙 LE 5.2 并未在板上启用,但未来可能就会开放了。
最近该公司给我寄一个树莓派 Pico W用来评测,本文我会集中精力测一下它 WiFi 相关的内容,因为树莓派 Pico W 支持与树莓派 Pico 板相同的 MicroPython 和 C/C++ SDK,还有用于无线连接的其他 API。
树莓派 Pico W 拆箱

我收到的这块板子是从一个 480 单元的卷盘上剪下来的。此外,我还收到了一根 1 米长的微型 USB 转 USB 数据线。注意,如果你只订购了 6 美元的开发板,默认情况下是不包含数据线的。

就像它的前身一样,其电路板很小,而且引脚排列与第一块 RP2040 板相同,他们在板的底部也有清楚的标记。

将树莓派 Pico W 连接到计算机上

一旦我们将开发板连接到主机,它就会在计算机中显示为 RPI-RP2 驱动器。我的笔记本电脑虽然运行的是 Ubuntu 20.04,但它与在 Windows 或 macOS 上运行是一样的。

RPI-RP2 驱动器中有两个文件包含了 UF2 引导加载程序版本和板子型号的 INFO_UF2.txt 和 重定向到树莓派网站Pico 文档的INDEX.HTM 。所以这里其实没有任何改变。
以下是其内核日志的输出,仅供参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[31039.896573] usb 1-1: new full-speed USB device number 7 using xhci_hcd [31040.068190] usb 1-1: New USB device found, idVendor=2e8a, idProduct=0003, bcdDevice= 1.00 [31040.068198] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [31040.068201] usb 1-1: Product: RP2 Boot [31040.068203] usb 1-1: Manufacturer: Raspberry Pi [31040.068205] usb 1-1: SerialNumber: E0C9125B0D9B [31040.126291] usb-storage 1-1:1.0: USB Mass Storage device detected [31040.126674] scsi host2: usb-storage 1-1:1.0 [31040.126893] usbcore: registered new interface driver usb-storage [31040.134814] usbcore: registered new interface driver uas [31041.138666] scsi 2:0:0:0: Direct-Access RPI RP2 3 PQ: 0 ANSI: 2 [31041.139090] sd 2:0:0:0: Attached scsi generic sg2 type 0 [31041.139462] sd 2:0:0:0: [sdc] 262144 512-byte logical blocks: (134 MB/128 MiB) [31041.140240] sd 2:0:0:0: [sdc] Write Protect is off [31041.140242] sd 2:0:0:0: [sdc] Mode Sense: 03 00 00 00 [31041.142282] sd 2:0:0:0: [sdc] No Caching mode page found [31041.142290] sd 2:0:0:0: [sdc] Assuming drive cache: write through [31041.150224] sdc: sdc1 [31041.154246] sd 2:0:0:0: [sdc] Attached SCSI removable disk |
带有 MicroPython 的 WiFi
树莓派 Pico 和 Pico W 共享相同的引导加载程序,但两者的 MicroPython 固件不同。我想可能是由于小的硬件差异,比如:用户 LED的连线;也可能是因为将 WiFi 栈添加到树莓派 Pico 上并没有什么意义,因为Pico上并没有WiFi,如果添加可能会浪费 Pico 上资源有限微控制器的宝贵存储和内存。
我们可以在树莓派网站上找到正确的固件文件,或者也可以直接下载它,如下所示:
1 |
wget https://micropython.org/download/rp2-pico-w/rp2-pico-w-latest.uf2 |
下载完成后,只需要将 rp2-pico-w-latest.uf2 复制到 RPI-RP2 驱动器上就可以了。

复制完成后,快驱动器就消失了。树莓派 Pico W 现在将会显示为串口设备:
1 2 3 4 5 6 7 8 9 |
[31932.896450] usb 1-1: new full-speed USB device number 8 using xhci_hcd [31933.070029] usb 1-1: New USB device found, idVendor=2e8a, idProduct=0005, bcdDevice= 1.00 [31933.070039] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [31933.070043] usb 1-1: Product: Board in FS mode [31933.070046] usb 1-1: Manufacturer: MicroPython [31933.070049] usb 1-1: SerialNumber: e6614c311b939432 [31933.164647] cdc_acm 1-1:1.0: ttyACM0: USB ACM device [31933.164710] usbcore: registered new interface driver cdc_acm [31933.164714] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters |
在 BootTerm 或 Putty、minicom、tio 等其他工具中也可以检测到树莓派 Pico W :
1 2 3 4 |
$ bt -l port | age (sec) | device | driver | description ------+------------+------------+------------------+---------------------- * 0 | 132 | ttyACM0 | cdc_acm | Board CDC |
如果我们连接到串行设备中,就会进入一个 REPL 界面,现在我们可以在其中键入一些命令来列出 2.4 GHz 接入点:
1 2 3 4 5 6 7 8 9 10 11 |
$ bt No port specified, using ttyACM0 (last registered). Use -l to list ports. Trying port ttyACM0... Connected to ttyACM0 at 115200 bps. Escape character is 'Ctrl-]'. Use escape followed by '?' for help. >>> import network >>> wlan = network.WLAN(network.STA_IF) >>> wlan.active(True) >>> print(wlan.scan()) [(b'CNX_Software_Xiaomi', b'<\xcdW\xf5\xaf\x92', 8, -24, 5, 5), (b'', b'B\xcdW\xf5\xaf\x92', 8, -26, 0, 2)] >>> |
很棒!这样操作是有用的。因为我住在农村,所以只检测到一个可见的接入点。另一个值 (”)是我的小米路由器上开启的隐藏 SSID。根据MicroPython API 文档来看,其他五个值应该分别是用于 bssid、通道号、RSSI、authmode 和隐藏状态的,但最后两个数字则超出了范围:
安全性有五个值:
- 0 – 打开
- 1 – WEP
- 2 – WPA-PSK
- 3 – WPA2-PSK
- 4 – WPA/WPA2-PSK
两个隐藏状态:
- 0 – 可见
- 1 – 隐藏
接下来更进一步,我们来查看该板“连接到 Internet”的指导文档。更新代码最简单方法是先安装 Thonny 编辑器:
1 2 |
sudo apt install python3-tk pip3 install thonny |
在运行—选择解释器—选择“MicroPython (Raspberry Pi Pico)”后,我们就可以毫无问题地在 Thonny IDE 中运行之前的 MicroPython 程序了。

现在我们使用以下程序连接到 CNX_Software_Xiaomi SSID:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import time import network ssid = 'CNX_Software_Xiaomi' password = 'The Password' wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) # Wait for connect or fail max_wait = 10 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') time.sleep(1) # Handle connection error if wlan.status() != 3: raise RuntimeError('network connection failed') else: print('connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) |
由上可以看到我的树莓派 Pico W 获得了一个 IP 地址,我可以 ping 通它:
1 2 3 4 5 6 |
$ ping 192.168.31.120 PING 192.168.31.120 (192.168.31.120) 56(84) bytes of data. 64 bytes from 192.168.31.120: icmp_seq=1 ttl=255 time=187 ms 64 bytes from 192.168.31.120: icmp_seq=2 ttl=255 time=35.3 ms 64 bytes from 192.168.31.120: icmp_seq=3 ttl=255 time=54.1 ms 64 bytes from 192.168.31.120: icmp_seq=4 ttl=255 time=77.1 ms |
作为参考,我们可以看到它在网络上显示为 PYPB:
1 2 3 4 5 6 |
$ nmap -sP 192.168.31.0/24 Starting Nmap 7.80 ( https://nmap.org ) at 2022-07-02 14:04 +07 ... Nmap scan report for PYBD (192.168.31.120) Host is up (0.026s latency). ... |
其固件附带安全的全局设置,但用户是可以通过设置国家和地区的方式来启用额外的频道:
1 2 |
import rp2 rp2.country('TH') |
如果你们遇到因通道不可用而无法连接到路由器的开发板,别着急,这其实也特别有用。
这种类型的板有一个很典型的用例就是具有控制 LED 或 GPIO 的 Web 界面。现在我们运行一个 Web 服务器来控制板上的用户 LED:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
import network import socket import time from machine import Pin # Select the onboard LED led = machine.Pin("LED", machine.Pin.OUT) ssid = 'CNX_Software_Xiaomi' password = 'A Password' wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) html = """<!DOCTYPE html> <html> <head> <title>CNX Software's Pico W </title> </head> <body> <h1>CNX Software's Pico W</h1> <p>%s</p> </body> </html> """ # Wait for connect or fail max_wait = 10 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') time.sleep(1) # Handle connection error if wlan.status() != 3: raise RuntimeError('network connection failed') else: print('connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.bind(addr) s.listen(1) print('listening on', addr) # Listen for connections while True: try: cl, addr = s.accept() print('client connected from', addr) request = cl.recv(1024) print(request) request = str(request) led_on = request.find('/light/on') led_off = request.find('/light/off') print( 'led on = ' + str(led_on)) print( 'led off = ' + str(led_off)) if led_on == 6: print("led on") led.value(1) stateis = "LED is ON" if led_off == 6: print("led off") led.value(0) stateis = "LED is OFF" response = html % stateis cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close() except OSError as e: cl.close() print('connection closed') |
代码有点长,因为它还包含一些错误处理。通过观看这个简短的演示视频中,我们其实也能看出它运行良好。相关视频链接,点击此处可查看。
在这里通过修改代码我们可以轻松控制继电器或 GPIO。还可以在树莓派 Pico W 上安装和运行流行的 iperf3 工具:
1 2 3 4 5 6 7 8 |
import network wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect('CNX_Software_Xiaomi', 'The Password') import upip upip.install("uiperf3") import uiperf3 uiperf3.client('192.168.31.48') |
代码很短,因为我们跳过了连接的错误处理。无论如何,我们总算得到了网络基准测试的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Connecting to ('192.168.31.48', 5201) Interval Transfer Bitrate 0.00-1.00 sec 492 KBytes 4.03 Mbits/sec 1.00-2.00 sec 552 KBytes 4.51 Mbits/sec 2.00-3.00 sec 587 KBytes 4.80 Mbits/sec 3.00-4.00 sec 579 KBytes 4.74 Mbits/sec 4.00-5.00 sec 567 KBytes 4.65 Mbits/sec 5.00-6.00 sec 521 KBytes 4.26 Mbits/sec 6.00-7.00 sec 551 KBytes 4.51 Mbits/sec 7.00-8.00 sec 563 KBytes 4.61 Mbits/sec 8.00-9.00 sec 564 KBytes 4.61 Mbits/sec 9.00-10.00 sec 533 KBytes 4.38 Mbits/sec - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.00-10.00 sec 5.38 MBytes 4.51 Mbits/sec sender 0.00-10.85 sec 5.38 MBytes 4.16 Mbits/sec receiver |
这个速度对我来说其实相当慢了,尽管有些性能并不不是这种平台的核心要求。其实这个基准测试它也可能是此类硬件上的典型代表。作为参考,MicroPython 的主要开发人员 Damien George 在 Pyboard D 系列板上也对其进行了测试,得到的测试结果达到了大约 7 Mbits/sec。
其实树莓派 Pico W 也可以用作最多四个客户端的接入点。我在 MicroPython 中暂时没有找到任何教程,但 ESP8266 和 ESP32 有相关说明,所以现在我们尝试设置以下 CNX-PICO SSID 并从手机连接:
1 2 3 4 5 6 |
import network ssid = 'CNX-PICO' password = 'cnx-review' ap.config(essid=ssid, password=password ap = network.WLAN(network.AP_IF) ap.active(True) |
操作之后,好消息是我的树莓派 Pico W 现在确实是一个接入点(AP),但不太好的是自定义 ssid 配置并不起作用,反而是板子会自动生成一个开放网络的接入点名称 — PICO349B。

因此要么是 API 不同,要么是树莓派 Pico W MicroPython 固件暂时还不支持自定义接入点名称。
通过 C 编程在树莓派 Pico W 上使用 WiFi
现在我们尝试使用 C/C++ SDK 重现以下上面的代码示例。如果你们没这么尝试过,那么就需要像我们为树莓派 Pico 板所做的那样设置一下 SDK,并在一个 Linux 计算机或树莓派板上获取该示例:
1 2 3 4 5 6 |
sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential git clone https://github.com/raspberrypi/pico-sdk cd pico-sdk git submodule update --init cd .. git clone -b master https://github.com/raspberrypi/pico-examples.git |
现在我们得到了一个 Pico W 的新目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ ls -l pico-examples/pico_w/ total 44 drwxrwxr-x 3 jaufranc jaufranc 4096 Jul 2 16:32 access_point drwxrwxr-x 2 jaufranc jaufranc 4096 Jul 2 16:32 blink -rw-rw-r-- 1 jaufranc jaufranc 985 Jul 2 16:32 CMakeLists.txt drwxrwxr-x 4 jaufranc jaufranc 4096 Jul 2 16:32 freertos drwxrwxr-x 2 jaufranc jaufranc 4096 Jul 2 16:32 iperf -rw-rw-r-- 1 jaufranc jaufranc 3417 Jul 2 16:32 lwipopts_examples_common.h drwxrwxr-x 2 jaufranc jaufranc 4096 Jul 2 16:32 ntp_client drwxrwxr-x 2 jaufranc jaufranc 4096 Jul 2 16:32 python_test_tcp drwxrwxr-x 2 jaufranc jaufranc 4096 Jul 2 16:32 tcp_client drwxrwxr-x 2 jaufranc jaufranc 4096 Jul 2 16:32 tcp_server drwxrwxr-x 2 jaufranc jaufranc 4096 Jul 2 16:32 wifi_scan |
这是 WiFi 扫描的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
** * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include <stdio.h> #include "pico/stdlib.h" #include "pico/cyw43_arch.h" static int scan_result(void *env, const cyw43_ev_scan_result_t *result) { if (result) { printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %u\n", result->ssid, result->rssi, result->channel, result->bssid[0], result->bssid[1], result->bssid[2], result->bssid[3], result->bssid[4], result->bssid[5], result->auth_mode); } return 0; } #include "hardware/vreg.h" #include "hardware/clocks.h" int main() { stdio_init_all(); if (cyw43_arch_init()) { printf("failed to initialise\n"); return 1; } cyw43_arch_enable_sta_mode(); absolute_time_t scan_test = nil_time; bool scan_in_progress = false; while(true) { if (absolute_time_diff_us(get_absolute_time(), scan_test) < 0) { if (!scan_in_progress) { cyw43_wifi_scan_options_t scan_options = {0}; int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result); if (err == 0) { printf("\nPerforming wifi scan\n"); scan_in_progress = true; } else { printf("Failed to start scan: %d\n", err); scan_test = make_timeout_time_ms(10000); // wait 10s and scan again } } else if (!cyw43_wifi_scan_active(&cyw43_state)) { scan_test = make_timeout_time_ms(10000); // wait 10s and scan again scan_in_progress = false; } } // the following #ifdef is only here so this same example can be used in multiple modes; // you do not need it in your code #if PICO_CYW43_ARCH_POLL // if you are using pico_cyw43_arch_poll, then you must poll periodically from your // main loop (not from a timer) to check for WiFi driver or lwIP work that needs to be done. cyw43_arch_poll(); sleep_ms(1); #else // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work // is done via interrupt in the background. This sleep is just an example of some (blocking) // work you might be doing. sleep_ms(1000); #endif } cyw43_arch_deinit(); return 0; } |
这比我们上面的 MicroPython 示例要复杂一些。现在我们准备系统来构建 C 示例:
1 2 3 4 5 |
cd pico-examples/ mkdir build cd build export PICO_SDK_PATH=../../pico-sdk cmake -DPICO_BOARD=pico_w -DWIFI_SSID="Your Network" -DWIFI_PASSWORD="Your Password" .. |
很显然,在这里我们需要更改 SSID 和密码用来匹配网络中的密码,以下是该命令的输出,仅供参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
Using PICO_SDK_PATH from environment ('../../pico-sdk') PICO_SDK_PATH is /home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-sdk Defaulting PICO_PLATFORM to rp2040 since not specified. Defaulting PICO platform compiler to pico_arm_gcc since not specified. -- Defaulting build type to 'Release' since not specified. PICO compiler is pico_arm_gcc -- The C compiler identification is GNU 9.2.1 -- The CXX compiler identification is GNU 9.2.1 -- The ASM compiler identification is GNU -- Found assembler: /usr/bin/arm-none-eabi-gcc Build type is Release PICO target board is pico_w. Using CMake board configuration from /home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-sdk/src/boards/pico_w.cmake Using board configuration from /home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-sdk/src/boards/include/boards/pico_w.h -- Found Python3: /usr/bin/python3.8 (found version "3.8.10") found components: Interpreter TinyUSB available at /home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-sdk/lib/tinyusb/src/portable/raspberrypi/rp2040; enabling build support for USB. cyw43-driver available at /home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-sdk/lib/cyw43-driver lwIP available at /home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-sdk/lib/lwip Enabling build support for Pico W wireless. Skipping tcp_client example as TEST_TCP_SERVER_IP is not defined Skipping Pico W FreeRTOS examples as FREERTOS_KERNEL_PATH not defined -- Configuring done -- Generating done -- Build files have been written to: /home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-examples/build |
我们现在可以构建 wifi_scan 示例了:
1 2 |
cd pico_w/wifi_scan make -j8 |
输出相当长,但基本应该都是按照如下方式开始和结束的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Scanning dependencies of target ELF2UF2Build [ 0%] Creating directories for 'ELF2UF2Build' [ 0%] No download step for 'ELF2UF2Build' [ 0%] No patch step for 'ELF2UF2Build' [ 0%] No update step for 'ELF2UF2Build' [ 0%] Performing configure step for 'ELF2UF2Build' -- The C compiler identification is GNU 9.4.0 -- The CXX compiler identification is GNU 9.4.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works ... [100%] Building C object pico_w/wifi_scan/CMakeFiles/picow_wifi_scan_background.dir/home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-sdk/lib/lwip/src/netif/ppp/polarssl/md5.c.obj [100%] Building C object pico_w/wifi_scan/CMakeFiles/picow_wifi_scan_background.dir/home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-sdk/lib/lwip/src/netif/ppp/polarssl/sha1.c.obj [100%] Building C object pico_w/wifi_scan/CMakeFiles/picow_wifi_scan_background.dir/home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-sdk/src/rp2_common/pico_lwip/nosys.c.obj [100%] Linking CXX executable picow_wifi_scan_background.elf [100%] Built target picow_wifi_scan_background |
该构建将会生成一堆文件,其中也包括了我们要用来复制到板上的 UF2 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ ls -l total 8560 drwxrwxr-x 4 jaufranc jaufranc 4096 Jul 2 16:48 CMakeFiles -rw-rw-r-- 1 jaufranc jaufranc 1027 Jul 2 16:48 cmake_install.cmake -rw-rw-r-- 1 jaufranc jaufranc 482519 Jul 2 16:48 Makefile -rwxrwxr-x 1 jaufranc jaufranc 306968 Jul 2 16:51 picow_wifi_scan_background.bin -rw-rw-r-- 1 jaufranc jaufranc 1499022 Jul 2 16:51 picow_wifi_scan_background.dis -rwxrwxr-x 1 jaufranc jaufranc 380952 Jul 2 16:51 picow_wifi_scan_background.elf -rw-rw-r-- 1 jaufranc jaufranc 452605 Jul 2 16:51 picow_wifi_scan_background.elf.map -rw-rw-r-- 1 jaufranc jaufranc 863499 Jul 2 16:51 picow_wifi_scan_background.hex -rw-rw-r-- 1 jaufranc jaufranc 614400 Jul 2 16:51 picow_wifi_scan_background.uf2 -rwxrwxr-x 1 jaufranc jaufranc 308304 Jul 2 16:51 picow_wifi_scan_poll.bin -rw-rw-r-- 1 jaufranc jaufranc 1490453 Jul 2 16:51 picow_wifi_scan_poll.dis -rwxrwxr-x 1 jaufranc jaufranc 418272 Jul 2 16:51 picow_wifi_scan_poll.elf -rw-rw-r-- 1 jaufranc jaufranc 437754 Jul 2 16:51 picow_wifi_scan_poll.elf.map -rw-rw-r-- 1 jaufranc jaufranc 867250 Jul 2 16:51 picow_wifi_scan_poll.hex -rw-rw-r-- 1 jaufranc jaufranc 616960 Jul 2 16:51 picow_wifi_scan_poll.uf2 |
由于我们已经在开发板上安装了 MicroPython 固件,现在我们需要按下 BOOT 按钮并重启开发板,从而进入大容量存储模式、并将picow_wifi_scan_poll.uf2文件复制到 RPI-RP2 驱动器。

在这里驱动器会直接消失,然后需要重新启动并自动启动程序,而且我还注意到该开发板已经不再显示为串口设备了。主要的原因是我们需要通过树莓派Pico W 的 UART 引脚(引脚 1 Tx、引脚 2 Rx 和引脚 3 GND)访问串行控制台。因此,我在树莓派Pico W 上的相关引脚上焊接了一个短接头,并使用 USB 转 TTL 板连接到我的笔记本电脑,但如果你们使用的是树莓派SBC 对 Pico W 板进行编程,可以把 UART改为 40 针 GPIO 接头。

现在我可以使用 BootTerm 、而且可以正确检测到 CNX_Software_Xiaomi SSID 和路由器中的隐藏 SSID:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ bt -n 0 ports found, waiting for a new one... port | age (sec) | device | driver | description ------+------------+------------+------------------+---------------------- 0 | 0 | ttyUSB0 | ch341-uart | USB2.0-Serial Trying port ttyUSB0... Connected to ttyUSB0 at 115200 bps. Escape character is 'Ctrl-]'. Use escape followed by '?' for help. Performing wifi scan ssid: rssi: -35 chan: 8 mac: 42:cd:57:f5:af:92 sec: 0 ssid: CNX_Software_Xiaomi rssi: -36 chan: 8 mac: 3c:cd:57:f5:af:92 sec: 5 ssid: CNX_Software_Xiaomi rssi: -35 chan: 8 mac: 3c:cd:57:f5:af:92 sec: 5 ssid: CNX_Software_Xiaomi rssi: -33 chan: 8 mac: 3c:cd:57:f5:af:92 sec: 5 ssid: rssi: -34 chan: 8 mac: 42:cd:57:f5:af:92 sec: 0 |
所以扫描是有效的,虽然它多次显示相同的 SSID。现在我们可以尝试使用wifi_connect/wifi_connect.c中的以下示例(示例来自 Pico W 文档)连接到我们的 WiFi 路由器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <stdio.h> #include "pico/stdlib.h" #include "pico/cyw43_arch.h" char ssid[] = "CNX_Software_Xiaomi"; char pass[] = "A Password"; int main() { stdio_init_all(); if (cyw43_arch_init_with_country(CYW43_COUNTRY_UK)) { printf("failed to initialise\n"); return 1; } printf("initialised\n"); cyw43_arch_enable_sta_mode(); if (cyw43_arch_wifi_connect_timeout_ms(ssid, pass, CYW43_AUTH_WPA2_AES_PSK, 10000)) { printf("failed to connect\n"); return 1; } printf("connected\n"); } |
这里只需要确保更改 ssid 、传递值,以及国家和地区(比如:CYW43_COUNTRY_TH) 。接着我们还需要一个CMakeLists.txt文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
cmake_minimum_required(VERSION 3.13) include(pico_sdk_import.cmake) project(test_project C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) pico_sdk_init() add_executable(wifi_connect wifi_connect.c ) pico_enable_stdio_usb(wifi_connect 1) pico_enable_stdio_uart(wifi_connect 1) pico_add_extra_outputs(wifi_connect) target_include_directories(wifi_connect PRIVATE ${CMAKE_CURRENT_LIST_DIR} ) target_link_libraries(wifi_connect pico_cyw43_arch_lwip_threadsafe_background pico_stdlib) |
对了,还需要将一些额外的文件复制到工作目录中:
1 2 |
cp ../../../pico-sdk/external/pico_sdk_import.cmake . cp ../lwipopts_examples_common.h lwipopts.h |
现在你们应该会得到有四个文件的文件夹:
1 2 3 4 5 6 |
jaufranc@cnx-laptop-4:~/edev/Raspberry-Pi-Pico-W/pico-examples/pico_w/wifi_connect$ ls -l total 16 -rw-rw-r-- 1 jaufranc jaufranc 480 Jul 3 11:59 CMakeLists.txt -rw-rw-r-- 1 jaufranc jaufranc 3417 Jul 3 12:01 lwipopts.h -rw-rw-r-- 1 jaufranc jaufranc 3165 Jul 3 12:00 pico_sdk_import.cmake -rw-rw-r-- 1 jaufranc jaufranc 519 Jul 3 11:55 wifi_connect.c |
现在就可以像以前一样构建程序了:
1 2 3 4 5 |
mkdir build cd build export PICO_SDK_PATH=../../../../pico-sdk/ cmake -DPICO_BOARD=pico_w .. make -j8 |
如果在 cmake 步骤中出现问题,只需要更正 CMakeLists.txt 文件并删除 build 文件夹中的所有文件,然后再次运行 cmake 命令就可以了。
因为我在泰国,所以最初以为 CYW43_COUNTRY_TH 就可以。然而结果却报错了!!!
1 2 3 4 5 |
/home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-examples/pico_w/wifi_connect/wifi_connect.c:11:36: error: 'CYW43_COUNTRY_TH' undeclared (first use in this function); did you mean 'CYW43_COUNTRY_UK'? 11 | if (cyw43_arch_init_with_country(CYW43_COUNTRY_TH)) { | ^~~~~~~~~~~~~~~~ | CYW43_COUNTRY_UK /home/jaufranc/edev/Raspberry-Pi-Pico-W/pico-examples/pico_w/wifi_connect/wifi_connect.c:11:36: note: each undeclared identifier is reported only once for each function it appears in |
如果你们也遇到了这种问题,pico-sdk/lib/cyw43-driver/src/cyw43_country.h 中提供了可用的国家/地区目录。下面是一个摘录,可以查看了解:
1 2 3 4 5 6 7 |
... #define CYW43_COUNTRY_SWITZERLAND CYW43_COUNTRY('C', 'H', 0) #define CYW43_COUNTRY_TAIWAN CYW43_COUNTRY('T', 'W', 0) #define CYW43_COUNTRY_THAILAND CYW43_COUNTRY('T', 'H', 0) #define CYW43_COUNTRY_TURKEY CYW43_COUNTRY('T', 'R', 0) #define CYW43_COUNTRY_UK CYW43_COUNTRY('G', 'B', 0) #define CYW43_COUNTRY_USA CYW43_COUNTRY('U', 'S', 0) |
我改用 CYW43_COUNTRY_THAILAND 之后就能完成构建了。我们所需的 UF2 文件如下所示:
1 2 |
ls -l *.uf2 -rw-rw-r-- 1 jaufranc jaufranc 638976 Jul 3 14:00 wifi_connect.uf2 |
现在我们用通常的方法把它刷到板子上,看看我们是否真的可以连接到路由器:
1 2 3 4 5 6 7 8 9 |
$ bt -n 1 ports found, waiting for a new one... port | age (sec) | device | driver | description ------+------------+------------+------------------+---------------------- 1 | 0 | ttyACM0 | cdc_acm | Board CDC Trying port ttyACM0... Connected to ttyACM0 at 115200 bps. Escape character is 'Ctrl-]'. Use escape followed by '?' for help. connected |
这里并没有打印太多信息,但至少程序运行没有错误。如果想要在树莓派Pico W 上使用 C 语言其实需要一段时间的学习,包括学习新的 CYW43 API、LWiP 库,甚至使用 FreeRTOS 来获得更复杂的示例。
因为目前没有通过一个网页来控制 LED 的示例代码,所以我们还是先来查看一下 iperf的 示例。我们先进入 /pico-examples/build/pico_w/iperf文件夹,并构建示例:
1 |
make -j8 |
在这种情况下,我们有两个 UF2 文件:
1 2 3 |
$ ls -l *.uf2 -rw-rw-r-- 1 jaufranc jaufranc 629760 Jul 3 14:28 picow_iperf_server_background.uf2 -rw-rw-r-- 1 jaufranc jaufranc 632832 Jul 3 14:28 picow_iperf_server_poll.uf2 |
我们要查看通过 PICO_CYW43_ARCH_POLL 选择的代码:
- PICO_CYW43_ARCH_POLL = 1 –如果使用 pico_cyw43_arch_poll,那么必须从主循环(而不是计时器)定期轮询,从而来检查需要完成的 WiFi 驱动程序或 lwIP 工作
- PICO_CYW43_ARCH_POLL != 1 – 如果不使用 pico_cyw43_arch_poll,则 需要 WiFI 驱动程序和 lwIP 工作通过后台中断的方式来完成。当然这个睡眠只是一些例子
使用轮询或中断程序流是两种不同的编程方法。中断程序流通常更有效,但更复杂。这里我只是将picow_iperf_server_background.uf2复制到板上进行了测试。注意,该实现要基于 iperf2,而不是像 MicroPython 示例那样基于 iperf3。如下所示,iperf 服务器可以在树莓派 Pico W 上运行:
1 2 3 4 5 6 7 8 9 10 11 12 |
$ bt -n 1 ports found, waiting for a new one... port | age (sec) | device | driver | description ------+------------+------------+------------------+---------------------- 0 | 0 | ttyUSB0 | ch341-uart | USB2.0-Serial Trying port ttyUSB0... Connected to ttyUSB0 at 115200 bps. Escape character is 'Ctrl-]'. Use escape followed by '?' for help. Connecting to WiFi... Connected. Ready, running iperf server at 192.168.31.120 |
现在我们在笔记本电脑上运行 iperf 客户端:
1 2 3 4 5 6 7 8 |
jaufranc@cnx-laptop-4:~$ iperf -c 192.168.31.120 ------------------------------------------------------------ Client connecting to 192.168.31.120, TCP port 5001 TCP window size: 85.0 KByte (default) ------------------------------------------------------------ [ 3] local 192.168.31.48 port 50008 connected with 192.168.31.120 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-10.0 sec 9.75 MBytes 8.15 Mbits/sec |
运行得到的结果是 8 Mbps,比使用 MicroPython 运行时要好一点,但仍然在同一个范围内。其结果也显示在了 Pico W 串口控制台中:
1 2 |
Completed iperf transfer of 9 MBytes @ 8.1 Mbits/sec Total iperf megabytes since start 9 Mbytes |
如果你们想要在 Pico W 上以客户端的方式运行 iperf 示例,就需要在标题后添加以下两行:
1 2 |
#define CLIENT_TEST 1 #define IPERF_SERVER_IP <IP_address of the server> |
这个“入门指南”感觉越写越长了,比我最初预期的要更多,所以这部分我直接跳过了。
现在,我们使用树莓派 Pico W 的接入点演示来完成 WiFi 教程。在构建示例之前,要先查看pico-examples/pico_w/access_point/picow_access_point.c 中的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const char *ap_name = "picow_test"; #if 1 const char *password = "password"; #else const char *password = NULL; #endif cyw43_arch_enable_ap_mode(ap_name, password, CYW43_AUTH_WPA2_AES_PSK); ip4_addr_t gw, mask; IP4_ADDR(&gw, 192, 168, 4, 1); IP4_ADDR(&mask, 255, 255, 255, 0); // Start the dhcp server dhcp_server_t dhcp_server; dhcp_server_init(&dhcp_server, &gw, &mask); |
在 main() 函数中,这部分我们可以看到默认的 SSID(接入点name0 是 picow_test,密码是 password(WPA2安全),子网是192.168.4.0,这个程序还会启动 DHCP 服务器,让客户端获取 IP 地址,如果设置密码为 NULL,应该就是一个开放网络,因此我把ap_name改成picow_cnxsoft,密码改成了 123456。
1 2 3 4 5 6 |
const char *ap_name = "picow_cnxsoft"; #if 1 const char *password = "123465"; #else const char *password = NULL; #endif |
现在像往常一样构建示例,并将picow_access_point_poll.uf2或picow_access_point_background.uf2复制到板上。下面就是其终端输出:
1 2 3 4 5 |
$ bt No port specified, using ttyUSB0 (last registered). Use -l to list ports. Trying port ttyUSB0... Connected to ttyUSB0 at 115200 bps. Escape character is 'Ctrl-]'. Use escape followed by '?' for help. Starting server on port 80 |
注意端口 80 上的服务器不是 Web 服务器,而是一个基本的 TCP 服务器,我认为它主要应该是为了与安装在另一个树莓派 Pico 上的 TCP 客户端示例一起使用的。
不过,我也可以将手机连接到 picow_cnxsoft 接入点中。

也可以在我的 Ubuntu 笔记本电脑中接入

这两款设备也出现在了树莓派 Pico W 的串口终端中:
1 2 3 |
Starting server on port 80 DHCPS: client connected: MAC=bc:2e:f6:6a:71:64 IP=192.168.4.16 DHCPS: client connected: MAC=70:c9:4e:b7:84:77 IP=192.168.4.17 |
树莓派 Pico W 的文档对于 WiFi 入门来说其实还是不错的,但不一定完整,比如目前还没有在 MicroPython 中设置接入点的示例代码。又比如我必须要查看 C/C++ SDK 中的代码,而不是文档,才能找到对应国家和地区的设置。不过,我觉得大多数人在该开发板上首先使用的还会是 MicroPython,因为 C/C++ SDK 虽然更灵活,但学习难度和学习时长也会大很多。

文章翻译者:Taylor Lee,瑞科慧联(RAK)高级嵌入式开发工程师,有丰富的物联网和开源软硬件经验,熟悉行业主流软硬件框架,对行业发展动向有着敏锐的感知力和捕捉能力。