chakokuのブログ(rev4)

日々のごった煮ブログです

NeoPixelをArduino Nanoで制御

当初、AVRマイコンでCで書いてNeoPixelを制御するか??と考えていたけど、使っていないArduino Nanoがあったので、これを使ってNeoPixelを制御することにした。下が俺ArduinoNano(自作)

幸い、Adafruitが提供しているNeoPixel用ライブラリがあるので、これを使うと、簡単に制御できる。実際に動かしてみると、非常に安定している。すばらしい。NeoPixelは発色がウリなので動画も撮ってしまった。。テストした結果が以下。
youtu.be
Arduinoのテストプログラムは以下

#include <Adafruit_NeoPixel.h>
#define PIN 6
#define NUMPIXELS 24

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
uint32_t tmpl_color= pixels.Color(0, 0, 0);

void setup() {
  pixels.begin();
  Serial.begin(9600);
}

void loop() {
  int i,j;
  uint32_t  color;
  Serial.print("circle");
  color = pixels.Color(0,0,20); 
  circle(color);
  color = pixels.Color(0,10,10); 
  circle(color);
  color = pixels.Color(0,20,0); 
  circle(color);
  color = pixels.Color(10,10,0); 
  circle(color);
  color = pixels.Color(20,0,0); 
  circle(color);
  color = pixels.Color(10,0,10); 
  circle(color);
}

void circle(uint32_t color){
  int i,n;
  for(i=0; i<3; i++){
    for(n=0; n<NUMPIXELS; n++){
       pixels.setPixelColor(n,color);
       pixels.show();
       delay(4);
       pixels.clear();
       pixels.show();
       delay(4);
    }   
  }
}

これまで誤動作していたのは、3.3Vでだましながら制御していたせいか、あるいは、タイミングの精度が悪くて、LEDを正しく制御できなかったのいずれかと思う。

Arduinoで制御できる目途が立ったけど、、RaspberryPiとArduinoを接続する必要がある。Arduino NanoにはUSB-Serial変換ICが載っているので、ラズパイとArduinoをUSBで接続できるはずだ。。多分。ラズパイ側にドライバいるかもしれないけど。

多少modprobeとかしてしまったのではっきりわからないけど、多分ドライバを追加しなくてもFT232Hはつながると思う。ただ、パーミッションを正しく設定する必要があり、rootでいきなり/dev/ttyUSB0とやるとエラーになる。

$ sudo su -
# cu -l /dev/ttyUSB0
cu: open (/dev/ttyUSB0): Permission denied
cu: /dev/ttyUSB0: Line in use

以下のようなコマンドで、dialoutグループに自分自身を追加する

$ sudo usermod -a -G dialout $USER

dialoutグループに自分が足されているのを確認する (UID:piのまま使っていたら問題なく使えたかも)
自分の名前が太郎冠者(tarokaja)とした場合の例

$ grep dialout /etc/group
dialout:x:20:pi,tarokaja

cuコマンドで接続できたことを確認

RaspberryPiからArduino Nanoと通信するのはUSB-Serialを介して行うが、RaspberryPi側はPySerialを使おう。
pyserialモジュールをインストールすると以下で操作できるらしい

import serial
 
ser = serial.Serial('/dev/ttyUSB0',9600, timeout=None)
line = ser.readline()
print(line) 
ser.close()  

Arduino側はNeoPixelを制御しながら、シリアルで'circle'と出力している。

[RaspberryPi Zero]---usb---[Arduino]-----[NeoPixel]

ラズパイ側のSerial受信サンプル

import serial
import time
ser = serial.Serial('/dev/ttyUSB0',9600, timeout=None)
while True:
    str = ser.read_all()
    print(str)
    time.sleep(1)

実行結果(Arduinoから送られてくる)

b'circlecirclecirclecirclecirclecirclecirclecirclecircle'
b''
b''
b''
b'circle'
b''
b''
b''
b'circle'

次は、、Arduino側のNeoPixel制御ソフトにSirial入力ルーチンを追加して、要求があったらLED点灯パターンを変えるようにする。そうすれば、、「Alexa。。。LEDをON」と発話すると、NeoPixelがグルグル回るはず。。

■追記
シリアルで制御できるNeoPixel Contorllerを試作

#include <Adafruit_NeoPixel.h>

#define PIN 6
#define NUMPIXELS 24

#define OFF 0
#define ON 1
#define RED 2
#define BLUE 3
#define GREEN 4
#define CIRCLE 5
#define CMD_NONE -1

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int current_state;

void led_fill(uint32_t color);

void setup() {
  pixels.begin();
  Serial.begin(9600);
  Serial.print("====================\n");
  Serial.print("Neo Pixel Controller\n");
  Serial.print("====================\n");
  Serial.print(">");
  led_off();
  current_state = OFF;
}

void loop() {
  int i,j,cmd;
  uint32_t color;
  String str;
  if (Serial.available() > 0){
      str = Serial.readStringUntil('\n');
      Serial.print("---target---\n");
      Serial.print(str);
      Serial.print("-------------\n");
  }else{
    str = "";
  }

  if(str.indexOf("ON")> -1){
      Serial.print("\n->ON!\n");    
    cmd = ON;
  }else if(str.indexOf("OFF")> -1){
      Serial.print("\n->OFF!\n");    
    cmd = OFF;
  }else if(str.indexOf("RED")> -1){
      Serial.print("\n->RED!\n");    
    cmd = RED;
  }else if(str.indexOf("BLUE")> -1){
      Serial.print("\n->BLUE!\n");    
    cmd = BLUE;
  }else if(str.indexOf("GREEN")> -1){
      Serial.print("\n->GREEN!\n");    
    cmd = GREEN;
  }else if(str.indexOf("CIRCLE")> -1){
      Serial.print("\n->CIRCLE!\n");    
    cmd = CIRCLE;
  }else{
    cmd = CMD_NONE;
  }
  if(cmd == CMD_NONE || current_state == cmd){
      ;
  }else{
    switch(cmd){
      case ON:
      case RED:
            Serial.print("turn on RED");
         color = pixels.Color(20,0,0);
         led_fill(color);
         current_state = RED;
         break;
      case OFF:
            Serial.print("SW OFF");
        led_off();
        current_state = OFF;
        break;
      case GREEN:
            Serial.print("switch to GREEN");
        color = pixels.Color(0,20,0);
        led_fill(color);
         current_state = GREEN;
         break;
      case BLUE:
           Serial.print("switch to BLUE");
        color = pixels.Color(0,0,20);
        led_fill(color);
         current_state = BLUE;
         break;
      case CIRCLE:
         current_state = CIRCLE;
         break;
      default:
         ;  
    }
  }
  if(current_state == CIRCLE){
      circle();
   }
   if (cmd != CMD_NONE){
    Serial.print("\n>");
   }
}

void circle(){
  uint32_t  color;
  Serial.print("circle");
  color = pixels.Color(0,0,20); 
  circle3(color);
  color = pixels.Color(0,10,10); 
  circle3(color);
  color = pixels.Color(0,20,0); 
  circle3(color);
  color = pixels.Color(10,10,0); 
  circle3(color);
  color = pixels.Color(20,0,0); 
  circle3(color);
  color = pixels.Color(10,0,10); 
  circle3(color);
}

void circle3(uint32_t color){
  int i,n;
  for(i=0; i<3; i++){
    for(n=0; n<NUMPIXELS; n++){
       pixels.setPixelColor(n,color);
       pixels.show();
       delay(4);
       pixels.clear();
       pixels.show();
       delay(4);
    }   
  }
}
void led_fill(uint32_t color){
  pixels.fill(color,0,NUMPIXELS);
  pixels.show();
}
void led_off(void){
  pixels.clear();
  pixels.show();
}

USB-SerialからArduinoにコマンドを送るとNeoPixelが制御できる。使えるコマンドは、ON,RED,BLUE,GREEN,CIRCLE,OFF。これを、USB接続したラスパイから叩くと。。
以下の操作でNeoPixelが赤色で点灯する

import serial
ser = serial.Serial('/dev/ttyUSB0',9600, timeout=None)
ser.write(b"RED\n")