SoftwareSerialをもっと使おう

こんにちは、くのへ@MasazaneKunoheです。

前回の記事で、ATtiny85でSoftwareSerialの使い方を紹介しました。
その記事の内容はシリアル通信を使ってATtiny85から情報を送信し、パソコンのシリアルモニタに文字を表示させるものでした。

シリアル通信では送信だけではなく受信もできます。
更に、その受信情報を元にリアクションさせることもできます。
例えばパソコンが「R」という文字を送信し、それを受け取ったATtiny85が赤色LEDを光らせる、なんてことが出来ます!

この仕組みを上手く使えば、パソコンから送った信号でATtiny85を操ることが出来るようになるので、非常に面白いものが作れるポテンシャルがあります!!

紹介するもの

この記事では、シリアル通信の信号をATtiny85が受ける方法と、その受けた情報を元に動作させる方法をご紹介します。

①シリアル通信を受ける方法
②シリアル通信を受けて、それに応じて動作させる方法
③シリアル信号で文字列を受ける方法

これにより、パソコンからATtiny85を操ることが出来ます。
つまりパソコンで電子工作ガジェットを操作する応用が出来るようになります。

後でも出てきますが、こちらがパソコンからATtiny85に信号を送り、LEDを光らせているところの動画です。

詳細

①シリアル通信を受ける方法

サンプルコード

まずはやってみましょう。
シリアル通信で送られてきた情報を受けるサンプルコードはこちらです。

#include <SoftwareSerial.h>
 
#define RX_PIN 4   // PB4ピン(3番ピン)USB-シリアル変換モジュールのTXに
#define TX_PIN 3   // PB3ピン(2番ピン)USB-シリアル変換モジュールのRXに
 
SoftwareSerial mySerial(RX_PIN, TX_PIN);    // RX,TXの割り当て

void setup() {
  // SoftwareSerialの初期化
  mySerial.begin(9600);
  mySerial.println("ATtiny Ready");

}

void loop() {
  // 受信バッファ内にデータがあるかどうかを確認する
  if (mySerial.available()) {
    // 受信したデータを読み取る
    char data = mySerial.read();
    
    // 受信したデータをシリアルモニタに表示する
    mySerial.print("Received: ");
    mySerial.println(data);
  }
}

このコードをATtiny85に書き込んでください。

回路

回路は前回の記事と同じくこちらを使っています。

シリアルモニタを開いてみる

上記の回路を組んでパソコンに接続したら、ArduinoIDEの「ツール」⇒「シリアルポート」から、このシリアル通信モジュールのUSBポートを選択します。(下図参照)
USBを抜いたときに表示されず、USBを挿した時に表示されるシリアルポートがあれば、そいつです。
(注:私の場合は「COM6」でしたが、ご使用のパソコン等の環境で表示が違います。)

そして、ArduinoIDEのシリアルモニタを開いてください。
すると↓この画像ように、「ATtiny Ready」と表示されていると思います。
もし表示されていなければ、ブレッドボードからATtiny85を外してもう一度取り付ければ表示されます。
(コードの「Setup」が動けば表示されます)

これでスタンバイOKです。ATtiny85にシリアル通信で情報を送ってみましょう。
では、下図の赤枠のところに「1」と書き、赤枠右側の「送信」ボタンを押してみましょう。

このような表示になったと思います。

これで、ATtiny85でシリアル通信の「受信」が成功しています!!

これは、以下様な動作をしています。
1)パソコンからATtiny85に「1」と言う信号をシリアル通信で送信
2)ATtiny85がそれをキャッチ
3)ATtiny85が「Received: (受信したデータ)」というデータにして、シリアル通信でパソコンに打ち返した

つまり、ATtiny85がシリアル通信を受信して、それに応じて動くことに成功したということです。
ヨシ!!

コード解説

では、コードを解説していきます。
まず、シリアル通信を受信する基本的なコードは次のとおりです。

  if (mySerial.available()) {
    // 受信したデータを読み取る
    char data = mySerial.read();
    
    // 処理開始
    // ・・・
    // 処理終了
  }

if文+mySerial.available:信号の受信を検知

まず、if文とmySerial.available()の組み合わせで、信号の受信を検知します。
↓ここの部分です

if (mySerial.available()) 

シリアル通信でデータを受信すると、「バッファ」と呼ばれるメモリにデータが一時保存されます。mySerial.available()は、このバッファにデータが何も入っていなければ「false」、入っていれば「true」になるコードです。

そのため、if文+mySerial.available()の組み合わせにより、シリアル通信でデータを受信した時だけ動く処理を実装することが出来ます。

mySerial.read():受信データが一時保存されているバッファから1バイトデータを抽出

mySerial.read()を使うと、受信データが一時保存されている「バッファ」から1バイト分のデータを抽出できます。

但し、このデータは「アスキーコード(*)」のデータになっています。例えば「a」というデータは、実際には「97」という数字データになっています。
(*)アスキーコードについては小型ディスプレイSSD1306を紹介した記事の一番下の参考資料欄を参照してください。

これを逆変換するために、char型変数のdataにmySerial.read()を代入しています。
「print」と「println」でchar型変数を表示する場合、アスキーコードで逆変換して表示してくれるので、これで人間が読めるように表示することができます。
(例えばchar型変数「data」に97を代入し、println(data)を実行すると、「a」が表示される。)

シリアル通信の受信の基本コード

以上がこのコードの解説になりますが、シリアル通信の受信は以下が基本コードになります!!
1)if文+mySerial.available() でシリアル通信の受信を検出
2)char data = mySerial.read() で受信したデータをdataに格納する

②シリアル通信を受けて、それに応じて動作させる方法

上記①項のコードをちょっと書き換えて、「R」という信号をパソコンからATtiny85が受け取ったら、赤色LEDをON/OFFする仕組みを作ってみます。

以下がコードです。

#include <SoftwareSerial.h>
 
#define RX_PIN 4   // PB4ピン(3番ピン)USB-シリアル変換モジュールのTXに
#define TX_PIN 3   // PB3ピン(2番ピン)USB-シリアル変換モジュールのRXに
 
SoftwareSerial mySerial(RX_PIN, TX_PIN);    // RX,TXの割り当て

#define LED_PIN 1  // PB1ピン(6番ピン)LEDに接続

void setup() {
  // SoftwareSerialの初期化
  mySerial.begin(9600);
  mySerial.println("ATtiny Ready");

  //LEDピンの初期化
  pinMode(LED_PIN,OUTPUT);
  digitalWrite(LED_PIN,LOW);
}

void loop() {
  // 受信バッファ内にデータがあるかどうかを確認する
  if (mySerial.available()) {
    // 受信したデータを読み取る
    char data = mySerial.read();
    
    // 受信したデータをシリアルモニタに表示する
    mySerial.print("Received: ");
    mySerial.println(data);

    //アスキーコードを文字に変換
    String str = String(data);

    //文字がR(Red)だったらLEDをON/OFFさせる
    if(str=="R")
    {
      digitalWrite(LED_PIN,!digitalRead(LED_PIN));
    }
    
  }
}

こんな動作をします

このコードは、受信したデータが「R(Red)」であれば赤色LEDをオンオフさせるものです。
途中の次の部分でアスキーコードを文字に変換しています。

String str = String(data);

dataはあくまでアスキーコードの数字が入っているので、文字列に変換しないと上手く動かないので注意が必要です(*)。
(*) ちなみにprintln(data)とすると文字に変換されたものがシリアルモニタに表示されていましたね。
printlnはchar型変数を文字に変換する機能があるため、char型変数をstring型に変換しなくてもよかったんです。

以上がシリアル通信でパソコンから送られてきた情報をATtiny85で受信する方法と、その受信したデータをもとに電子パーツをアクションさせる方法になります。

今回はLEDをオンオフしましたが、同じ仕組みでモータなどを動かすと、パソコンで外部の機械を操作できるようになるので、超熱いです!!!

③シリアル信号で文字列を受ける方法

上記の①のサンプルコードには弱点があります。

このように一文字ではなく、複数の文字のデータを送信してみます。

↓はい。上手くいきません!!

これは、mySerial.read()で読み込むのは送信されたデータ(最大64バイト)のうち、最初の1バイトのみを読み込んでいるためです。
なので、最初の1バイトを読み込んだら、それをprintlnしてloopが一周し、そして2週目でHelloの「e」を処理しようとするのですが、改行のコードなどと干渉してバグってしまっています。

これを解決するためには、送信されたデータ全てをmySerial.read()で読み込んでから表示させればOKです。

次のサンプルコードがこの解決コードになります。
(マーキング部が加筆・修正した部分です)

#include <SoftwareSerial.h>
 
#define RX_PIN 4   // PB4ピン(3番ピン)USB-シリアル変換モジュールのTXに
#define TX_PIN 3   // PB3ピン(2番ピン)USB-シリアル変換モジュールのRXに
 
SoftwareSerial mySerial(RX_PIN, TX_PIN);    // RX,TXの割り当て

void setup() {
  // SoftwareSerialの初期化
  mySerial.begin(9600);
  mySerial.println("ATtiny Ready");

}

void loop() {
  if (mySerial.available()) {
    // データを受信し終わるまで、0.1秒待つ
    delay(100);
    
    // 受信バッファ内のすべてのデータを読み取る
    String data = "";
    while (mySerial.available()) {
      char myChar = mySerial.read();
      data += myChar;
    }

    // 受信したデータをシリアルモニタに表示する
    mySerial.print("Received: ");
    mySerial.println(data);
  }
}

見て分かる通り、データ一式が受信されるまで待つためのdelay(ここでは0.1秒)と、データを一通り処理するためのwhileループを設けたコードになります。

これを実行すると、上手く表示できました!!

これで文字列も処理できます!!ヨシ!!


今日はパソコンからシリアル通信をATtiny85が受信する方法を紹介しました。

今回のご紹介した方法を応用すると、パソコンから信号を送信して電子工作で作ったガジェットを動かせられるようになるので、すごく応用の幅が広がります!!

ではまた別の記事で会いましょう。
ではでは~

🦅バサバサ~

参考

シリアル通信の受信データのバッファ容量とread()の取り扱いについて

シリアル通信を受信すると、バッファに一時的に受信データが格納されますが、1回の受信データは64バイトまでです。

そして、read()を使うと、バッファに保存された最初の1バイト分を抽出します。この時、バッファから抽出したデータは削除されます。ここ重要です!!

read()を使うとバッファの中身のデータがどんどんなくなっていき、最終的にデータが全てなくなった段階で、if文+available()がfalseとなり、Loopの中でシリアル通信を受信した時の処理がされなくなります。

こういう仕組みなので、シリアル通信を一度受信したからと言って、if文+availableがずっとtrueになり続けるということにはなりません。