【Digikey好物畅享】Arduino UNO R4 WiFi 搭建开发环境+试玩LED Matrix+WiFi测速

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第一创建工程自动下载开发环境

创建第一个工程,进行下面的设置:

  1. 设置工程名字为 uno_r4_wifi_tutorial

  2. 选择开发板 Arduino Uno R4 WiFi

  3. 选择 Framework 为 Arduino

  4. 取消勾选,使用下面自定义路径保存工程

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 工程,尝试了多种滚动模式,总结如下:

  1. 字符串滚动,不要给字符串添加 const 修饰符,不然没有显示;

  2. 其实最常用、最实用的滚动模式就是 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矩阵显示效果可以看出,文本滚动完成后,才会结束这一轮显示,进入到下一轮
}

VID_20260203_195244 00_00_00-00_00_30

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流程图

初始化阶段:

  1. 串口初始化:等待串口连接(用于USB调试输出)

  2. 硬件检查:确认WiFi模块正常工作

  3. 固件检查:提示用户升级固件(如果版本过旧)

扫描循环(每10秒一次):

  1. 设备信息:获取并显示本机的MAC地址

  2. 网络扫描:扫描周围可用的WiFi网络

  3. 结果显示:对于每个找到的网络,显示:

    1. 网络名称(SSID)

    2. 信号强度(RSSI,单位dBm)

    3. 加密类型(WEP/WPA/WPA2/WPA3等)

  4. 延迟等待:暂停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%
==================================================