1. 开箱
DigiKey 联合 EEWORLD 举办的“你晒单,我买单”活动,进行了好几期,第8期终于入围了,非常开心,感谢 Digikey 和 EEWORLD。
这次终于入手了 Arduino 官方的板子,看看和其他系列的板子有什么不同。
等了7/8天,快递到了,还是经典的包装,熟悉的味道。
盒子小巧,印刷清晰。
蓝色的底板,非常经典。
板卡背面还有塑料还是亚克力外壳,非常nice。
2.Arduino UNO R4 WiFi 资料
官方介绍
https://docs.arduino.cc/hardware/uno-r4-wifi/#features
官方文档有板卡介绍,教程,Pinout,Datasheet,原理图,CAD 文件,非常全的资料。
特性1:核心处理能力:瑞萨 RA4M1
Uno R4 放弃了老旧的 ATmega328P,采用了 Renesas RA4M1 芯片。
-
主频:48MHz(比 R3 快了 3 倍)。
-
存储:256KB Flash 和 32KB SRAM(分别提升了 8 倍和 16 倍),这意味着你可以运行更复杂的算法。
-
架构:32 位 ARM 架构支持单周期数学运算,性能大幅提升。
特性2:无线连接:ESP32-S3 副处理器 【可惜的是WiFi和BLE不能共存】
为了实现强大的联网能力,板载集成了一个 ESP32-S3-MINI-1 模块。
-
Wi-Fi & 蓝牙:支持 2.4GHz Wi-Fi 和蓝牙 5.0 (BLE)。
-
独立运行:ESP32 负责复杂的网络协议栈,通过串口与主控 RA4M1 通信,减轻了主控的负担。
特性3:板载视觉:12x8 LED 矩阵 【可玩性很强】
这是 Uno R4 WiFi 最亮眼的外观特征。板子背面隐藏了 96 颗红色 LED。
-
无需额外硬件:可以直接通过代码显示字符、图标、甚至是简单的动画。
-
创意交互:非常适合制作倒计时器、状态指示灯或动态表情图。
特性4:强大的硬件扩展
Uno R4 WiFi 增加了很多专业级接口:
-
CAN 总线支持:内置 CAN 控制器,适合汽车电子或工业自动化项目。
-
DAC (12 位):支持真正的模拟电压输出(引脚 A0),不再仅限于 PWM 模拟。
-
HID 支持:可以模拟键盘、鼠标,通过 USB 与电脑交互。
-
Qwiic 接口:板载 I2C 扩展口,支持免焊接连接传感器。
-
RTC (实时时钟):内置 RTC,只要外接电池(VRTC 引脚),断电也能计时。
3. Arduino IDE 搭建开发环境
3.1安装 Uno R4 Wifi 包
打开 Arduino IDE,打开 board 管理器,输入 uno r4 wifi 开始搜索,找到 “Arduino UNO R4 Boards” 点击 INSTALL 开始安装板级支持包和编译器、烧写软件等工具。
安装 Arduino 通用串行总线设备
日志区列出了安装的工具
Tool arduino:dfu-util@0.11.0-arduino5 already installed
Tool builtin:dfu-discovery@0.1.2 already installed
Downloading packages
arduino:arm-none-eabi-gcc@7-2017q4
arduino:bossac@1.9.1-arduino5
arduino:openocd@0.11.0-arduino2
arduino:renesas_uno@1.5.2
Installing arduino:arm-none-eabi-gcc@7-2017q4
Configuring tool.
arduino:arm-none-eabi-gcc@7-2017q4 installed
Installing arduino:bossac@1.9.1-arduino5
Configuring tool.
arduino:bossac@1.9.1-arduino5 installed
Installing arduino:openocd@0.11.0-arduino2
Configuring tool.
arduino:openocd@0.11.0-arduino2 installed
Installing platform arduino:renesas_uno@1.5.2
Configuring platform.
Platform arduino:renesas_uno@1.5.2 installed
把开发板通过Type-C连接到电脑,电脑弹出通知, UNO WiFi R4 CMSIS-DAP 已就绪。
打开设备管理器,新增加一个串口
3.2 新建工程
为了更好的使用 Led Matrix,需要安装依赖库:ArduinoGraphics
打开 Blink 工程,点击上传,自动编译、下载,开发板上的 LED 就会闪烁。
4.PlatformIO 搭建开发环境】
4.1第一创建工程自动下载开发环境
创建第一个工程,进行下面的设置:
-
设置工程名字为 uno_r4_wifi_tutorial
-
选择开发板 Arduino Uno R4 WiFi
-
选择 Framework 为 Arduino
-
取消勾选,使用下面自定义路径保存工程
4.2 PlatformIO IDE 调试
修改 platformio.ini 增加如下一行 【经过测试,不需要这样修改,使用默认的 platform.ini 即可】
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:uno_r4_wifi]
platform = renesas-ra
board = uno_r4_wifi
framework = arduino
lib_deps =
arduino-libraries/ArduinoGraphics@^1.1.4
arduino-libraries/ArduinoBLE@^1.5.0
; 使用板载调试功能
debug_tool = cmsis-dap
monitor_speed = 921600
VS Code 调试,自动新增了如下3个配置
对应的配置文件 .vscode/launch.json 内容如下:
// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY
//
// PlatformIO Debugging Solution
//
// Documentation: https://docs.platformio.org/en/latest/plus/debugging.html
// Configuration: https://docs.platformio.org/en/latest/projectconf/sections/env/options/debug/index.html
{
"version": "0.2.0",
"configurations": [
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug",
"executable": "j:/LinTeX9527/MyEVAL/EEWORLD/dk_purchase08_2025/sdk/uno_r4_wifi_tutorial/.pio/build/uno_r4_wifi/firmware.elf",
"projectEnvName": "uno_r4_wifi",
"toolchainBinDir": "C:/Users/admin/.platformio/packages/toolchain-gccarmnoneeabi/bin",
"internalConsoleOptions": "openOnSessionStart",
"svdPath": "C:/Users/admin/.platformio/platforms/renesas-ra/misc/svd/R7FA4M1AB.svd",
"preLaunchTask": {
"type": "PlatformIO",
"task": "Pre-Debug"
}
},
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug (skip Pre-Debug)",
"executable": "j:/LinTeX9527/MyEVAL/EEWORLD/dk_purchase08_2025/sdk/uno_r4_wifi_tutorial/.pio/build/uno_r4_wifi/firmware.elf",
"projectEnvName": "uno_r4_wifi",
"toolchainBinDir": "C:/Users/admin/.platformio/packages/toolchain-gccarmnoneeabi/bin",
"internalConsoleOptions": "openOnSessionStart",
"svdPath": "C:/Users/admin/.platformio/platforms/renesas-ra/misc/svd/R7FA4M1AB.svd"
},
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug (without uploading)",
"executable": "j:/LinTeX9527/MyEVAL/EEWORLD/dk_purchase08_2025/sdk/uno_r4_wifi_tutorial/.pio/build/uno_r4_wifi/firmware.elf",
"projectEnvName": "uno_r4_wifi",
"toolchainBinDir": "C:/Users/admin/.platformio/packages/toolchain-gccarmnoneeabi/bin",
"internalConsoleOptions": "openOnSessionStart",
"svdPath": "C:/Users/admin/.platformio/platforms/renesas-ra/misc/svd/R7FA4M1AB.svd",
"loadMode": "manual"
}
]
}
点击调试
如果调试启动失败,开发板完全断电一次,再重新调试。成功进入调试状态,如下
5.测试 LED Matrix
我习惯了 PlatformIO 下开发程序,下面的代码都是在这个环境下开发的。
修改官方的 led matrix 工程,尝试了多种滚动模式,总结如下:
-
字符串滚动,不要给字符串添加 const 修饰符,不然没有显示;
-
其实最常用、最实用的滚动模式就是
SCROLL_LEFT
// To use ArduinoGraphics APIs, please include BEFORE Arduino_LED_Matrix
#include "ArduinoGraphics.h"
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;
void setup() {
Serial.begin(921600);
matrix.begin();
Serial.println("Starting...");
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
// add some static text
// will only show "UNO" (not enough space on the display)
const char text[] = "UNO r4";
matrix.textFont(Font_4x6);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println(text);
matrix.endText();
matrix.endDraw();
Serial.println("Done.");
delay(2000);
}
void loop() {
Serial.println("Starting scroll...");
// Make it scroll!
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
// matrix.textScrollSpeed(50);
matrix.textScrollSpeed(200); // NOTE: 数值越大,滚动越慢,即表示 scroll internal
// add the text
char text[] =
" Hello Uno R4 WiFi! "; // NOTE: 把 const
// 去掉,现在可以上下滚动了,但是过长的文字会被截断,所以上下滚动并不实用。
// 设置字体
matrix.textFont(Font_4x6);
// matrix.textFont(Font_5x7);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println(text);
matrix.endText(SCROLL_LEFT); // 文字从右边向左滑动 【最实用】
// matrix.endText(SCROLL_RIGHT); // 文字从左边向右滑动 【反直觉】
// matrix.endText(SCROLL_UP); // NOTE: 用来显示的字符不能有 const 修饰符
// matrix.endText(SCROLL_DOWN); // BUG: 有残影
matrix.endDraw();
Serial.println("Done."); // NOTE: 从打印日志和LED矩阵显示效果可以看出,文本滚动完成后,才会结束这一轮显示,进入到下一轮
}

6.测试 WiFi
6.1测试1:扫描周围的热点并列出来
6.1.1源码
#include <WiFiS3.h>
void listNetworks();
void printEncryptionType(int thisType);
void printMacAddress(byte mac[]);
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(DMOE_UART_BAUDRATE);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
// don't continue
while (true);
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("Please upgrade the firmware");
}
}
void loop() {
byte mac[6];
// scan for existing networks:
Serial.println("Scanning available networks...");
listNetworks();
WiFi.macAddress(mac);
Serial.println();
Serial.print("Your MAC Address is: ");
printMacAddress(mac);
delay(10000);
}
void listNetworks()
{
// scan for nearby networks:
Serial.println("** Scan Networks **");
int numSsid = WiFi.scanNetworks();
if (numSsid == -1) {
Serial.println("Couldn't get a WiFi connection");
while (true);
}
// print the list of networks seen:
Serial.print("number of available networks:");
Serial.println(numSsid);
// print the network number and name for each network found:
for (int thisNet = 0; thisNet < numSsid; thisNet++) {
Serial.print(thisNet);
Serial.print(") ");
Serial.print(WiFi.SSID(thisNet));
Serial.print(" Signal: ");
Serial.print(WiFi.RSSI(thisNet));
Serial.print(" dBm");
Serial.print(" Encryption: ");
printEncryptionType(WiFi.encryptionType(thisNet));
}
}
void printEncryptionType(int thisType)
{
// read the encryption type and print out the name:
switch (thisType) {
case ENC_TYPE_WEP:
Serial.println("WEP");
break;
case ENC_TYPE_WPA:
Serial.println("WPA");
break;
case ENC_TYPE_WPA2:
Serial.println("WPA2");
break;
case ENC_TYPE_WPA3:
Serial.println("WPA3");
break;
case ENC_TYPE_NONE:
Serial.println("None");
break;
case ENC_TYPE_AUTO:
Serial.println("Auto");
break;
case ENC_TYPE_UNKNOWN:
default:
Serial.println("Unknown");
break;
}
}
void printMacAddress(byte mac[])
{
for (int i = 0; i < 6; i++) {
if (i > 0) {
Serial.print(":");
}
if (mac[i] < 16) {
Serial.print("0");
}
Serial.print(mac[i], HEX);
}
Serial.println();
}
6.2流程图
初始化阶段:
-
串口初始化:等待串口连接(用于USB调试输出)
-
硬件检查:确认WiFi模块正常工作
-
固件检查:提示用户升级固件(如果版本过旧)
扫描循环(每10秒一次):
-
设备信息:获取并显示本机的MAC地址
-
网络扫描:扫描周围可用的WiFi网络
-
结果显示:对于每个找到的网络,显示:
-
网络名称(SSID)
-
信号强度(RSSI,单位dBm)
-
加密类型(WEP/WPA/WPA2/WPA3等)
-
-
延迟等待:暂停10秒后重新扫描
6.1.3执行结果
6.2 测试2:WiFi 吞吐率
测试由两部分组成:
-
UNO R4 WiFi 服务端
-
电脑上的 Python 程序
其核心工作原理是:客户端发送带有CRC32校验码的数据包,服务器端校验后原样发回,客户端再进行二次校验。通过测量成功回传的数据量,计算出真实的网络吞吐量(Mbps),并统计校验错误和丢包率。
6.2.1系统交互时序图
6.2.2 UNO R4 WiFi 流程图
6.2.3 Python 端流程图
6.2.4 uno r4 wifi 代码
#include <WiFiS3.h>
#include <WiFiUdp.h>
#include "arduino_secrets.h"
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;
unsigned int localPort = 2345;
WiFiUDP Udp;
byte packetBuffer[1024];
unsigned long totalBytes = 0;
unsigned long lastReportTime = 0;
unsigned long crcErrors = 0;
/* CRC32 查找表实现 */
uint32_t calculate_crc32(const byte *data, size_t n_bytes) {
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < n_bytes; i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++) {
crc = (crc >> 1) ^ (0xEDB88320 & (-(crc & 1)));
}
}
return ~crc;
}
String timeString(unsigned long ms);
void setup() {
Serial.begin(DEMO_UART_BAUDRATE);
while (!Serial);
Serial.println("\n========================================");
Serial.println(" Uno R4 WiFi UDP CRC32 Perf Server");
Serial.println("========================================");
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED || WiFi.localIP() == IPAddress(0, 0, 0, 0)) {
delay(500);
Serial.print(".");
}
Serial.println("\n\n>>> WiFi CONNECTION ESTABLISHED <<<");
Serial.print(" [+] Local IP: "); Serial.println(WiFi.localIP());
Serial.println("----------------------------------------");
Udp.begin(localPort);
lastReportTime = millis();
}
void loop() {
int packetSize = Udp.parsePacket();
if (packetSize >= 4) {
int len = Udp.read(packetBuffer, sizeof(packetBuffer));
if (len > 4) {
// 提取包末尾的 CRC (小端模式)
uint32_t receivedCrc;
memcpy(&receivedCrc, &packetBuffer[len - 4], 4);
// 计算数据的 CRC
uint32_t computedCrc = calculate_crc32(packetBuffer, len - 4);
if (computedCrc == receivedCrc) {
// 校验通过,回传
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(packetBuffer, len);
Udp.endPacket();
totalBytes += len;
} else {
crcErrors++;
}
}
}
unsigned long currentTime = millis();
if (currentTime - lastReportTime >= 3000) {
float duration = (currentTime - lastReportTime) / 1000.0;
float mbps = (totalBytes * 8.0) / duration / 1000000.0;
Serial.print("[Perf] ");
Serial.print(timeString(currentTime));
Serial.print(" | Rate: ");
Serial.print(mbps, 4);
Serial.print(" Mbps | CRC Errors: ");
Serial.println(crcErrors);
totalBytes = 0;
lastReportTime = currentTime;
}
}
String timeString(unsigned long ms) {
unsigned long seconds = ms / 1000;
unsigned long minutes = seconds / 60;
unsigned long hours = minutes / 60;
char buf[20];
sprintf(buf, "%02lu:%02lu:%02lu", hours % 24, minutes % 60, seconds % 60);
return String(buf);
}
6.2.5 电脑端 python 代码
import socket
import time
import zlib
import struct
# --- 配置区 ---
BOARD_IP = '192.168.3.37'
PORT = 2345
PACKET_SIZE = 1024 # 总大小 1KB (1020 字节数据 + 4 字节 CRC)
DURATION = 30 # 测试持续 30 秒
REPORT_INTERVAL = 3 # 报告间隔 3 秒
def run_udp_perf_test():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(0.5)
data_payload = b'x' * (PACKET_SIZE - 4)
# 计算数据的 CRC32 并打包为 4 字节小端字节序
crc = zlib.crc32(data_payload) & 0xFFFFFFFF
full_packet = data_payload + struct.pack('<I', crc)
rx_total_bytes = 0
tx_total_packets = 0
rx_total_packets = 0
crc_error_count = 0
print("\n" + "╔" + "═"*48 + "╗")
print(f"║{'UDP CRC32 PERFORMANCE TEST':^48}║")
print("╚" + "═"*48 + "╝\n")
start_time = time.time()
last_report_time = start_time
interval_rx_bytes = 0
try:
while (time.time() - start_time) < DURATION:
# 发送
sock.sendto(full_packet, (BOARD_IP, PORT))
tx_total_packets += 1
try:
# 接收
data, addr = sock.recvfrom(2048)
rx_total_packets += 1
# 验证回传数据的校验码
if len(data) >= 4:
received_data = data[:-4]
received_crc = struct.unpack('<I', data[-4:])[0]
computed_crc = zlib.crc32(received_data) & 0xFFFFFFFF
if received_crc == computed_crc:
rx_total_bytes += len(data)
interval_rx_bytes += len(data)
else:
crc_error_count += 1
except socket.timeout:
pass
# 周期性报告
now = time.time()
if now - last_report_time >= REPORT_INTERVAL:
mbps = (interval_rx_bytes * 8) / (now - last_report_time) / 1000000
print(f"[{time.strftime('%H:%M:%S')}] Rate: {mbps:>7.4f} Mbps | Success: {rx_total_packets}/{tx_total_packets}")
interval_rx_bytes = 0
last_report_time = now
# 频率控制,避免硬件缓冲区溢出
time.sleep(0.005)
except KeyboardInterrupt:
print("\n[User] Test Interrupted")
total_duration = time.time() - start_time
final_mbps = (rx_total_bytes * 8) / total_duration / 1000000
loss_rate = ((tx_total_packets - rx_total_packets) / tx_total_packets) * 100
print("\n" + "="*50)
print(f"{'FINAL SUMMARY':^50}")
print("-"*50)
print(f" > Duration : {total_duration:.2f} s")
print(f" > Avg Throughput : {final_mbps:.4f} Mbps")
print(f" > CRC Failures : {crc_error_count}")
print(f" > Packet Loss : {loss_rate:.1f}%")
print("="*50 + "\n")
sock.close()
if __name__ == "__main__":
run_udp_perf_test()
6.2.6 测试结果
UNO R4 WiFi 运行结果
[Perf] 00:03:50 | Rate: 0.0000 Mbps | CRC Errors: 0
[Perf] 00:03:53 | Rate: 0.0000 Mbps | CRC Errors: 0
[Perf] 00:03:56 | Rate: 0.0053 Mbps | CRC Errors: 0
[Perf] 00:03:59 | Rate: 0.0355 Mbps | CRC Errors: 0
[Perf] 00:04:02 | Rate: 0.0362 Mbps | CRC Errors: 0
[Perf] 00:04:05 | Rate: 0.0361 Mbps | CRC Errors: 0
[Perf] 00:04:08 | Rate: 0.0359 Mbps | CRC Errors: 0
[Perf] 00:04:11 | Rate: 0.0361 Mbps | CRC Errors: 0
[Perf] 00:04:15 | Rate: 0.0358 Mbps | CRC Errors: 0
[Perf] 00:04:18 | Rate: 0.0361 Mbps | CRC Errors: 0
[Perf] 00:04:21 | Rate: 0.0358 Mbps | CRC Errors: 0
[Perf] 00:04:24 | Rate: 0.0357 Mbps | CRC Errors: 0
[Perf] 00:04:27 | Rate: 0.0136 Mbps | CRC Errors: 0
Python 端运行结果
╔════════════════════════════════════════════════╗
║ UDP CRC32 PERFORMANCE TEST ║
╚════════════════════════════════════════════════╝
[12:00:18] Rate: 0.0352 Mbps | Success: 13/13
[12:00:21] Rate: 0.0358 Mbps | Success: 27/27
[12:00:24] Rate: 0.0361 Mbps | Success: 41/41
[12:00:27] Rate: 0.0359 Mbps | Success: 55/55
[12:00:30] Rate: 0.0361 Mbps | Success: 69/69
[12:00:34] Rate: 0.0357 Mbps | Success: 83/83
[12:00:37] Rate: 0.0361 Mbps | Success: 97/97
[12:00:40] Rate: 0.0357 Mbps | Success: 111/111
[12:00:43] Rate: 0.0357 Mbps | Success: 125/125
==================================================
FINAL SUMMARY
--------------------------------------------------
> Duration : 30.20 s
> Avg Throughput : 0.0358 Mbps
> CRC Failures : 0
> Packet Loss : 0.0%
==================================================



















