Raspberry PiとRGBマトリクスで電光掲示板作ってアニメ流したった


この記事は、[Raspberry Pi Advent Calendar 2015](http://qiita.com/advent-calendar/2015/raspberrypi)の22日目の記事です。

# 製作記
RasPi2も出たし(Zeroなんてのも出てますが。)そろそろ触ってみたいな〜なんて思ってなんとなーく作りました。

適当な説明ですが、ご容赦を。

## 作った物
16×32のRGBフルカラーマトリクスを9枚つかってアニメを流してみました。

### 大体の仕様

マトリクスは3つまで並列に接続することができます。直列にはいくらでもイケルと思います。今回は3枚直列に、3枚並列に接続し、3×3=9枚(解像度は48×96)使用しました。

## 使ったもの
- [RaspberryPi 2 ModelB](http://akizukidenshi.com/catalog/g/gM-09024/)
![M-09024.jpg](http://akizukidenshi.com/img/goods/C/M-09024.jpg)

- [RGBフルカラードットマトリクスLEDパネル](http://akizukidenshi.com/catalog/g/gM-07764/)
![M-07764.jpg](http://akizukidenshi.com/img/goods/2/M-07764.jpg)

- [スイッチング電源ボード DC5V20A](http://akizukidenshi.com/catalog/g/gM-08705/)
![M-08705.jpg](http://akizukidenshi.com/img/goods/C/M-08705.jpg)

- 適当な基盤
- 適当な抵抗
- 適当なコンデンサ
- 適当なレギュレータ
- WiFiアダプタ


## 配線

- 太字のものが並列接続する初段のマトリクスに接続します。
- 直列に接続するディスプレイはチェーン状に接続します。
- [数字]の太字で表記しているのは並列接続初段の1段目・2段目・3段目に対応しています。対応した数字と接続してください。
- [数字]表記されてなく、太字で書かれているものは並列接続初段のマトリクス全てと接続してください。
- 普通の文字のものはその辺の基板とその辺の部品使って電源作ってそれを接続してください。

![haisen.jpg](https://qiita-image-store.s3.amazonaws.com/0/35850/6dc6215d-0d52-b065-021e-f2b2c3b10f2b.jpeg)

### ラズパイ側接続図

接続先                             | ピン | ピン |  接続先
---------------------------------:|:---:|:---:|:-----------------------------
                          3.3V    |   1 |   2 | 5V
                       **[3] G1** |   3 |   4 | 5V
                       **[3] B1** |   5 |   6 | **GND**                       
                       **strobe** |   7 |   8 | **[3] R1**
                            GND   |   9 |  10 | -
                       **clock**  |  11 |  12 | **OE-**                        
                       **[1] G1** |  13 |  14 | GND
                            **A** |  15 |  16 | **B**                          
                          3.3V    |  17 |  18 | **C**
                       **[1] B2** |  19 |  20 | GND
                       **[1] G2** |  21 |  22 | **D**
                       **[1] R1** |  23 |  24 | **[1] R2**
                           GND    |  25 |  26 | **[1] B1**
                             -    |  27 |  28 | -
                       **[2] G1** |  29 |  30 | GND
                       **[2] B1** |  31 |  32 | **[2] R1**
                       **[2] G2** |  33 |  34 | GND
                       **[2] R2** |  35 |  36 | **[3] G2**
                       **[3] R2** |  37 |  38 | **[2] B2**
                            GND   |  39 |  40 | **[3] B2**


### マトリクス側ピンアサインなど

#### マトリクス側
![hub75-other.jpg](https://raw.githubusercontent.com/hzeller/rpi-rgb-led-matrix/master/img/hub75-other.jpg)

#### 付属ケーブル接続時
![idc-hub75-connector.jpg](https://raw.githubusercontent.com/hzeller/rpi-rgb-led-matrix/master/img/idc-hub75-connector.jpg)


## ラズパイ設定

Raspbianなどの適当なOSをラズパイに突っ込んで起動しましょう。
SSHとかやりたければご自由に。

必要そうなソフトウェア
- Python(標準で入ってるよね？・適当にあそぶなら。)
- make(最初からありそう)
- gcc/g++(最初からありそう)
- git(なかったらapt-getなりyumなりで入れてください)
- OpenCV(動画再生するときに使う)
- SDL(音も出したければどうぞ)

この辺を入れてください。

## デモを動かす

[hzeller/rpi-rgb-led-matrix](https://github.com/hzeller/rpi-rgb-led-matrix)を使います。
cloneしてmake。

```shell-session
git clone https://github.com/hzeller/rpi-rgb-led-matrix.git
cd rip-rgb-led-matrix
make
# Pythonであそぶなら
make install-python
```

これでデモがビルドされます。

以下でデモが動きます。

```shell-session
sudo ./led-matrix -t 10 -D 9 -P 3 -r 16 -c 3
```

- -P:並列接続数
- -c:直列接続数
- -r:マトリクスの縦のピクセル数
- -D:デモ番号

-Dの数字いじって遊んでみてください。


## アニメを流す

動画の解像度が高い場合は最初にある程度の解像度まで落としておいてください。

先のデモのライブラリを新たにsubmoduleとして使います。

```shell-session
git submodule add https://github.com/hzeller/rpi-rgb-led-matrix.git matrix
```

Makefile作ります。

```mf:Makefile

CXX=g++-4.8


OBJS=matrix-app.o 
TARGET=matrix-app

CXXFLAGS=-std=c++11
RGB_INCDIR=matrix/include
RGB_LIBDIR=matrix/lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread -lopencv_core -lopencv_highgui -lopencv_imgproc -lSDL -lSDL_mixer


all: $(TARGET)

$(RGB_LIBRARY):
	$(MAKE) -C $(RGB_LIBDIR)


$(TARGET): $(OBJS) $(RGB_LIBRARY)
	$(CXX) $(CXXFLAGS) $(OBJS) -o $@ $(LDFLAGS)

%.o : %.cc
	$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $<


install:
	cp $(TARGET) /usr/local/bin/$(TARGET)

clean:
	rm -f $(OBJECTS) $(TARGET)
	$(MAKE) -C matrix clean
```

プログラム書きます。
このプログラムは音流すつもりで書いてますが適宜どうぞ。

```cpp:matrix-app.cc

#include <iostream>
#include <stdio.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <unistd.h>
#include <chrono>
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include "led-matrix.h"
#include "graphics.h"


using namespace rgb_matrix;

int main(int argc, char *argv[]){

    if(argc != 2){
        std::cout << "little or many argments" << std::endl;
        return 0;
    }

    std::string path_prefix = "/usr/local/movieplayer/";

    std::string file_prefix = argv[1]; 

    std::string audio_file = path_prefix + "audio/" + file_prefix + ".flac";
    std::string movie_file = path_prefix + "movie/" + file_prefix + ".mp4"; 

    //Setting
    GPIO io;
    int chain = 3;
    int parallel = 3;
    int rows = 16;

    if(!io.Init())
        return 1;

    SDL_Init(SDL_INIT_AUDIO);
    Mix_OpenAudio(48000, MIX_DEFAULT_FORMAT, 2, 4096);
    Mix_Music *music=Mix_LoadMUS(audio_file.c_str());
    if(!music)return 1;

    RGBMatrix *matrix = new RGBMatrix(&io, rows, chain, parallel);
    matrix->set_luminance_correct(true);
    matrix->SetBrightness(100);
    matrix->SetPWMBits(11);
    FrameCanvas *canvas = matrix->CreateFrameCanvas();
    const int width = canvas->width();
    const int height = canvas->height();

    //Loading
    
    cv::Mat src_img;
    cv::Mat dst_img(height, width, src_img.type());
    cv::VideoCapture cap(movie_file.c_str());

    auto fps = cap.get(CV_CAP_PROP_FPS);
    long fps_ustime = 1000000.0/(double)fps+0.5;
    
    const auto hoseicount = (int)fps*10;
    auto hcnt=0;
    //Drawing
    

    if(Mix_PlayMusic(music, 1)==-1) {
        printf("Mix_PlayMusic: %s\n", Mix_GetError());
        return 1;
    }
    auto pre_dc=1ll;

    auto frame_cnt = cap.get(CV_CAP_PROP_FRAME_COUNT) - 1; // Magic!!!
    auto start = std::chrono::system_clock::now();
    while(true){

        auto fpos = cap.get(CV_CAP_PROP_POS_FRAMES);
        if(!(fpos < frame_cnt)){
            std::cout << "end." << std::endl;
            break;
        }

        cap >> src_img;

        auto pos = cap.get(CV_CAP_PROP_POS_MSEC);
        auto r = (double) height / src_img.rows;
        auto w = (int) (src_img.cols * r);
        auto d = (width - w) / 2;
        cv::resize(src_img, dst_img, cv::Size(w, height), cv::INTER_AREA);
        for(int y=0; y<height; y++){
            for(int x=0; x<w; x++){
                cv::Vec3b bgr = dst_img.at<cv::Vec3b>(y, x);
                canvas->SetPixel(x + d, y, bgr[2], bgr[1], bgr[0]);
            }
        }
        matrix->SwapOnVSync(canvas);
        auto end = std::chrono::system_clock::now();
        
        auto delaytime = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();

        auto delaycount = pos * 1000 - delaytime;
        if(delaycount > 0) {
            usleep(delaycount);
        }
    }
    matrix->Clear();
    Mix_HaltMusic();
    Mix_FreeMusic(music);
    Mix_CloseAudio();
    SDL_Quit();
    
    delete matrix;

    std::cout << "See You!" << std::endl;
    return 0;
}

```

1. `/usr/local/movieplayer/movie`にmp4の動画ファイルを置きます
2. `/usr/local/movieplayer/audio`にflacの音声ファイルを置きます
    - ここで動画ファイルと音声ファイルのファイル名を同じにしておいてください
3. `make && make install`する
4. `sudo matrix-app 拡張子なしファイル名`を実行
5. 動画が流れる
6. 喜ぶ！ 

## こんな感じにできました

![anime.gif](http://img.gifmagazine.net/gifmagazine/images/688257/original.gif?1450447365)

# 終わりに

雑ですが、以上です。
ラズパイ、いろんなことができて楽しいですね。Linuxがのってますからコレ一個でやりたい放題です。

一応PS3のコントローラとBluetoothでテトリス作ったり、リモコンでアニメ流せるようにしたんですが、それはまた別の機会にでも。

明日は@kaiware007 さんです。

それでは！！