【ルネサス技術コラム】Arduino Uno R4 WiFiでBLEのRSSIをOLED表示、信号強度を見える化してみた
Bluetooth機器を使ったIoTシステムでは、「通信できるかできないか」だけでなく、対象が近づいているのか、遠ざかっているのかを知りたい場面があります。
例えば、BLEタグを取り付けた工具や設備、作業者、搬送機などの存在確認です。実際の距離を正確に測ることは難しくても、信号強度の変化を見れば「いつもより離れている」「近くに来た」といった状態変化を把握できます。
今回も「エレクトロニクスを簡単に」をテーマに、Renesas RA4M1搭載のArduino Uno R4 WiFiを使って、特定のBLEデバイスを検出し、RSSI (Received Signal Strength Indication) をOLEDへ表示して見える化するPoCを作成しました。
Arduino Uno R4 WiFiは、メインMCUにRenesas RA4M1、無線機能にESP32-S3を搭載しており、Wi-FiやBluetooth機能を活用したIoT開発を手軽に始められます。
今回はそのBluetooth機能を活用し、BLEデバイスのRSSIを取得して、人やモノの接近・離脱、通信品質の変化を見える化してみます。
ポイントは、RSSIを距離計として使うのではなく、人やモノの接近・離脱、通信品質の変化を捉える指標として活用することです。
Arduino Uno R4 WiFi は ESP32-S3 による Wi-Fi / Bluetoothを備え、BLE対応ボードとして利用できます。
注意
このPoCは、RSSIを距離の目安として可視化するものです。RSSIは向き、遮蔽物、人体、周辺の2.4GHz帯機器などで大きく変動するため、正確な距離計にはなりません。見るべきなのは絶対値よりも変化の傾向です。
目次
RSSIは「距離」ではなく「傾向」で見る
RSSI (Received Signal Strength Indication)は、受信した無線信号の強さを示す値です。目安としては次のように見られます。
-40 dBm 前後: 強い
-60 dBm 前後: 中程度
-80 dBm 以下: 弱い
ただし同じ距離でも、向きや障害物、周囲の電波環境で数値は簡単に変わります。
そのためこのPoCでは、何mかを求めるのではなく、近づいた / 離れたを把握する用途に絞ります。
なぜLocal Nameで検出するのか
今回は、対象BLEデバイスをLocal Nameで絞り込む前提で進めます。
BLE機器によってはアドレスが固定で見えない場合もあるため、PoCでは見つかった名前で対象を識別する方が扱いやすいことがあります。
Local Name指定のメリットは次の通りです。
Windowsで見えている名前を手がかりにしやすい
コード上で設定が分かりやすい
PoCとして確認しやすい
一方で、注意点もあります。
相手機器が広告にLocal Nameを載せていないと検出できない
同名デバイスが複数あると区別しにくい
表示名と広告名が完全一致しない場合がある
そのため、まずスキャンして実際のLocal Nameを確認してから設定する流れがおすすめです。
今回のPoCでやること
流れはシンプルです。
BLEスキャンを開始
見つかったデバイスの Local Name を確認
一致したらRSSIを取得
値を平滑化
OLEDに数値・状態・バー表示を出す
単なるシリアル出力ではなく、その場で状態を見やすくするのが狙いです。
Arduino Uno R4 WiFiを使う理由
Arduino Uno R4 WiFi は Renesas RA4M1 と ESP32-S3を搭載し、Wi-Fi / Bluetooth を利用できます。BLEを扱うPoCでは、この内蔵無線が大きな利点です。
開発環境は PlatformIO + VS Code で進められ、ボード設定は board = uno_r4_wifi、プラットフォームは renesas-ra です。
使用部品
使用部品は次の通りです。
Arduino Uno R4 WiFi
SSD1306系 OLEDディスプレイ (I2C)
ジャンパーワイヤ
BLE発信元デバイス
まずWindowsで対象デバイス名を確認する
このPoCでは、追跡対象のLocal Nameを先に確認しておくのがポイントです。
Windowsでは設定 > Bluetooth とデバイスから対象機器を確認できます。
確認の流れ
スタート > 設定 > Bluetooth とデバイスを開く
対象BLE機器を接続候補または登録済み一覧で確認する
表示されているデバイス名を控える
その後、Arduino側のスキャン結果で実際のLocal Name を確認する
メモ
Windows上の表示名と、BLE広告で見える Local Name が少し違うことがあります。
最終的には、Arduinoのスキャンで見えた名前を TARGET_NAME に設定するのが確実です。
うまく検出できるかを先に確認する
最初に、見つかったBLEデバイスのAddress / Local Name / RSSI をシリアルモニタに出す確認用スケッチを使うと進めやすくなります。
#include <Arduino.h>
#include <ArduinoBLE.h>
void setup() {
Serial.begin(115200);
while (!Serial);
if (!BLE.begin()) {
Serial.println("BLE init failed");
while (1);
}
Serial.println("BLE scan start");
BLE.scan();
}
void loop() {
BLEDevice peripheral = BLE.available();
if (peripheral) {
Serial.println("---- Found BLE Device ----");
Serial.print("Address: ");
Serial.println(peripheral.address());
if (peripheral.hasLocalName()) {
Serial.print("Local Name: ");
Serial.println(peripheral.localName());
} else {
Serial.println("Local Name: (none)");
}
Serial.print("RSSI: ");
Serial.println(peripheral.rssi());
if (peripheral.hasAdvertisedServiceUuid()) {
Serial.print("Service UUIDs: ");
for (int i = 0; i < peripheral.advertisedServiceUuidCount(); i++) {
Serial.print(peripheral.advertisedServiceUuid(i));
Serial.print(" ");
}
Serial.println();
}
Serial.println();
}
}確認手順
スケッチを書き込む
シリアルモニタを 115200 で開く
対象BLE機器(例:BLE対応ヘッドセット)を近くに置く
表示される Local Name: を確認する
追跡したい名前を本編コードの
TARGET_NAMEに設定する
今回は、以下のように見えた LE-OpenRun Pro をターゲットにしてRSSIの変化を見える化します。
Found BLE Device
Address: XX:XX:XX:XX:XX:XX
RSSI: -78
Local Name: LE-OpenRun Pro 表示上は末尾に NUL が見えることがありますが、設定名としては通常 LE-OpenRun Pro を使えば十分です。
配線
OLEDはI2C接続、BLEはボード内蔵機能を使うので追加配線は不要です。
部品 | 端子 | 接続先 |
OLED | VCC | 5Vまたは3.3V |
OLED | GND | GND |
OLED | SDA | SDA |
OLED | SCL | SCL |
[SSD1306 OLED]
VCC -> 5V or 3.3V
GND -> GND
SDA -> SDA
SCL -> SCL仕組み
ロジックは次の通りです。
BLEを初期化
BLE.scan()でスキャン開始BLE.available()で見つかったデバイスを取得peripheral.localName()が対象 Local Name と一致するか確認一致したら
peripheral.rssi()を読み、OLED表示を更新
ArduinoBLEの基本的な流れは公式スキャン例と同じです。
ArduinoBLE Scan Example
サンプルコード
以下は、特定のLocal Nameを持つBLEデバイスを検出し、RSSIをOLEDに表示する最小構成サンプルです。
使い方TARGET_NAME を、確認したLocal Nameに置き換えてください。
今回の例なら LE-OpenRun Pro を設定します。
#include <Arduino.h>
#include <Wire.h>
#include <ArduinoBLE.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// 追跡したいBLEデバイスのLocal Name
const char* TARGET_NAME = "LE-OpenRun Pro";
int lastRssi = -100;
float smoothRssi = -100.0f;
bool foundTarget = false;
unsigned long lastSeenMs = 0;
unsigned long lastDisplayUpdate = 0;
const unsigned long displayIntervalMs = 200;
String rssiLevelText(float rssi) {
if (rssi >= -55) return "NEAR";
if (rssi >= -70) return "MID";
return "FAR";
}
int rssiToBarWidth(float rssi) {
float clamped = rssi;
if (clamped < -95) clamped = -95;
if (clamped > -35) clamped = -35;
return (int)((clamped + 95.0f) / 60.0f * 100.0f);
}
void drawScreen(bool found, int rawRssi, float avgRssi) {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.print("BLE RSSI Monitor");
if (!found) {
display.setCursor(0, 12);
display.print("Scanning...");
display.display();
return;
}
display.setCursor(0, 10);
display.print("RSSI:");
display.print(rawRssi);
display.print(" dBm");
display.setCursor(0, 20);
display.print("ST:");
display.print(rssiLevelText(avgRssi));
int bar = rssiToBarWidth(avgRssi);
display.drawRect(70, 20, 54, 8, SSD1306_WHITE);
display.fillRect(72, 22, map(bar, 0, 100, 0, 50), 4, SSD1306_WHITE);
display.display();
}
bool isTargetDevice(BLEDevice peripheral) {
if (!peripheral.hasLocalName()) return false;
String name = peripheral.localName();
name.trim();
return name.startsWith(TARGET_NAME);
}
void setup() {
Serial.begin(115200);
delay(500);
Wire.begin();
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
while (1) delay(1000);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Starting BLE...");
display.display();
if (!BLE.begin()) {
display.clearDisplay();
display.setCursor(0, 0);
display.println("BLE init failed");
display.setCursor(0, 10);
display.println("Check firmware");
display.display();
while (1) delay(1000);
}
BLE.scan();
display.clearDisplay();
display.setCursor(0, 0);
display.println("BLE scan started");
display.display();
}
void loop() {
BLEDevice peripheral = BLE.available();
if (peripheral) {
if (isTargetDevice(peripheral)) {
int rssi = peripheral.rssi();
lastRssi = rssi;
smoothRssi = smoothRssi * 0.7f + rssi * 0.3f;
foundTarget = true;
lastSeenMs = millis();
Serial.print("Found target name: ");
Serial.print(peripheral.localName());
Serial.print(" RSSI: ");
Serial.println(rssi);
}
}
if (millis() - lastSeenMs > 3000) {
foundTarget = false;
}
if (millis() - lastDisplayUpdate >= displayIntervalMs) {
lastDisplayUpdate = millis();
drawScreen(foundTarget, lastRssi, smoothRssi);
}
}PlatformIO設定例
[env:uno_r4_wifi]
platform = renesas-ra
board = uno_r4_wifi
framework = arduino
monitor_speed = 115200
lib_deps =
arduino-libraries/ArduinoBLE
adafruit/Adafruit GFX Library
adafruit/Adafruit SSD1306プログラムのポイント
このサンプルでは、単にRSSIを表示するだけではなく、BLEを使ったPoCとして扱いやすいようにいくつかの工夫を入れています。
1. Local Nameで対象を絞り込む
BLEスキャンを実行すると、周囲のさまざまなデバイスが検出されます。
そこで今回は、対象機器のLocal Name を指定し、一致したデバイスだけを監視しています。
const char* TARGET_NAME = "LE-OpenRun Pro";2. RSSIを平滑化して表示する
RSSIは無線信号のため、同じ場所に置いていても値が変動します。
例えば、
人が間を通る
金属棚の近くで測定する
機器の向きが変わる
Wi-Fi機器が増える
といった条件で変化することがあります。
そこで本サンプルでは、
smoothRssi = smoothRssi * 0.7f + rssi * 0.3f;を使って平滑化し、急激な変動を抑えています。これにより、瞬間的なノイズよりも全体的な傾向を把握しやすくなります。
3. RSSIを距離ではなく状態として扱う
RSSIから正確な距離を求めることは難しいため、本サンプルでは数値をそのまま見るのではなく、
NEAR
MID
FAR
の3段階に分類しています。
これにより、「何m離れているか」ではなく、「近づいている」「遠ざかっている」という状態変化を直感的に把握できます。
4. 一時的な受信途絶に対応する
BLE広告は一定周期で送信されるため、瞬間的に受信できないことがあります。
そのため、本サンプルでは最後に受信してから3秒間は状態を保持し、すぐに見失った判定にならないようにしています。
if (millis() - lastSeenMs > 3000) {
foundTarget = false;
}これにより、一時的な電波状況の変化でも表示が安定します。
5. OLEDでその場で確認できる
シリアルモニタだけでもRSSIは確認できますが、現場ではPCを常時接続できない場合があります。
そこでOLEDを利用し、
RSSI値
状態(NEAR/MID/FAR)
バーグラフ
を表示することで、PCなしでも無線状態を確認できるようにしています。
OLED表示の内容
表示はシンプルです。
BLE RSSI Monitor
RSSI: -62 dBm
ST:MID
[##########......]RSSI: 現在の受信強度
ST: ざっくりした強弱
バー: 直感的に見やすくするための表示
対象が見つからないときは、次のように表示されます。
BLE RSSI Monitor
Scanning...対象デバイスの距離によって信号強度が変化します。
トラブルシュート
検出できないときの確認ポイント
OLEDに Scanning... が表示されたままの場合は、対象デバイスが見つかっていない可能性があります。まずは次の項目を確認します。
BLE機器の電源が入っているか
BLE機器が広告 (Advertising) を送信しているか
Arduino Uno R4 WiFi のBluetooth機能が正常に初期化されているか
対象機器が十分近くにあるか
TARGET_NAMEに設定した名前が実際の Local Name と一致しているか
特にBLE機器によっては、Windows上の表示名と広告パケット内の Local Name が異なることがあります。そのため、最初に確認用スケッチで実際の Local Name を確認しておくことが重要です。
BLE初期化に失敗した場合
起動時に次のメッセージが表示された場合は、Bluetooth機能の初期化に失敗しています。
BLE init failed
Check firmwareその場合は以下を確認します。
PlatformIOのボード設定
ArduinoBLEライブラリの導入状況
ボードファームウェアの更新状況
USBケーブルの接続状態
RSSI値が不安定な場合
RSSIは無線信号を利用するため、同じ距離でも値が大きく変動することがあります。
例えば、
人が間に入る
金属ラックの近くで測定する
Wi-Fi機器が多数存在する
BLE機器の向きが変わる
といった条件で数dBから十数dB変化することがあります。
そのため、このPoCでは単発の値で判断するのではなく、平滑化したRSSIを参考にしながら、距離ではなく「変化の傾向」を見ることをおすすめします。
調整ポイント
TARGET_NAME
TARGET_NAMEは、実際に追跡したい機器のLocal Nameに合わせます。まずWindowsで候補名を確認し、次にArduino側の確認用スケッチで実際に見えている Local Name を確認してから設定するのが確実です。検出できない場合
対象が見えないときは、相手機器が広告パケットにLocal Nameを載せていない可能性があります。
その場合は、別の識別方法を使うか、別の機器で試す方が確実です。名前比較
機器によっては末尾に余分な文字が付いて見えることがあります。
そのため、完全一致ではなくstartsWith()を使った前方一致の方が安定することがあります。RSSIの閾値
NEAR/MID/FAR の境界は環境でかなり変わるため、実際に近づけたり離したりして調整します。・RSSI ≥ -55dBm → NEAR
・-70dBm ~ -55dBm → MID
・RSSI < -70dBm → FARただし実距離ではなく環境依存の目安です。
平滑化
値が暴れるなら平均化を強め、反応速度を重視するなら弱めます。OLEDサイズ
128×64のOLEDなら、履歴グラフや最終検出状態なども追加しやすくなります。
改善案
発展させるなら、次のような拡張が考えられます。
OLEDにRSSIの折れ線グラフを表示
一定以上で NEAR アイコン表示
LED Matrixに接近状態を表示
Wi-Fi経由でRSSI履歴を送信
一定時間見つからなければ LOST 表示
複数のLocal Nameを監視
無線機能という観点では、Renesasは Bluetooth LE対応のRA6シリーズや各種無線モジュールも展開しています。今回のPoCはArduino Uno R4 WiFiを利用していますが、将来的にBLEビーコン監視や位置推定、設備トラッキング用途へ展開する際には、より低消費電力なRenesas製BLEソリューションの活用も検討できます。
まとめ
このPoCでは、Arduino Uno R4 WiFiとOLEDを使って、特定BLEデバイスをLocal Nameで検出し、RSSIの変化をその場で見える化できます。
ポイントは次の通りです。
対象をLocal Nameで分かりやすく指定できる
Windowsで事前に候補名を確認しやすい
Arduino側で実際の広告名を見て設定できる
OLEDで数値と状態を見やすく表示できる
RSSIを絶対距離ではなくトレンドとして扱える
将来的にLED MatrixやWi-Fi連携へ拡張しやすい
まずはローカル表示のPoCとして動かし、そこからログ化や接近通知へ広げていくと扱いやすいテーマです。








