12-06-2020 08:57 PM
シリアルLEDは1本のデジタル出力で多数のLEDを制御できるので便利です。
テープ状に連なってリールの巻かれて販売されているシリアルLEDや、多数のLEDを長方形やリング状に連結されたシリアルLEDもありますが、この記事では砲弾型のLED(APA106)56個を正四面体の形状に空中配線した特別な形をしています。LEDの信号線は直列で接続されていますので一般的なシリアルLEDと同じです。
シリアルLEDの信号は400kHz、あるいは800kHzのパルス列を使用しますので、ArduinoやRaspberry PiやマイクロビットなどのデジタルIOで制御することができますが、一番情報が豊富なArduinoをArduinoをインターフェースとして、LabVIEWから制御するプログラムを紹介します。
なお、LabVIEWからArduinoを使う方法としてLINXがあります。Arduinoの一般的な入出力に対応するために多機能なArduinoプログラムがLINXファームウェアとしてArduinoに書き込まれているので、Arduinoに接続した入出力をLabVIEWプログラミングだけで制御できるのが利点です。欠点は、特別なArduihnoライブラリを使用するセンサー、アクチュエータを使うのが面倒なことと、動作速度が遅いことです。
LEDをキューブ状に配線したディスプレイは数年前にはやりましたので、目にした方は多いと思いますが、私が作成したのは一辺が6個の正四面体です。自作の立体パズルの組み立て手順を表示させるために作りました。球体を4個連結した7種類各2個のパズルピースを積み上げて正四面体を完成させるパズルですが、そこそこの難易度なのでLEDで手順を表示させたいと思いました。
Fusion360でLEDを配置して空中配線の検討をしました。
5Vの電源線とGND線で自立できる程度の強度を持たせて、信号線を直列で配線します。
5VとGNDには線材からはぎとったビニル被覆(赤と白)で絶縁します。
層ごとに動作チェックします。
完成後は内部にはんだごては届きません。
Arduinoの受信プログラムとLabVIEWの送信プログラムを作ります。
もちろんArduino単体で動作させることもできますが、パズルの組み立て方が100通り以上あるのでLabVIEWで制御できる方が便利です。
プログラムは数値文字列を送る安直な方法と、バイナリで送る高速な方法を作りましたので、順番に返信形式でアップします。
2020.12.07
12-06-2020 10:46 PM
LabVIEWとArduinoに関するご質問でなく申し訳ないのですが、今回設計にFusion360をお使いのことに関心を持ちました。
私はこの製品の名前を知ってます、レベルなのですが、お使いのライセンスは有料のものをお使いでしょうか。
また、今回設計にFusion360を使われたのはどういった背景でしょう?
設計の"せ"の時も十分理解できていないのですが、3次元の配線検討をするときには、こういったツールを使ったほうが便利なのでしょうか?
12-06-2020 11:22 PM
私はスタートアップ企業向けライセンスで無料で使っています。1年ごとの更新が必要ですが、たぶん次回も更新できると期待しています。
非商用の個人用向けのリンク
立体的にLEDを配置して5V,GND,信号の3種類の配線が矛盾しないかどうか検討するために使いました。はんだ付けがかなり根気がいる作業なので、2層目を乗せようとしたら配線が1層目にぶつかった、という事態を避けるためです。1層目を書いてみたら2層目も問題なく積み上げられそうでしたので、途中でやめました。
3Dプリンターはあまり使わないのでFusion360をよく使うわけではないのですが、悩みながら使っていくうちに便利な操作が分かってくるので、使えそうな機会にとりあえず使ってみるという姿勢でいます。
12-07-2020 01:23 AM
送信側のLabVIEWのプログラム
Arduinoが接続されているCOMポートを選択します。ボーレートはArduinoプログラムで指定した値(9600bps)にします。LEDは直列で0から55まであり、R,G,Bそれぞれ0-255で指定します。
1個のLEDに対してLED番号、R値、G値、B値、ラインフィード(¥n)をカンマ区切りで送ります。
このデモプログラムでは0からn個のLEDに同じ色のデータが送られます。n=56で全てのLEDのデータが書き換えられます。
最後にLED番号として99を書きますが、最新のデータで点灯するコマンドになります。
上記の送信プログラムは、Arduinoに書き込まれている受信プログラムに合わせてコンマ区切りで数値文字列を送っているだけです。「2桁のLED番号とコンマ、3桁の数字3個とコンマ2個、改行」で15文字です。56個のLEDの色をすべて変えると840文字です。点灯コマンドは「99,0,0,0¥n」で9文字です。1文字が1byteなので、849byteとなります。
通信速度9600bps(データビットが8bit、ストップビットが1bit)ですと、9600/9=1066.7byte/secなので、送信するだけで796msec必要です。実際はRGBの各値が1桁、2桁という場合があるので、もう少し短いかもしれません。
Arduino側の受信プログラム
概要
Adafruit_NeoPixelライブラリを使ってシリアルLEDを制御します。シリアル通信でLED毎のコンマ区切りのデータを受信し、LEDの色情報を集めた2次元配列(配列名:C)に格納します。シリアル通信でLED番号が"99"の時は配列Cを読んでLEDを点灯させます。
数値文字列で通信していることと受信側でSerial.parseInt()という関数を使っているため時間がかかります。Arduino UNO(互換機)の場合、通信速度を19200bpsに上げてもSerial.parseInt()がネックになって、実動作で400msec間隔での通信が上限でした。
必要なLEDだけのデータを送って更新するような使い方の場合は、気軽に使える利点があると思います。
一般的に、ArduinoにLabVIEWから指示や情報を与える使い方の場合に、とりあえず試してみることのできる方法だと思います。
「簡単にLEDデータを受信する.zip」には「DemoNumStrSerialLED.vi」とArduino用プログラム「NumStrSerialLED」が入っています。
******************************************************************************************************
プログラムから部分的にピックアップして説明
シリアルLEDのライブラリ(Adafruit_NeoPixel)を使います。
#include <Adafruit_NeoPixel.h>
Digital IOの6ピンを信号出力にします。
#define PIN 6
LED数、出力ピン、LEDのタイプをライブラリにつたえます。
Adafruit_NeoPixel strip = Adafruit_NeoPixel(COUNTS, PIN, NEO_RGB + NEO_KHZ400);
LED点灯用の色情報の2次元配列です。シリアル通信で受け取ったデータを格納します。点灯命令(99)でこの配列全体を読み取ってライブラリの管理している色設定を更新します。
byte C[COUNTS][3];
シリアル通信の速度指定とライブラリの開始
Serial.begin(9600);
strip.begin();
init_C(byte r, byte g, byte b)は配列C[COUNTS][3]の初期値設定の関数です。LED_turnON()は配列C[COUNTS][3]を読んで点灯させます。
init_C(30,0,30);
LED_turnON();
シリアル通信の受信バッファにデータがある限りwhileループが回ってデータを受け取ります。バッファは64バイトしかないので注意が必要です。
while (Serial.available() > 0) {
Serial.parseInt()はArduinoで用意されている便利な関数で、数値文字列を抽出して数字に変換してくれます。便利ですが、その分動作時間がかかります。
LED番号、R値、G値、B値をカンマ区切りを目印にして受け取ります。
int num = Serial.parseInt();
int red = Serial.parseInt();
int green = Serial.parseInt();
int blue = Serial.parseInt();
Serial.read()は1バイト受け取る関数で、ラインフィード(¥n)であれば手はず通りデータを受け取れたと考えます。
if (Serial.read() == '\n') {
LED番号が99なら配列C[COUNTS][3]を読んで点灯させます。
if (num == 99){
LED_turnON();
LED番号が99以外ならLED番号に対応する配列の要素に受け取った数値を書き込みます。
} else {
C[num][0]=red;
C[num][1]=green;
C[num][2]=blue;
Arduinoプログラム全文(インデントは失われてみにくいですが、、。)
--------------------------------------------------------------------
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define COUNTS 56
Adafruit_NeoPixel strip = Adafruit_NeoPixel(COUNTS, PIN, NEO_RGB + NEO_KHZ400);
byte C[COUNTS][3];
void setup() {
Serial.begin(9600);
strip.begin();
init_C(30,0,30);
LED_turnON();
}
void loop() {
while (Serial.available() > 0) {
int num = Serial.parseInt();
int red = Serial.parseInt();
int green = Serial.parseInt();
int blue = Serial.parseInt();
if (Serial.read() == '\n') {
if (num == 99){
LED_turnON();
} else {
C[num][0]=red;
C[num][1]=green;
C[num][2]=blue;
}
}
}
}
void LED_turnON()
{
for(int i=0;i<COUNTS;i++){
strip.setPixelColor(i, strip.Color(C[i][0], C[i][1], C[i][2]));
}
strip.show();
}
void init_C(byte r, byte g, byte b){
for(int i=0;i<56;i++){
C[i][0]=r;
C[i][1]=g;
C[i][2]=b;
}
}
12-07-2020 01:49 AM
12-07-2020 01:52 AM
12-07-2020 03:33 AM
</>でインデントが活きたまま掲載できました。
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define COUNTS 56
Adafruit_NeoPixel strip = Adafruit_NeoPixel(COUNTS, PIN, NEO_RGB + NEO_KHZ400);
byte C[COUNTS][3];
void setup() {
Serial.begin(9600);
strip.begin();
init_C(30,0,30);
LED_turnON();
}
void loop() {
while (Serial.available() > 0) {
int num = Serial.parseInt();
int red = Serial.parseInt();
int green = Serial.parseInt();
int blue = Serial.parseInt();
if (Serial.read() == '\n') {
if (num == 99){
LED_turnON();
} else {
C[num][0]=red;
C[num][1]=green;
C[num][2]=blue;
}
}
}
}
void LED_turnON()
{
for(int i=0;i<COUNTS;i++){
strip.setPixelColor(i, strip.Color(C[i][0], C[i][1], C[i][2]));
}
strip.show();
}
void init_C(byte r, byte g, byte b){
for(int i=0;i<56;i++){
C[i][0]=r;
C[i][1]=g;
C[i][2]=b;
}
}
12-07-2020 04:03 AM - 編集済み 12-07-2020 04:05 AM
LabVIEW側の変更は少しです。
Byteデータを配列にして、それを文字列に変換して送るだけなのでむしろ簡単になりました。
Arduino側のプログラムもシンプルといえばシンプルになりました。
ボーレートを38400bpsにして50msec間隔で受信することができました。
LEDの個数の3倍のバイト数のデータが来ることを信じ切って受信します。
LEDのRGBデータは1次元配列(配列名:C)にしました。RGBRGBRGBRGB....でLEDの個数分続きます。
byte C[Buf_Size]
受信はSerial.readBytes()関数を使って、配列Cに直接取り込みます。
Serial.readBytes(C,Buf_Size);
同期が合わないとデータがずれたままになりますので、全部読み取ったのに受信バッファに残っている部分を捨ててしまいます。次回は先頭から読むことができます。
while(Serial.available()){
Serial.read();
}
表示は1次元配列からrgbを取り出すだけです。
strip.setPixelColor(i, strip.Color(C[i*3], C[i*3+1], C[i*3+2]));
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define COUNTS 56
#define Buf_Size COUNTS*3
Adafruit_NeoPixel strip = Adafruit_NeoPixel(COUNTS, PIN, NEO_RGB + NEO_KHZ400);
byte C[Buf_Size];//rgbrgbrgb.....
void setup() {
//Serial.begin(9600);
Serial.begin(38400);
strip.begin();
init_C(20,0,20);
}
void loop() {
if(Serial.available()){
Serial.readBytes(C,Buf_Size);
TurnOn();
while(Serial.available()){
Serial.read();
}
}
}
void TurnOn()
{
for(int i=0;i<COUNTS;i++){
strip.setPixelColor(i, strip.Color(C[i*3], C[i*3+1], C[i*3+2]));
}
strip.show();
}
void init_C(byte r, byte g, byte b){
for(int i=0;i<56;i++){
C[i*3]=r;
C[i*3+1]=g;
C[i*3+2]=b;
}
}
2020.12.07