【ルネサス技術コラム】Arduino Nano R4でマイクとLCDを使った簡易騒音可視化をしてみた

作業場所や室内環境で、「なんとなくうるさい」「時間帯によって騒がしさが違う」と感じることはあっても、それを数値で説明するのは意外と難しいものです。そこで今回も「エレクトロニクスを簡単に」をテーマに、Renesas RA4M1搭載のArduino Nano R4とアナログ出力付きマイクモジュール、I2C LCDディスプレイを使って、現在の騒音レベルと累積した騒音スコアを表示する簡易システムを作ってみました。
Arduino Nano R4は、Renesas RA4M1 (Arm Cortex-M4) を搭載したコンパクトなマイコンボードです。

今回はいつものUno系ではなく、Arduino Nano R4を使って実装する構成にしています。Nano系のコンパクトなサイズ感でブレッドボード上にまとめやすく、デスク周りや小型PoCにも載せやすいのが使いやすいポイントです。

今回の構成では、マイクモジュールのアナログ出力をArduinoのアナログ入力で読み取り、その変動幅をもとに「音の大きさ」を相対的に扱います。さらにLCDへ表示しながら時間方向に積算することで、どのくらいうるさい状態が続いたかも見えるようにしました。

なお、今回の騒音レベルや累積騒音スコアは、厳密なdB(デシベル) 計測ではなく、相対的な音量変化を可視化する簡易指標です。PoCとして、まずは環境音の変化と累積傾向を見える化することを主眼にしています。

目次

はじめに: どんな困りごとを解決したいのか

騒音に関する困りごとは、実はかなり身近です。たとえば次のようなケースがあります。

  • オフィスや作業部屋で、時間帯によって集中しづらい

  • 窓際や道路側の部屋が、どのくらい騒がしいのか感覚でしか分からない

  • 機械やファンの音が大きくなってきた気がするが、定量的に比較できない

  • 静かなはずの環境で、どの時間帯に騒音が増えているのか把握したい

  • 「今日はうるさかった」を感覚ではなく、ざっくり数値で見たい

こうした場面では、瞬間的に「今うるさいかどうか」だけでなく、その状態がどれくらい続いたかが重要になることがあります。

たとえば、瞬間的に大きな音が1回鳴っただけなのか、それとも小さめの騒音が長時間続いていたのかでは、実際の不快感や作業への影響がかなり違います。そこで今回は、現在の騒音レベルだけでなく、累積した騒音スコアも表示するようにしました。

つまり今回のソリューションは、以下のような困りごとに対するものです。

「なんとなくうるさい」を、まずは簡易的に見える化したい
「今」だけでなく「どれくらい長く騒がしかったか」も把握したい

まずは厳密な騒音計ではなく、比較しやすい簡易メーターとして始める、という位置づけです。

今回のソリューション概要

今回の仕組みでは、マイクモジュールのアナログ出力を Arduino Nano R4の A0 で読み取り、LCDに次の2つを表示します。

  • 1行目: 現在の騒音レベル

  • 2行目: 累積した騒音スコア

たとえば、

  • Noise: 42%

  • Sum: 1280pt

のように表示します。

ここでの Noise は、その瞬間の音の大きさを相対的に0~100%にしたものです。
また Sumは、その値を1秒ごとに加算していくことで、騒がしい状態がどのくらい続いたかを表す簡易スコアです。

たとえば、

  • 80%の騒がしさが1秒続けば80ポイント

  • 20%の騒がしさが1秒続けば20ポイント

というイメージで加算されます。 つまりこの累積値は、騒がしさ×時間を簡易的に表しています。

なぜ今回はArduino Nano R4を使うのか

今回は、Arduino Uno R4 WiFiではなく Arduino Nano R4を使う構成にしています。

その理由は、今回のPoCが

  • マイク入力を1本読む

  • I2C LCDをつなぐ

  • その場で可視化する

という、比較的シンプルな構成だからです。この程度の構成であれば、Nanoサイズのコンパクトなボードで十分扱いやすく、ブレッドボード上でもまとまりやすくなります。

また、公式ドキュメントによると Arduino Nano R4 は Renesas RA4M1を搭載し、32-bit Arm Cortex-M4 / 48MHz / 5V動作の構成になっています。センサ入力を読みながらLCDを更新するような用途には十分使いやすいスペックです。

PlatformIO側でも RA4M1 / 48MHz / 256KB Flash / 32KB RAM のボードとしてサポートされており、board = nano_r4 で利用できます。
※USBドライバのインストールのため、事前にArduino IDEでの環境構築が必要になります。

今回は「小さく始める」「机上でサッと試す」という意味でも、Arduino Nano R4を選ぶ価値がある構成だと感じました。

Arduino Nano R4

ローカル計測の基本構成

今回の構成では、マイクモジュールのアナログ出力をArduino Nano R4で読み取り、LCDに現在の騒音レベルと累積騒音スコアを表示するシステムを作ります。

マイクモジュールのアナログ出力は、一般的に音圧に応じて電圧が細かく揺れる信号です。そのため、単純に1回だけ analogRead() した値を見るよりも、短時間の中での最大値と最小値の差を見るほうが、周囲の音の大きさをつかみやすくなります。

表示にはI2C接続のLCDディスプレイを使い、配線本数を抑えながら、現在の騒音レベルと累積騒音スコアをその場で確認できるようにします。

使用した主な部品

  • Arduino Nano R4

  • アナログ出力付きマイクモジュール

  • I2C LCD 1602

  • ブレッドボード

  • ジャンパーピン

  • USB-C ケーブル

開発環境と実装のポイント

開発環境には Visual Studio Code と PlatformIO IDE を使用しました。PlatformIOでは、Arduino Nano R4向けに platform = renesas-ra、board = nano_r4、framework = arduino の設定を使います。

実装上の注意

今回の構成はシンプルですが、そのままでは“デシベル”にはなりません。理由は、マイクモジュールの個体差、感度調整、周囲環境、電源ノイズ、設置方向などによって値が変わるからです。そのため、まずは「騒音の傾向を見る」 目的で使い、必要に応じて後から校正していくのが自然です。

また、LCDのI2Cアドレスは 0x27 を想定していますが、モジュールによっては 0x3F の場合もあります。その場合はコード中のアドレスを変更してください。

配線情報

今回は、マイクモジュールのアナログ出力をA0に接続し、LCDはI2C接続にしています。

モジュール

信号

接続先

補足

マイクモジュール

VCC

Arduino Nano R4 の 5V

モジュール仕様に応じて給電します。

マイクモジュール

GND

Arduino Nano R4 の GND

共通GNDです。

マイクモジュール

AO

Arduino Nano R4 の A0

アナログ出力を読み取ります。

I2C LCD 1602

SDA

Arduino Nano R4 の SDA

I2C通信用です。

I2C LCD 1602

SCL

Arduino Nano R4 の SCL

I2C通信用です。

I2C LCD 1602

VCC

Arduino Nano R4 の 5V

モジュール仕様に応じて給電します。

I2C LCD 1602

GND

Arduino Nano R4 の GND

共通GNDです。

配線イメージを言葉で整理すると

  • マイクモジュールの AO を Arduino Nano R4 の A0 へ接続

  • マイクモジュールの VCC を 5V、GND を GND へ接続

  • LCDの SDA と SCL を Nano R4の SDA / SCL へ接続

  • LCDの VCC を 5V、GND を GND へ接続

比較的シンプルな構成で、すぐにPoCを始めやすいのが利点です。

実際のプログラム

今回のローカル計測版で使用したプログラムは以下の通りです。 マイクのアナログ出力をA0から読み取り、現在の騒音レベルと累積騒音スコアをLCDへ表示します。
※本プログラムは、生成AIを用いて作成しております。

#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <math.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

const int micPin = A0;

// 少し短めにして反応を良くする
const unsigned long measureWindowMs = 30;

// 1秒ごとに累積
const unsigned long sampleIntervalMs = 1000;
unsigned long lastSampleTime = 0;

// 表示更新
const unsigned long displayIntervalMs = 200;
unsigned long lastDisplayTime = 0;

// ノイズスコア
unsigned long totalNoisePoints = 0;

// 最新値
int lastAmplitude = 0;
int lastPercent = 0;
int smoothedPercent = 0;

// 感度調整
const int noiseFloor = 4;        // 小さいほど小音を拾いやすい
const int minScale = 40;         // 最低スケール
int adaptiveScale = 120;         // 自動調整される上限
const int maxAdaptiveScale = 400; // 暴れ防止

int readAmplitude()
{
  unsigned long startMs = millis();
  int signalMin = 1023;
  int signalMax = 0;

  while (millis() - startMs < measureWindowMs)
  {
    int sample = analogRead(micPin);
    if (sample < signalMin)
      signalMin = sample;
    if (sample > signalMax)
      signalMax = sample;
  }
  return signalMax - signalMin;
}

int amplitudeToPercent(int amplitude)
{
  int effectiveAmplitude = amplitude - noiseFloor;
  if (effectiveAmplitude < 0)
  {
    effectiveAmplitude = 0;
  }

  // 大きい音が来たら上限を引き上げる
  if (effectiveAmplitude > adaptiveScale)
  {
    adaptiveScale = effectiveAmplitude;
  }
  else
  {
    // 徐々に下げる(感度を戻す)
    adaptiveScale = adaptiveScale - 1;
    if (adaptiveScale < minScale)
      adaptiveScale = minScale;
  }

  if (adaptiveScale > maxAdaptiveScale)
  {
    adaptiveScale = maxAdaptiveScale;
  }

  float normalized = (float)effectiveAmplitude / (float)adaptiveScale;
  if (normalized < 0.0f)
    normalized = 0.0f;
  if (normalized > 1.0f)
    normalized = 1.0f;

  // 小さい音も持ち上げる
  float boosted = sqrtf(normalized);
  int percent = (int)(boosted * 100.0f);

  if (percent < 0)
    percent = 0;
  if (percent > 100)
    percent = 100;

  return percent;
}

void setup()
{
  Serial.begin(115200);
  analogReadResolution(10);

  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Noise Meter");
  lcd.setCursor(0, 1);
  lcd.print("Starting...");
  delay(1000);
  lcd.clear();

  Serial.println("Microphone noise measurement started.");
}

void loop()
{
  unsigned long now = millis();

  // 音の振れ幅を読む
  lastAmplitude = readAmplitude();

  // 感度高めで%変換
  lastPercent = amplitudeToPercent(lastAmplitude);

  // 平滑化は弱めにして反応を良くする
  smoothedPercent = (smoothedPercent + lastPercent) / 2;

  // 1秒ごとに累積
  if (now - lastSampleTime >= sampleIntervalMs)
  {
    totalNoisePoints += smoothedPercent;
    lastSampleTime = now;

    Serial.print("Amp: ");
    Serial.print(lastAmplitude);
    Serial.print(" | Noise: ");
    Serial.print(smoothedPercent);
    Serial.print("%");
    Serial.print(" | Scale: ");
    Serial.print(adaptiveScale);
    Serial.print(" | Sum: ");
    Serial.print(totalNoisePoints);
    Serial.println(" pt");
  }

  // LCD更新
  if (now - lastDisplayTime > displayIntervalMs)
  {
    lcd.setCursor(0, 0);
    lcd.print("Noise: ");
    lcd.print(smoothedPercent);
    lcd.print("%   ");

    lcd.setCursor(0, 1);
    lcd.print("Sum: ");
    lcd.print(totalNoisePoints);
    lcd.print("pt   ");

    lastDisplayTime = now;
  }
}
[env:nano_r4]
[env:nano_r4]
platform = renesas-ra
board = nano_r4
framework = arduino
monitor_speed = 115200
lib_deps =
  marcoschwartz/LiquidCrystal_I2C

プログラムのポイント

  • analogRead(A0) で、マイクモジュールのアナログ出力を読み取っています。

  • 1回の瞬間値ではなく、短時間内の最大値と最小値の差を使って音の大きさを見ています。

  • noiseFloor で、無音時や環境ノイズの細かな揺れをある程度無視しつつ、元の版よりも値を小さめに設定することで、小さい音も拾いやすくしています。

  • 固定の fullScale で単純変換するのではなく、adaptiveScale を使って、最近の音量に応じて感度を自動調整するようにしています。

  • 音量の正規化後に sqrt() を使うことで、小さい音が埋もれにくく、低めの音量でも%表示に反映されやすいようにしています。

  • smoothedPercent では平滑化を残しつつ、元の版よりも弱めにしているため、表示の揺れを抑えながらも反応を少し速くしています。

  • totalNoisePoints は、1秒ごとに現在の%値を足し込むことで累積しています。

  • つまり今回の Sum は、相対的な累積騒音量であり、ポイント (pt) として扱っています。

  • シリアルモニタには Amp / Noise / Scale / Sum を出力しているため、どの程度の振れ幅をどの感度で解釈しているかも確認できます。

  • LCDの1行目に現在値、2行目に累積値を表示することで、今の騒がしさとこれまでの積み上がりを同時に確認できます。

動作確認

プログラムを書き込んだら、LCD表示とシリアルモニタを見ながら値の変化を確認します。

たとえば、

  • 静かな部屋に置く

  • 近くで会話する

  • 手を叩く

  • キーボードを強めに打つ

  • 扇風機や機械の近くに置く

といった操作をすると、1行目の Noise が以前よりも小さい音から変化しやすくなります。
また、シリアルモニタでは Amp と Scale も確認できるため、どの程度の振れ幅が入力され、どのくらいの感度で%へ変換されているかを把握できます。

そして、騒がしい状態が続くほど2行目の Sum が速く増えていきます。

この累積値を見ることで、単なる瞬間値だけでなく、時間を通した騒音の違いも分かりやすくなります。さらに今回の改善版では、小さな音の変化にも反応しやすくなっているため、静かな環境の中での差も以前より見つけやすくなります。

デモの様子
周囲の騒音に対してNoiseの値が変化し、Sumに加算されます
PCと接続してシリアル出力を確認します
シリアル上でNoise:、Scale、 Sumの出力が確認できます

発展編:ログ保存や可視化への拡張

LCDでその場の値を見るだけでもPoCとしては十分ですが、実際に使うなら 「今見る」から「あとで振り返る」に広げたくなります。

たとえば、次のような発展が考えられます。

  • 一定間隔でシリアルへログ出力する

  • PC側でCSV保存する

  • 日時ごとの騒音傾向をグラフ化する

  • 閾値を超えた時だけ通知する

  • 作業場所ごとの騒音差を比較する

今回の構成はPoCとしてシンプルなので、まずはローカル表示から始め、あとで可視化や記録へ広げやすいのが利点です。

今後の展望・改善案

今回の試作は「相対的な騒がしさ」と「その累積」を見ることが主目的ですが、さらに改善するなら次のような方向があります。

しきい値調整

今回の%表示は、noiseFloor と adaptiveScale の設定に依存します。環境によって静かな基準やうるさい基準が異なるため、設置場所ごとに調整すると使いやすくなります。

平均化や移動平均の改善

今回は簡単な平滑化だけ入れていますが、もう少し安定表示したい場合は、移動平均や指数移動平均を強めに使う方法もあります。

閾値超えの警告表示

あるレベルを超えた時にLCDへ LOUD! のような警告を出したり、LEDを点灯したりすると、視認性が上がります。

ログ保存の追加

日単位・時間帯単位で比較したい場合は、シリアルログや外部保存を追加すると、単なるその場表示から一歩進んだ分析がしやすくなります。

dB相当への校正

本当に騒音計に近い扱いをしたい場合は、既知の騒音計と比較しながら校正テーブルを作る必要があります。そこまで行くとPoCの範囲を超えますが、相対指標から次の段階へ進む方法として有効です。

ルネサス製品への展開

今回のPoCではArduino Nano R4を利用しましたが、実際の騒音監視システムへ発展させる場合はルネサスRAファミリを活用した組込み機器へ展開できます。

  • RA4M1

    • 小規模な騒音監視ノード

    • ローカル表示機器

  • RA6シリーズ

    • Wi-Fi/Bluetooth対応機器

    • エッジAI搭載騒音監視装置

  • センサー + RAマイコン

    • 騒音傾向監視

    • 閾値判定

  • クラウド連携

    • MQTT送信

    • ダッシュボード可視化

    • 騒音履歴分析

まとめ

今回は、Arduino Nano R4とアナログ出力付きマイクモジュール、I2C LCD 1602を使って、現在の騒音レベルと簡易的な累積騒音スコアを表示する仕組みを作ってみました。

特に今回のテーマは、単にマイク値を読むことではなく、

  • 作業環境がどれくらい騒がしいのか把握したい

  • 時間帯ごとの騒音傾向を見たい

  • 「なんとなくうるさい」を数値で比較したい

  • 騒音の瞬間値だけでなく、継続時間も見たい

という困りごとに対して、まずは小さく始められる可視化PoCを作ることにあります。

構成自体はシンプルですが、

  • マイクのアナログ出力を使う

  • Arduino Nano R4のA0で電圧変動を読む

  • 短時間の振れ幅から騒音レベルを求める

  • LCDに現在値を表示する

  • 騒がしさを時間方向に積算して累積値も表示する

という流れを通して、瞬間的な音の大きさと、その積み上がりの両方を見える化できる題材になりました。

いきなり本格的な騒音計へ進む前に、まずは Arduino Nano R4で環境音の変化と騒音の積み上がりを見てみるところから始めたい時に、ちょうどよいテーマだと思います。

本記事のNoise値は計量法に基づくdB値ではなく、マイク出力の変化量を相対指標として可視化したものです。

参考リンク