2023.02.09

Motion Gestures社 無償評価用SDKを用いて8×8 LEDシートをジェスチャー操作する方法

WEBマガジン非接触

  • TOP>
  • 特集>
  • Motion Gestures社 無償評価用SDKを用いて8×8 LEDシートをジェスチャー操作する方法
コーンテクノロジー
この記事の監修者
コーンズテクノロジー編集部
コーンズテクノロジーでは先進的な製品・技術を日本産業界へ紹介する技術専門商社として、通信計測・自動車・防衛セキュリティ・電子機器装置・航空宇宙・産業機械といった技術分野のお役立ち情報を紹介しています。

1. はじめに
2. システム構成および操作仕様
3. ジェスチャー操作仕様
4. 8×8 LEDシート
5. Arduino Nano Everyでの開発環境構築
6. Motion Gestures SDKサンプルプログラムの修正
7. Windows PCのシリアル通信プログラム
8. 8×8 LEDシート制御プログラム @ Arduino Nano Every
9. 本システムの回路
10. 動作確認

 


1. はじめに

当社WebマガジンにてMotion Gestures社の無償評価用SDKに関する記事を数多く投稿してきましたが、いずれもソフトウェアのみに特化した内容でした。
そこで今回、無償評価用SDKのジャスチャー結果を用いてハードウェア制御を題材として取り扱います。使用するハードウェアは組み立てが不要な8×8のLEDシートを選択しています。このLEDにはAdafruit社のNeoPixelを使用しています(詳細は後述)。

なお、Motion Gestures社の無償評価用SDKのサンプルプログラムのソースコードは、ソフトウェア使用許諾書による同意を頂いてからの開示となるため、Webマガジン上では公開不可となっております。ご了承下さい。よって、本記事ではSDKのサンプルプログラム以外を中心に記載します。

 


2. システム構成および操作仕様

今回のシステム構成を以下の図に示します。

 

今回のシステムでの各ブロックの処理内容は以下の通りです。
(b)から(d)まではWindows PC内での処理、(e)から(f)まではArduino Nano Every内での処理となっています。Arduino Nano Everyを選択した理由は後述します。

(a)ノートPC内蔵カメラやUSBカメラなどにより手の画像を取得
(b)(a)を基にMotion Gestures社のSDKにより各種ジェスチャーを認識
(c)(b)でのジェスチャー認識結果をAPI経由でサンプルプログラムで取得、特定のジェスチャーを文字列に変換し、標準出力
(d)(c)での標準出力をシリアル通信用プログラムに標準入力、標準入力した内容に合わせて制御データをシリアル送信
(e)(d)の制御データをLED制御用プログラムでシリアル受信し、LEDをNeoPixelライブラリのAPI経由で制御
(f)(e)でのAPI制御に従いNeoPixelライブラリでNeoPixel操作用のプロトコルに変換
(g)(e)のNeoPixelプロトコルに従い、8×8のLEDを点灯・消灯

 


3.ジェスチャー操作仕様

8×8 LEDシートの表示をジャスチャーで操作するにあたり、操作仕様を決める必要があります。今回のシステムでは以下のジェスチャー操作仕様および制御データ仕様を用いています。

◆左スワイプ(制御データ:”SERIAL_COMM:COLOR”)
LED色の切り替え。
(起動時)なし→白→赤→緑→青→黄色→シアン→マゼンタ→白→赤→…の順で切り替わります。
※なしは起動時だけ。

 

◆右スワイプ(制御データ:”SERIAL_COMM:MODE”)
表示モードを切り替え。
全面表示
→四角(描画)→四角(1点のみ)→四角(7点追尾)
→円(描画)→円(1点のみ)→円(7点追尾)
→三角(描画)→三角(1点のみ)→三角(7点追尾)
→十字(描画)→十字(1点のみ)→十字(7点追尾)
→シアター(点滅)→虹→虹+シアター
→文字(1文字ずつ表示)→文字(スクロール)
→全面表示
→…
の順で切り替わります。
虹と虹+シアター以外は左スワイプで切り替えた色が反映されます。
表示モードの詳細については後述します。

◆上スワイプ(制御データ:”SERIAL_COMM:UP”)
LEDの輝度を上げます(明るくします)。

◆下スワイプ(制御データ:”SERIAL_COMM:DOWN”)
LEDの輝度を下げます(暗くします)。ただし消えることはありません。

◆Thumbで上向き(制御データ:”SERIAL_COMM:FAST”)
各モード時の描画速度を速くします。

◆Thumbで下向き(制御データ:”SERIAL_COMM:SLOW”)
各モード時の描画速度を遅くします。

◆表示モード詳細
表示モードに関する詳細説明です。
・全面表示
8×8のすべてのLEDを同じ色・同じ輝度で点灯させます。

・四角(描画)
四角の描画パターンにてLEDを同じ色・同じ輝度で1点ずつ点灯させていきます。四角の描画パターンでのLEDをすべて点灯後、また同じ処理を繰り返します。
四角の描画パターン

四角(描画)の点灯手順

・四角(1点のみ)
四角の描画パターンで、LEDを同じ色・同じ輝度で1点のみ点灯させていきます。
四角(1点のみ)の点灯手順

・四角(7点追尾)
四角の描画パターンで、LEDを同じ色・同じ輝度で連続した7点のみ点灯させていきます。
四角(7点追尾)の点灯手順

・円(描画)
円の描画パターンにてLEDを同じ色・同じ輝度で1点ずつ点灯させていきます。円の描画パターンでのLEDをすべて点灯後、また同じ処理を繰り返します。
円の描画パターン

・円(1点のみ)
円の描画パターンで、LEDを同じ色・同じ輝度で1点のみ点灯させていきます。

・円(7点追尾)
円の描画パターンで、LEDを同じ色・同じ輝度で連続した7点のみ点灯させていきます。

・三角(描画)
三角の描画パターンにてLEDを同じ色・同じ輝度で1点ずつ点灯させていきます。三角の描画パターンでのLEDをすべて点灯後、また同じ処理を繰り返します。
三角の描画パターン

・三角(1点のみ)
三角の描画パターンで、LEDを同じ色・同じ輝度で1点のみ点灯させていきます。

・三角(7点追尾)
三角の描画パターンで、LEDを同じ色・同じ輝度で連続した7点のみ点灯させていきます。

・十字(描画)
十字の描画パターンにてLEDを同じ色・同じ輝度で1点ずつ点灯させていきます。十字の描画パターンでのLEDをすべて点灯後、また同じ処理を繰り返します。
十字の描画パターン

・十字(1点のみ)
十字の描画パターンで、LEDを同じ色・同じ輝度で1点のみ点灯させていきます。

・十字(7点追尾)
十字の描画パターンで、LEDを同じ色・同じ輝度で連続した7点のみ点灯させていきます。

・シアター(点滅)
「strandtest_nodelay」というサンプルプログラムのtheaterChase()関数の動作です。
動作については以下の動画を参照ください。

 

・虹
「strandtest_nodelay」サンプルプログラムのrainbow()関数の動作です。
動作については以下の動画を参照ください。

 

・虹+シアター
「strandtest_nodelay」サンプルプログラムのtheaterChaseRainbow()関数の動作です。
動作については以下の動画を参照ください。

 

・文字(1文字ずつ表示)
まず「ありがとうございます。」を下記8×8のピクセルデータとして準備します。


そして、一定期間LEDを同じ色・同じ輝度で1文字表示した後、1文字ずつ切り替えます。

・文字(スクロール)
LEDを同じ色・同じ輝度で表示したのち、1列ずつ文字をスクロールさせます。

 


4. 8×8 LEDシート

今回使用した8×8 LEDシートに関して説明します。

マルツ様の『LEDシート 8X8 RGB色【EM-LEDS-8X8-RGB】』という製品を購入しています。

この製品の詳細説明は、同じくマルツ様のサイトにありますので、こちらを参照ください。

このLEDシートではWorldsemi社のWS2812Bというシリアル信号で制御可能なLEDが8行x8列、合計で64個搭載されています。64個のLEDをそれぞれ別々に制御することにより、様々なパターンを表示することができます。

WSB2812Bのデータシートは以下のURLよりダウンロード可能です。
http://www.world-semi.com/Certifications/details-141-4.html

シリアル信号は以下の仕様で1周期1.25μsec(800kHz)のHighとLowの期間を変えて0と1を判別しています。また50μsec以上Lowにすることで、Resetすることができます。

1つのLEDはRGBそれぞれ8bit(256階調)の計24bitのシリアルデータで制御されます。

またこのLEDはシリアル信号用にデータ入力端子(DIN)とデータ出力端子(DOUT)をもっており、複数のLEDを数珠つなぎで接続できます(カスケード接続)。

このLEDは、シリアルデータを受信すると、先頭24bitをそのLEDに対する設定データとして使用し、また後段のLEDに送信しないように削除します。下記の例では、1st LEDは1st Data(24bit)で設定しこれを削除、2nd LEDで2nd Data(24bit)で設定しこれを削除、3rd LEDで3rd Data(24bit)で設定しこれを削除、…という処理になります。

ちなみに、このLED製品はNeoPixelというアメリカのAdafruit社のLED製品のブランド名として有名ですが、開発は中国のWorldSemiが行っています。

本記事ではこれ以降、シリアル信号で制御可能なLEDをNeoPixelと称して説明します。

今回使用するLEDシートですが、0~63の順番で接続されています。本記事では0~63をインデックスとし、この並び順を物理インデックス(Pysical Index)と記載します。

物理インデックスのままで制御プログラムを書くと管理が面倒です。よって制御時には下記の論理インデックス(Logical Index)を用いてプログラムを記載し、シリアルデータ送信部にて論理インデックスから物理インデックスへ変換すると、管理が楽になります。

 


5. Arduino Nano Everyでの開発環境構築

NeoPixelを搭載した8×8 LEDシートをWindows PCから直接制御できれば早いのですが、Windows用のNeoPixel制御ライブラリが存在しないため(実際は存在しており探し切れていないだけかもしれませんが)、何らかの方法で別途Neopixelを制御する必要があります。

Adafruit社のWebsiteには「Adafruit NeoPixel Überguide」という開発者向けのNeoPixelユーザーガイドがあります。

ここに「Software」および「Python & CircuitPython」という記事が含まれており、前者はArduino用のNeoPixelライブラリ後者はPython用のNeoPixelライブラリの使用方法が記載されています。

前者の方がライブラリ中にいろいろとサンプルプログラムが含まれており、またNeoPixel向けプログラムに関してインターネット上で検索すると、PythonよりもArduinoの情報が多いため、今回のシステムではArduinoを使用することにします。

またArduinoと言っても、いろいろと種類があり、選択に困ります。
※Arduinoの種類についてはマイコン技術Navi様の「【Arduinoの選び方】UNO・Nano Every・Micro・Due・MEGAは何が違うのか?」に詳しく記載されているので参照ください。

今回は
・サイズが小さい
・安価
・Bluetoothは不要
などを条件にし、「Arduino Nano Every」を使うことにしました。

Arduinoを用いてソフトウェア開発を行う場合、Arduio IDEを使うと簡単に環境構築できます。

Arduino IDEのダウンロード・インストール・設定・使用方法などについては、マルツ様の「いろいろなマイコンボードに使えるArduino IDE 環境構築メモ」やDEVICE PLUS様の「はじめての電子工作! Arduinoを触ってみよう(ソフトウェア編)」に詳しく記載がありますので、これらをご参照下さい。

また、Arduino Nano EveryをArduino IDEで使う場合には、ボードマネージャーにてArduino megaAVR Boardsというボード設定をインストールする必要があります。詳しくは、meyon’s STUDY様の「Arduino Nano Every を使ってみる – IDE 設定」や中野島ロボット様の「週刊中ロボ74 Arduino Nano Every 到着 その5開発環境のインストール」に記載がありますので、これらをご参照下さい。

Arduino IDEとArduino megaAVR Boardsのインストール後に、先に説明しましたArduino用のNeoPixelライブラリをインストールします。このライブラリのインストールについては、NOBのArduino日記!様の「Arduino IDE(Adafruit NeoPixelライブラリのインストール)」に詳しい記載がありますので、こちらをご参照下さい。

 


6. Motion Gestures SDKサンプルプログラムの修正

まず、SDK付属のサンプルプログラムですが、Motion Gestures社のSDKに関するソフトウェア使用許諾書による同意を頂いてからの開示となるため、Webマガジン上では公開不可となっております。ご了承下さい。よって修正内容の概要説明のみとなります。

今回のシステムでは、Windows PC上で動作するSDKサンプルプログラムのジェスチャー認識部に下記の修正を追加しています。

  if(動的ジェスチャーが有効){
    auto temp = 動的ジェスチャー名を取得するAPI;
    if(tempに上スワイプを示す文字が含まれている場合){
      std::cout << "SERIAL_COMM:UP" << std::endl;
    }
    else if(tempに下スワイプを示す文字が含まれている場合){
      std::cout << "SERIAL_COMM:DOWN" << std::endl;
    }
    else if(tempに左スワイプを示す文字が含まれている場合){
      std::cout << "SERIAL_COMM:COLOR" << std::endl;
    }
    else if(tempに右スワイプを示す文字が含まれている場合){
      std::cout << "SERIAL_COMM:MODE" << std::endl;
    }
  }

  if(静的ジェスチャーが有効){
    auto temp = 静的ジェスチャー名を取得するAPI;
    if(tempにThumbを示す文字が含まれている場合){
      if(静的kジャスチャーの向きを取得するAPI == 上方向){
        std::cout << "SERIAL_COMM:FAST" << std::endl;
      }
      else if(静的kジャスチャーの向きを取得するAPI == 下方向){
        std::cout << "SERIAL_COMM:SLOW" << std::endl;
      }
    }
  }

なお、実際はサンプルプログラムに上記以外の修正も加えておりますが、ソースコードを表示して説明する必要があるため、今回の説明では省略しています。

 


7. Windows PCのシリアル通信プログラム

サンプルプログラムの標準出力結果を用いて、Windows PCからArduinoへ制御データとしてシリアル通信を行います。

今回シリアル通信用プログラムには、記述が簡単なPythonを用いています。

以下、シリアル通信部のソースコード例を添付します。説明用のコメントをソースコード中の「#★」の後ろに追記してあります。

import serial
import sys

if len(sys.argv) < 3: #★ 引数が3未満ならばUsage表示して終了
  print('serial_send.py COMPORT BAUDRATE')
  print('COPMPORT : COMx (e.x. COM5, COM6, COM7, ...) ')
  print('BAUDRATE : 9600, 19200, 28400, 57600, 115200, ...')
  exit()
else: #★引数が3以上時の処理 com_port = sys.argv[1] #★ COMポート設定取得
  baud_rate = int(sys.argv[2]) #★ ボーレート設定取得
  print('com_port = {0}, baud_rate = {1}'.format(com_port, baud_rate)) #★ 念のため設定内容表示

try: #★ シリアルポートオープン
  ser = serial.Serial(com_port, baud_rate, timeout=1) #★ serとしてオープン、タイムアウトは1sec
  print(ser.name) #★ 念のためシリアルポート名表示
except (OSError, serial.SerialException): #★ シリアルポートオープン失敗時の処理
  print('Serial port {0} open error!'.format(com_port)) #★ Errorメッセージ表示
  exit() #★ 終了

while True: #★ 以下繰り返し
  print('Gesture Waiting...') #★ 標準入力待ちの表示
  send_buf = input() #★ 標準入力待ち-> send_bufに標準入力をセット

  if 'SERIAL_COMM:' in send_buf: #★ 標準入力中に'SERIAL_COMM'があれば(ジェスチャー操作であれば)
    str = send_buf + '\r\n' #★改行コード追加
    ser.write(str.encode()) #★ 標準入力の内容をシリアル送信
    print('Sent Gesture:{0}'.format(send_buf)) #★ 念のため送信した内容を表示
  else: #★ ジェスチャー操作以外時の処理
    print(send_buf) #★ 単に標準入力の内容を表示

ser.close() #★ While文抜けた後でシリアルポートをクローズ

上記プログラムにてシリアル通信を行うにあたり、以下の準備が必要になります。

・Windows PCにPythonがインストールされていなければ、インストールして下さい。
※インストールに関してはPython.jp様の「Windows版Pythonのインストール」を参照願います。

・コマンドプロンプトまたはPowerShellにて、以下のコマンド実行し、pyserialパッケージをインストールして下さい。

pip install pyserial

 


8. 8×8 LEDシート制御プログラム @ Arduino Nano Every

今まで説明した仕様を踏まえて、Arduino Nano Everyを用いて8×8 LEDシートを制御するプログラムを作成します。

NeopixcelのライブラリをArduino IDEにインストールするといろいろサンプルプログラムが含まれており、これらを基に修正を加えて使用するのが確実な方法です。

今回は「strandtest_nodelay」というサンプルプログラムにいろいろと機能追加して使用しています。また文字表示系の処理については、スクラッチ&スクラップ様の「Arduino と NeoPixel で作る ミニ電光掲示板」を参照しております。

以下、ソースコード例を添付します。説明用のコメントをソースコード中の「//★」の後ろに追記してあります。

//★NeoPixelライブラリのヘッダーファイル読み込み
#include 
#ifdef __AVR__
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Digital IO pin connected to the button. This will be driven with a
// pull-up resistor so the switch pulls the pin to ground momentarily.
// On a high -> low transition the button press logic will execute.
//★Arduino Nano Everyで何番ピンをNeoPixel制御に使いうかの設定、今回は1番ピンを使用
#define LED_PIN    1  // Digital IO pin connected to the NeoPixels.

//★LEDシートの幅&高さ設定
#define LED_W 8 //Nember of NeoPixels @ Width
#define LED_H 8 //Nember of NeoPixels @ Height

//★LEDの数量設定
#define LED_COUNT (LED_W * LED_H)  // Number of NeoPixels

//★文字用配列の幅設定、1文字幅8画素(=LEDシート幅)、11文字なので8*11
#define STR_W (LED_W * 11)  // Number of Strings pixel @ Width
//★文字用配列の高さ設定、1文字高さ8画素(=LEDシート高さ)
#define STR_H LED_H         // Number of Strings pixel @ Height

//★"ありがとうございます。"を示す文字配列、点灯箇所を1にセット
boolean pix_str[STR_H][STR_W] = {
  {0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
  {0,1,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0},
  {0,0,1,0,0,0,0,0,0,1,1,0,0,1,0,0,1,1,1,1,0,1,0,0,0,0,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0},
  {0,0,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0},
  {0,1,1,0,1,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0},
  {1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0},
  {0,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};

// Declare our NeoPixel strip object:
//★NeoPixcelをstripとして宣言
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)

//★表示モード用変数を初期化
int mode     = 0;
//★色用変数を初期化
int color = 0;

//★LED輝度用変数に15をセット
int pixel_val = 15;

//★wait時間補正用変数に1をセット
float wait_correction = 1.0;


//★前回処理時刻用変数を初期化
unsigned long pixelPrevious = 0;
//★LED処理間隔用変数に50[msec]をセット
int           pixelInterval = 50;

//★画素Queue用変数を初期化(シアター・文字表示時に使用)
int           pixelQueue = 0;
//★画素Cycle用変数を初期化(raibow時に使用)
int           pixelCycle = 0;
//★表示順変数を初期化
uint16_t      pixelCurrent = 0;
//★総画素数用変数に総画素数(=総LED数)をセット
uint16_t      pixelNumber = LED_COUNT;
//★画素Chase配列を初期化(7点追尾時に使用)
uint16_t      pixelChase[7] = {0};

//★四角用の論理インデックス表示順配列の設定
int square_logidx[] = {0,1,2,3,4,5,6,7,15,23,31,39,47,55,63,62,61,60,59,58,57,56,48,40,32,24,16,8};
//★円用の論理インデックス表示順配列の設定
int circle_logidx[] = {4,13,14,22,31,39,46,54,53,60,59,50,49,41,32,24,17,9,10,3};
//★三角用の論理インデックス表示順配列の設定
int triangle_logidx[] = {4,12,20,21,29,37,38,46,54,55,63,62,61,60,59,58,57,56,48,49,41,33,34,26,18,19,11,3};
//★十字用の論理インデックス表示順配列の設定
int cross_logidx[] = {4,12,20,28,29,30,31,39,38,37,36,44,52,60,59,51,43,35,34,33,32,24,25,26,27,19,11,3};

//★setup関数
//★Arduino Nano Every起動時に一回だけ実行される
void setup() {
  Serial.begin(9600); //★シリアル通信のボーレートを9600にセット
  while (!Serial); //★初期化完了するまでwait

  strip.begin(); // Initialize NeoPixel strip object (REQUIRED) //★strip(NeoPixel)の起動
  strip.show();  // Initialize all pixels to 'off' //★念のためshow実行、ただしすべてoffのまま
}

//★loop関数
//★通常処理はloop関数内に記載
void loop() {

  //★比較用文字変数に"SERIAL_COMM:"をセット
  String comp_str = "SERIAL_COMM:";
  //★受信文字列格納用変数
  String receive_str;
  //★抽出文字格納用変数
  String sub_str;

  //★R・G・B各輝度用変数
  uint8_t r_val;
  uint8_t g_val;
  uint8_t b_val;
  
  //★MAX輝度用変数
  uint8_t max_val;

  //★wait時間用変数
  unsigned int wait_val;
  
  //★現時刻取得
  unsigned long currentMillis = millis();


  //★シリアル通信で何らかの受信があれば
  if(Serial.available()){
    receive_str = Serial.readString(); //★受信データをreceive_strにセット
    //★receive_str先頭とcomp_strが一致であれば
    if(receive_str.startsWith(comp_str)){
      sub_str = receive_str.substring(comp_str.length()); //★sub_strにcomp_str以降の文字列をセット
    }
    //★receive_str先頭とcomp_strが一致しなければ(LED制御用データでなければ)
    else {
      sub_str = ""; //★sub_strを初期化
    }
    //★以下シリアルデータ受信後の処理
    Serial.print("Receive Strings:");
    Serial.println(receive_str); //★受信した文字列をすべてコンソールに表示
    Serial.print("Sub Strings:");
    Serial.println(sub_str); //★sub_strの文字列をコンソールに表示
    //★受信した文字の先頭が"SERIAL_COMM:"であれば
    if(receive_str.startsWith(comp_str)) {
      //★以下、sub_strの内容に従って処理
      //★"STOP"時の処理→停止なので各変数を初期化
      if(sub_str.startsWith("STOP")){
        mode = 0; //★表示モード用変数を初期化
        color = 0; //★色用変数を初期化
        pixel_val = 15; //★LED輝度用変数に15をセット
        wait_correction = 1.0; //★wait時間補正用変数に1をセット
      }

      //★"UP"時の処理→LEDの輝度を上げるための変数調整
      if(sub_str.startsWith("UP")){
        //★色が黒以外かつ表示モードが虹系以外であれば
        if (color != 0 && mode !=14 && mode !=15){
          pixel_val += 16; //★LEDの輝度を+16
          if(pixel_val > 255) pixel_val = 255; //★輝度が255超であれば、255をセット
        }
      }

      //★"DOWN"時の処理→LEDの輝度を下げるための変数調整
      if(sub_str.startsWith("DOWN")){
        //★色が黒以外かつ表示モードが虹系以外であれば
        if (color != 0 && mode !=14 && mode !=15){
          pixel_val -= 16;  //★LEDの輝度を-16
          if(pixel_val < 0) pixel_val = 15; //★輝度がマイナス値であれば、15をセット
        }
      }

      //★"COLOR"時の処理→LEDの色を変更するための変数調整
      if(sub_str.startsWith("COLOR")){
        color++; //★色用変数をインクリメント
        if(color > 7) color = 1; //★最後の色を超えたら1(白)に戻す(0(黒)ではない)
        pixelCurrent = 0; //★表示順変数を初期化
        pixelQueue = 0; //★画素Queue用変数を初期化
      }

      //★"MODE"時の処理→表示モードを変更するための変数調整
      if(sub_str.startsWith("MODE")){
        //★色が黒以外であれば
        if(color != 0) {
          mode++; //★表示モード用変数をインクリメント
          if(mode > 17) mode = 0; //★最後の表示モードを超えたら0に戻す
          pixelCurrent = 0; //★表示順変数を初期化
          pixelQueue = 0; //★画素Queue用変数を初期化
          //★画素Chase配列を初期化
          int temp_size = sizeof(pixelChase) / sizeof(uint16_t);
          for(int i = 0; i < temp_size; i++){
            pixelChase[i] = 0;
          }
          strip.clear(); //★表示モード変更であれば一度strip(NeoPixel)を初期化
          strip.setBrightness(255); //★ブライトネスを最大値に戻す ※ブライトネス調整していると必要
        }
      }

      //★"FAST"時の処理→描画速度を速くするための変数調整
      if(sub_str.startsWith("FAST")){
        //★色が黒以外かつ全面表示以外であれば
        if(color != 0 && mode != 0){
          //★wait時間補正用変数が1超であれば
          if(wait_correction > 1.0){
            wait_correction -= 1.0; //★wait時間補正用変数を-1する
          }
          //★wait時間補正用変数が1以下であれば
          else {
            wait_correction /= 2.0;  //★wait時間補正用変数を半分にする
          }
          if(wait_correction < 0.125) wait_correction = 0.125; //★wait時間補正用変数が0.125未満であれば、0.125をセット
        }
      }

      //★"SLOW"時の処理→描画速度を遅くするための変数調整
      if(sub_str.startsWith("SLOW")){
        //★色が黒以外かつ全面表示以外であれば
        if(color != 0 && mode != 0){
          //★wait時間補正用変数が1超であれば
          if(wait_correction > 1.0){
            wait_correction += 1.0;  //★wait時間補正用変数を+1する
          }
          //★wait時間補正用変数が1以下であれば
          else {
            wait_correction *= 2.0; //★wait時間補正用変数を倍にする
          }
          if(wait_correction > 5.0) wait_correction = 5.0; //★wait時間補正用変数が5超であれば、5をセット
        }
      }

      //★色用変数にあわせてLEDの色設定→R・G・B各輝度用変数の値をセット
      switch(color){
        case 0: //★0であれば黒(消灯)→RGBすべてに0をセット
          r_val = 0; g_val = 0; b_val = 0;
          break;
        case 1: //★1であれば白→RGBすべてにpixel_valをセット
          r_val = pixel_val; g_val = pixel_val; b_val = pixel_val;
          break;
        case 2: //★2であれば赤→Rのみpixel_val、GとBは0をセット
          r_val = pixel_val; g_val = 0; b_val = 0;
          break;
        case 3: //★3であれば緑→Gのみpixel_val、RとBは0をセット
          r_val = 0; g_val = pixel_val; b_val = 0;
          break;
        case 4: //★4であれば青→Bのみpixel_val、RとGは0をセット
          r_val = 0; g_val = 0; b_val = pixel_val;
          break;
        case 5: //★5であれば黄色→RとGはpixel_val、Bのみ0をセット
          r_val = pixel_val; g_val = pixel_val; b_val = 0;
          break;
        case 6: //★6であればシアン→GとBはpixel_val、Rのみ0をセット
          r_val = 0; g_val = pixel_val; b_val = pixel_val;
          break;
        case 7: //★7であればマゼンタ→RとBはpixel_val、Gのみ0をセット
          r_val = pixel_val; g_val = 0; b_val = pixel_val;
          break;
      }

      //★RGBのうちの最大値をMAX輝度用変数にセット
      max_val = max(r_val, g_val);
      max_val = max(max_val, b_val); 
    }
  }


  //★現時刻-前回処理時刻が設定したLED処理間隔を超えた場合に表示処理
  if(currentMillis - pixelPrevious >= pixelInterval) {        //  Check for expired time
    //★前回処理時刻用変数に現時刻をセット
    pixelPrevious = currentMillis;                            //  Run current frame

    //★NeoPixcel色設定用変数(32bit)にRGBの値をセット
    uint32_t main_color = strip.Color(r_val, g_val, b_val);
    int temp_size;

    //★表示用モード変数に合わせて表示用関数&引数設定
    switch(mode) {           // Start the new animation...
      case 0: //★0であれば全面表示
        wait_val = (int)(10.0 * wait_correction); //★基準wait時間10msecに補正をかけて
        colorWipe(main_color, wait_val); //★色・wait時間設定して、colorWipe関数を実行
        break;
      case 1: //★1であれば四角(描画)
        wait_val = (int)(100.0 * wait_correction); //★基準wait時間100msecに補正をかけて
        temp_size = sizeof(square_logidx) / sizeof(int); //★square_logidx配列のサイズを求める
        colorDraw(square_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorDraw関数を実行
        break;
      case 2: //★2であれば四角(1点のみ)
        wait_val = (int)(20.0 * wait_correction); //★基準wait時間20msecに補正をかけて
        temp_size = sizeof(square_logidx) / sizeof(int); //★square_logidx配列のサイズを求める
        colorDot(square_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorDot関数を実行
        break;
      case 3: //★3であれば四角(7点追尾)
        wait_val = (int)(50.0 * wait_correction); //★基準wait時間50msecに補正をかけて
        temp_size = sizeof(square_logidx) / sizeof(int); //★square_logidx配列のサイズを求める
        colorChase(square_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorChase関数を実行
        break;
      case 4: //★4であれば円(描画)
        wait_val = (int)(100.0 * wait_correction); //★基準wait時間100msecに補正をかけて
        temp_size = sizeof(circle_logidx) / sizeof(int); //★circle_logidx配列のサイズを求める
        colorDraw(circle_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorDraw関数を実行
        break;
      case 5: //★5であれば円(1点のみ)
        wait_val = (int)(20.0 * wait_correction); //★基準wait時間20msecに補正をかけて
        temp_size = sizeof(circle_logidx) / sizeof(int); //★circle_logidx配列のサイズを求める
        colorDot(circle_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorDot関数を実行
        break;
      case 6: //★6であれば円(7点追尾)
        wait_val = (int)(50.0 * wait_correction); //★基準wait時間50msecに補正をかけて
        temp_size = sizeof(circle_logidx) / sizeof(int); //★circle_logidx配列のサイズを求める
        colorChase(circle_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorChase関数を実行
        break;
      case 7: //★7であれば三角(描画)
        wait_val = (int)(100.0 * wait_correction); //★基準wait時間100msecに補正をかけて
        temp_size = sizeof(triangle_logidx) / sizeof(int); //★triangle_logidx配列のサイズを求める
        colorDraw(triangle_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorDraw関数を実行
        break;
      case 8: //★8であれば三角(1点のみ)
        wait_val = (int)(20.0 * wait_correction); //★基準wait時間20msecに補正をかけて
        temp_size = sizeof(triangle_logidx) / sizeof(int); //★triangle_logidx配列のサイズを求める
        colorDot(triangle_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorDot関数を実行
        break;
      case 9: //★9であれば三角(7点追尾)
        wait_val = (int)(50.0 * wait_correction); //★基準wait時間50msecに補正をかけて
        temp_size = sizeof(triangle_logidx) / sizeof(int); //★triangle_logidx配列のサイズを求める
        colorChase(triangle_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorChase関数を実行
        break;
      case 10: //★10であれば十字(描画)
        wait_val = (int)(100.0 * wait_correction); //★基準wait時間100msecに補正をかけて
        temp_size = sizeof(cross_logidx) / sizeof(int); //★cross_logidx配列のサイズを求める
        colorDraw(cross_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorDraw関数を実行
        break;
      case 11: //★11であれば十字(1点のみ)
        wait_val = (int)(20.0 * wait_correction); //★基準wait時間20msecに補正をかけて
        temp_size = sizeof(cross_logidx) / sizeof(int); //★cross_logidx配列のサイズを求める
        colorDot(cross_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorDot関数を実行
        break;
      case 12: //★12であれば十字(7点追尾)
        wait_val = (int)(50.0 * wait_correction); //★基準wait時間50msecに補正をかけて
        temp_size = sizeof(cross_logidx) / sizeof(int); //★cross_logidx配列のサイズを求める
        colorChase(cross_logidx, temp_size, main_color, wait_val); //★配列・サイズ・色・wait時間設定してcolorChase関数を実行
        break;
      case 13:
        wait_val = (int)(50.0 * wait_correction); //★基準wait時間50msecに補正をかけて
        theaterChase(main_color, wait_val); //★色・wait時間設定してtheaterChase関数を実行
        break;
      case 14:
        wait_val = (int)(50.0 * wait_correction); //★基準wait時間50msecに補正をかけて
        rainbow(max_val, wait_val); //★輝度・wait時間設定してrainbow関数を実行
        break;
      case 15:
        wait_val = (int)(50.0 * wait_correction); //★基準wait時間50msecに補正をかけて
        theaterChaseRainbow(max_val, wait_val); //★輝度・wait時間設定してtheaterChaseRainbow関数を実行
        break;
      case 16:
        wait_val = (int)(500.0 * wait_correction); //★基準wait時間500msecに補正をかけて
        strDisp(main_color, wait_val); //★色・wait時間設定してstrDisp関数を実行
        break;
      case 17:
        wait_val = (int)(100.0 * wait_correction); //★基準wait時間100msecに補正をかけて
        strScroll(main_color, wait_val); //★色・wait時間設定してstrScroll関数を実行
        break;
    }
  }
}

//★logidxは論理インデックス
//logidx(logical index)
// 0,  1,  2,  3,  4,  5,  6,  7,
// 8,  9, 10, 11, 12, 13, 14, 15,
//16, 17, 18, 19, 20, 21, 22, 23,
//24, 25, 26, 27, 28, 29, 30, 31,
//32, 33, 34, 35, 36, 37, 38, 39,
//40, 41, 42, 43, 44, 45, 46, 47,
//48, 49, 50, 51, 52, 53, 54, 55,
//56, 57, 58, 59, 60, 61, 62, 63,
//★phyidxは(LEDシートの)物理インデックス
//phyidx(physical index, 8x8 NeoPixel index)
// 7,  6,  5,  4,  3,  2,  1,  0,
// 8,  9, 10, 11, 12, 13, 14, 15,
//23, 22, 21, 20, 19, 18, 17, 16,
//24, 25, 26, 27, 28, 29, 30, 31,
//39, 38, 37, 36, 35, 34, 33, 32,
//40, 41, 42, 43, 44, 45, 46, 47,
//55, 54, 53, 52, 51, 50, 49, 48,
//56, 57, 58, 59, 60, 61, 62, 63,
//★論理インデックス→物理インデックス変換関数、引数は論理インデックス
int logidx2phyidx(int logidx){
  int phyidx;
  //★論理インデックスの下位4itの値が8以上の場合(8~15)
  if((logidx & 0xf) >= 8){
    phyidx = logidx; //★物理インデックスは論理インデックスと同じ
  }
  //★論理インデックスの下位4itの値が8未満の場合(0~7)
  else {
    phyidx = (logidx & 0xfffffff0) | (~logidx & 0x7); //★下位3bitのみbit反転(0->7,1->6,…,7->0)
  }
  return(phyidx); //★物理インデックスを返す
}

//★全面表示用関数、引数は色・wait時間
void colorWipe(uint32_t color, int wait) {
  //★pixelInterval(LED処理間隔)とwait時間が異なる場合、pixelIntervalにwait時間をセット
  if(pixelInterval != wait)
    pixelInterval = wait;                   //  Update delay time
  strip.setPixelColor(pixelCurrent, color); //  Set pixel's color (in RAM) //★表示順変数の示すLEDの色設定
  strip.show();                             //  Update strip to match //★点灯
  pixelCurrent++;                           //  Advance current pixel //★表示順変数をインクリメント
  //★最終LEDまで行ったら表示順変数を0クリア(先頭LEDに戻す)
  if(pixelCurrent >= pixelNumber)           //  Loop the pattern from the first LED
    pixelCurrent = 0;
}

//★描画用関数、引数は配列・サイズ・色・wait時間
void colorDraw(int draw_logidx[], int draw_size, uint32_t main_color, int wait) {
  //★pixelInterval(LED処理間隔)とwait時間が異なる場合、pixelIntervalにwait時間をセット
  if(pixelInterval != wait)
    pixelInterval = wait;
  //★表示順変数が0であれば、すべてのLEDをOFFにする
  if(pixelCurrent == 0){
    strip.clear();
  }
  //★表示順変数を用いてdraw_logidx配列から論理インデックス取得->物理インデックスに変換してその位置のLEDの色設定
  strip.setPixelColor(logidx2phyidx(draw_logidx[pixelCurrent]), main_color);
  strip.show(); //★点灯
  pixelCurrent++; //★表示順変数インクリメント
  //★表示順変数が最後(サイズ以上)であれば、表示順変数を0クリア
  if(pixelCurrent >= draw_size)
    pixelCurrent = 0;
}

//★1点表示用関数、引数は配列・サイズ・色・wait時間
void colorDot(int draw_logidx[], int draw_size, uint32_t main_color, int wait) {
  //★pixelInterval(LED処理間隔)とwait時間が異なる場合、pixelIntervalにwait時間をセット
  if(pixelInterval != wait)
    pixelInterval = wait;
  strip.clear(); //★すべてのLEDをOFF
  //★表示順変数を用いてdraw_logidx配列から論理インデックス取得->物理インデックスに変換してその位置のLEDの色設定
  strip.setPixelColor(logidx2phyidx(draw_logidx[pixelCurrent]), main_color);
  strip.show(); //★点灯
  pixelCurrent++; //★表示順変数インクリメント
  //★表示順変数が最後(サイズ以上)であれば、表示順変数を0クリア
  if(pixelCurrent >= draw_size)
    pixelCurrent = 0;
}

//★7点追尾用関数、引数は配列・サイズ・色・wait時間
void colorChase(int draw_logidx[], int draw_size, uint32_t main_color, int wait) {
  //★pixelInterval(LED処理間隔)とwait時間が異なる場合、pixelIntervalにwait時間をセット
  if(pixelInterval != wait)
    pixelInterval = wait;
  //★表示順変数を用いてdraw_logidx配列から論理インデックス取得->物理インデックスに変換してその位置のLEDの色設定
  strip.setPixelColor(logidx2phyidx(draw_logidx[pixelCurrent]), main_color);
  //★画素Chase配列のサイズを取得
  int temp_size = sizeof(pixelChase) / sizeof(uint16_t);
  //★画素Chase配列から最も古い表示順変数取得->この値を用いてdraw_logidx配列から論理インデックス取得->物理インデックスに変換してその位置のLEDを黒(消灯)に
  strip.setPixelColor(logidx2phyidx(draw_logidx[pixelChase[temp_size-1]]), strip.Color(0, 0, 0));
  strip.show(); //★点灯
  //★画素Chase配列のデータシフト
  for(int i = temp_size-1; i >0; i--){
    pixelChase[i] = pixelChase[i-1];
  }
  pixelChase[0] = pixelCurrent; //★画素Chase配列の[0]に現表示順変数の値をセット
  pixelCurrent++; //★表示順変数インクリメント
  //★表示順変数が最後(サイズ以上)であれば、表示順変数を0クリア
  if(pixelCurrent >= draw_size)
    pixelCurrent = 0;
}

//★シアター用関数、基の記述から変更ないため説明省略
void theaterChase(uint32_t color, int wait) {
  if(pixelInterval != wait)
    pixelInterval = wait;                   //  Update delay time
  for(int i = 0; i < pixelNumber; i=i+3) {
    strip.setPixelColor(i + pixelQueue, color); //  Set pixel's color (in RAM)
  }
  strip.show();                             //  Update strip to match
  for(int i=0; i < pixelNumber; i=i+3) {
    strip.setPixelColor(i + pixelQueue, strip.Color(0, 0, 0)); // Set pixel's color (in RAM)
  }
  pixelQueue++; // Advance current pixel if(pixelQueue >= 3)
  pixelQueue = 0;                         //  Loop the pattern from the first LED
}


//★虹用関数、追記コメント箇所以外は基の記述から変更なし
//★引数に輝度を追加
void rainbow(int bright_val, uint8_t wait) {
  if(pixelInterval != wait)
    pixelInterval = wait;                   
  for(uint16_t i=0; i < pixelNumber; i++) {
    strip.setPixelColor(i, Wheel((i + pixelCycle) & 255)); // Update delay time
  }
  strip.setBrightness(bright_val); //★輝度を設定
  strip.show(); // Update strip to match
  pixelCycle++; // Advance current cycle
  if(pixelCycle >= 256)
    pixelCycle = 0;                         //  Loop the cycle back to the begining
}


//★虹+シアター用関数、追記コメント箇所以外は基の記述から変更なし
//★引数に輝度を追加
void theaterChaseRainbow(int bright_val, uint8_t wait) {
  //★pixelInterval(LED処理間隔)とwait時間が異なる場合、pixelIntervalにwait時間をセット
  if(pixelInterval != wait)
    pixelInterval = wait;                   //  Update delay time  
  for(int i=0; i < pixelNumber; i=i+3) {
    strip.setPixelColor(i + pixelQueue, Wheel((i + pixelCycle) % 255));  
  }
  strip.setBrightness(bright_val); //★輝度を設定
  strip.show();
  for(int i=0; i < pixelNumber; i=i+3) {
    strip.setPixelColor(i + pixelQueue, strip.Color(0, 0, 0));
  }
  pixelQueue++; // Advance current queue
  pixelCycle++; // Advance current cycle
  if(pixelQueue >= 3)
    pixelQueue = 0;                       //  Loop
  if(pixelCycle >= 256)
    pixelCycle = 0;                       //  Loop
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
//★虹系関数内で使用するWheel関数、基の記述から変更ないため説明省略
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

//★文字スクロール用関数、引数は色・wait時間
//★画素Queue用変数は、文字用配列にて表示開始する幅側(横方向)の開始位置に使用
void strScroll(uint32_t main_color, int wait) {
  int sw = STR_W; //表示内容の幅ピクセル
  int dh = LED_H; //ディスプレーの高さピクセル
  int dw = LED_W;  // ディスプレーの幅ピクセル
  uint32_t temp_color;

  //★pixelInterval(LED処理間隔)とwait時間が異なる場合、pixelIntervalにwait時間をセット
  if(pixelInterval != wait)
    pixelInterval = wait;

  for (int h = 0; h < dh; h++) {  // 上の行から
    for (int w = 0; w < dw; w++) {  // 左の列から
      if (((pixelQueue + w) < dw) || ((pixelQueue + w) >= (dw + sw))) { // 表示範囲外
        continue; //★1文字分そろっていないor表示完了なので、次の列に進む
      }
      else if (pix_str[h][w - dw + pixelQueue] == 1) { // 表示位置の データが 1 のとき
        temp_color = main_color; //★色設定
      }
      else {  // 表示位置のデータが 0 のとき
        temp_color = strip.Color(0, 0, 0); //★黒(表示 OFF)
      }
      //★行・列から論理インデックスを求める->物理インデックスに変換してその位置のLEDの色設定
      strip.setPixelColor(logidx2phyidx(h * dw + w), temp_color);
    }
  }
  strip.show(); //★点灯
  pixelQueue++; //  Advance current pixel //★画素Queue用変数をインクリメント(文字用配列の列の位置を一つ右に移動)
  //★画素Queue用変数が最後(文字用配列の幅+LEDシートの幅の値以上)であれば、画素Queue用変数を0クリア
  if(pixelQueue >= (sw + dw))
    pixelQueue = 0;                         //  Loop the pattern from the first LED
}

//★1文字表示用関数、引数は色・wait時間
//★追記コメント部以外、文字スクロール用関数と同じ
void strDisp(uint32_t main_color, int wait) {
  int sw = STR_W;  // 表示内容の幅ピクセル
  int dh = LED_H;   // ディスプレーの高さピクセル
  int dw = LED_W;  // ディスプレーの幅ピクセル
  uint32_t temp_color;

  if(pixelInterval != wait)
    pixelInterval = wait;

  for (int h = 0; h < dh; h++) {  // 上の行から
    for (int w = 0; w < dw; w++) {  // 左の列から
      if (((pixelQueue + w) < dw) || ((pixelQueue + w) >= (dw + sw))) { // 表示範囲外
        continue;
      }
      else if (pix_str[h][w - dw + pixelQueue] == 1) { // 表示位置の データが 1 のとき
        temp_color = main_color;
      }
      else {  // 表示位置のデータが 0 のとき
        temp_color = strip.Color(0, 0, 0);
      }
      strip.setPixelColor(logidx2phyidx(h * dw + w), temp_color);
    }
  }
  strip.show();
  pixelQueue+=dw; //  Advance current pixel //★画素Queue用変数をLEDシートの幅分加える(文字用配列の列の位置を一文字分移動する)
  if(pixelQueue >= (sw + dw))
    pixelQueue = 0;                         //  Loop the pattern from the first LED
}


9. 本システムの回路

本システムではArduino Nano Everyおよび8×8 LEDシートを使用しますが、その周辺回路について説明します。

◆Arduino周辺回路

Arduino周辺回路を以下に示します。

「8×8 LEDシート制御プログラム」のプログラムでは1番ピンをNeopixel制御用として設定したので、1番ピンを330Ωの抵抗を介してDOUT出力としています。

電源部に念のためノイズ対策として0.1μFと10μFのコンデンサを追加しています。

また、図では省略していますが、Arduino Nano EveryのMicro USB Type-Bコネクタを介してWindows PCと接続します。

 

◆8×8 LEDシート周辺回路

8×8 LEDシートに関しては、下記の図のように単にDIN用・DOUT用の信号ケーブルと電源用のケーブルが接続されています。

これをArduinoと接続するように以下の図のように改造します。

まず、DOUT用のケーブルを半田ごてなどを用いて取り外し、これをArduino側の回路に取り付けます。
次にArduinoからの電源ラインだけでは8×8 LEDを駆動するには電流が不足するため、電源用の別途5V 4AのACアダプターを接続するようにします。
またこちらもノイズ対策として0.1μFのコンデンサを追加しています。

◆接続例
本システムでの接続例を以下に示します。

 


10. 動作確認

以下の手順で今回のシステムの動作確認を行います。
なお、「9. 本システムの回路」での内容に従い、接続済みとして説明します。

◆シリアルポートの確認
(1)Windowsのスタートメニューをクリックし、「デバイスマネージャー」を選択します。

(2)デバイスマネージャーウィンドウにて、「ポート(COM と LPT)」の下に「Arduino NANO Every」があることを確認します。またCOMポート番号を確認します。下記の図ではCOM5となっています。

(3)(2)の「Arduino NANO Every」をクリックし、「プロパティ」を選択します。

(4)「ポートの設定」タブにて、ビット/秒が「9600」であることを確認します。もし異なる場合は「9600」を選択します。

(5)「OK」をクリック後、デバイスマネージャーウィンドを閉じます。

(6)Arduino IDEを起動します。

(7)「ツール」→「シリアルポート」を選択し、「Arduino Nano Every」と表示され、COMポート番号が(2)で確認したものと同じであることを確認します。

 

◆Arduino+8×8 LEDでの動作確認
(1)まずArduino IDEで「8×8 LEDシート制御プログラム」で作成したプログラムをコンパイルします。
「スケッチ」→「検証・コンパイル」を選択します。

(2)コンパイルが成功するとIDE下部のメッセージ部に「コンパイルが完了しました。」と表示されます。

(3)もし下記のようにエラーメッセージが表示される場合は、ソースコード中にErrorがあるので、修正して下さい。

(4)コンパイル結果をArduinoへ書き込みます。
「スケッチ」→「マイコンボードに書き込む」を選択します。

(5)書き込みが成功するとIDE下部のメッセージ部に「ボードへの書き込みが完了しました。」と表示されます。

(6)もし下記のようにエラーメッセージが表示される場合は、接続等に問題があるので、確認してください。

(7)「ツール」→「シリアルモニタ」を選択します。

(8)以下のウィンドウが表示され、表示されているCOMポート番号が「◆シリアルポートの確認」の(2)と同じであることを確認します。

(9)下記図の示す箇所に「SERIAL_COMM:COLOR」とコマンド入力し、Enterキー入力または「送信」をクリックします。

(10)以下のメッセージが表示され、8×8 LEDシートが点灯することを確認します。

(11)(9)と同じように他のコマンドで8×8 LEDシートが正常動作することを確認します。
 以下にコマンド一覧を記載します。
  SERIAL_COMM:COLOR  色の変更
  SERIAL_COMM:MODE    表示モードの変更
  SERIAL_COMM:UP          輝度を上げる
  SERIAL_COMM:DOWN   輝度を下げる
  SERIAL_COMM:FAST     描画速度を速くする
  SERIAL_COMM:SLOW   描画速度を遅くする

以上でArduino Nano Everyに対する書き込み・動作確認は終了です。これ以降、この作業は不要です。
 
◆ジェスチャー操作での動作確認
(1)「6. Motion Gestures SDKサンプルプログラムの修正」で修正したSDKサンプルプログラムをbuildします。

(2)(1)で生成したexeファイルと同じフォルダに、「7. Windows PCのシリアル通信プログラム」で作成したpythonファイルをコピーします。

(3)コマンドプロンプトまたはPowerShellにて、上記保存先のフォルダまで移動します。

(4)以下のコマンドでSDKサンプルプログラムとpythonファイルを起動します。

.\SDKサンプルプログラムexeファイル名 各種コマンドオプション | python pyhtonファイル名 COMポート ボーレート

※”|”は、パイプです。
※”|”(パイプ)以降の例:Pythonファイル名はserial_send_8x8_led.py、COMポートはCOM5、ボーレートは9600として

python serial_send_8x8_led.py COM5 9600

 

(5)以下のジェスチャーで8×8 LEDシートが正常動作することを確認します。
  左スワイプ    色の変更
  右スワイプ    表示モードの変更
  上スワイプ    輝度を上げる
  下スワイプ    輝度を下げる
  Thumbで上   描画速度を速くする
  Thumbで下   描画速度を遅くする

上記操作例を以下に示します。
※上スワイプ・下スワイプ操作は省略しています。

 

 

今回はMotion Gestures社の無償評価用SDK付属のサンプルプログラムを用いて、8×8 LEDシートをジェスチャー操作する方法についてご紹介しました。

本コラムに関するご質問やご要望等ございましたら、ページ下部のWEBマガジンお問い合わせフォームより、お気軽にお問い合わせください。