二次元NeoPixel行列を使いやすくしたい

2017-05-01

ライブラリのおかげでRaspberry Piで、C言語でNeoPixelのLEDを制御できるので、今回はsocketを使って、LEDの情報をRaspberry Piに送ることで外部からLEDをコントロールできるようにする。

ライブラリの使い方は前回のやつで。

Socketを使う

データのやり取りにSocketを使う。

参考http://www.geekpage.jp/programming/linux-network/

あとは頑張る

今回はLEDが、24x15の配列、GBRなので、データ送る側は

  • バッファは 24x15x3色 の1080バイト
  • 各色1バイト、256段階で、Green, Blue, Redの順にバッファに詰める
  • 送る

ということをした。実際のLEDは、下から上、上から下、下から上・・・という順でつながっているので、バッファのLEDの情報もその順で詰める。そうすると、Raspberry Pi側では受け取ったデータをfor文で回して順番にLEDを更新していくだけで良い。


/\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  || 
||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/
/\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  || 
||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/
/\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  || 
||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/
/\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  ||  /\  || 
||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/  ||  \/
(こんな感じなので)

                              

これであとは、データを送る側のコードをゴニョゴニョするだけでよくなった。とりあえず、今の時点でのRaspberry Pi側のコードだけ書いておく。


#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <signal.h>
#include <stdarg.h>
#include <getopt.h>

#include <sys/socket.h>
#include <netinet/in.h>

#include "clk.h"
#include "gpio.h"
#include "dma.h"
#include "pwm.h"
#include "ws2811.h"

#define TARGET_FREQ WS2811_TARGET_FREQ
#define GPIO_PIN 18
#define DMA 5   
#define STRIP_TYPE WS2811_STRIP_RGB

ws2811_t ledstr = 
{
     .freq = TARGET_FREQ,
     .dmanum = DMA,
     .channel = 
     {
          [0] = 
          {
               .gpionum = GPIO_PIN,
               .count = 0,
               .invert = 0,
               .brightness = 255,
               .strip_type = STRIP_TYPE,
          },
          [1] = 
          {
               .gpionum = 0,
               .count = 0,
               .invert = 0,
               .brightness = 0,
          },
     },
};

void update_leds(char *array, int size)
{
     int i;
     uint32_t gbr;
     for(i = 0; i < size; i+=3)
     {
          gbr = (array[i] << 16) | (array[i+1] << 8) | (array[i+2] << 0);
          ledstr.channel[0].leds[i/3] = gbr;
     }
     ws2811_render(&ledstr);
}



int main(int argc, char *argv[])
{
     /*====== Argument Handling =======*/
     struct option longopts[] = {
          { "port", required_argument, NULL, 'p'},
          { "width", required_argument, NULL, 'w'},
          { "height", required_argument, NULL, 'h'},
     };

     int port, width, height;
     int opt, longindex;
     while((opt = getopt_long(argc, argv, "", longopts, &longindex)) != -1)
     {
          if(opt == 'p')
               sscanf(optarg, "%d", &port);
          else if(opt == 'w')
               sscanf(optarg, "%d", &width);
          else if(opt == 'h')
               sscanf(optarg, "%d", &height);
     }

     if(port <= 0 | width <= 0 | height <= 0){
          printf("Invalid Arguments.\n");
          return 1;
     }

     /*=====END Argument Handling=======*/

     int led_count = width * height;
     int array_size = led_count * 3;

     printf("Led Count : %dleds\n", led_count);
     printf("Width     : %d\n", width);
     printf("Height    : %d\n", height);

     /*===== Socket ======*/

     struct sockaddr_in addr;
     struct sockaddr_in client;
     int length, sock_s, sock_c;

     // IPV4, TCP
     sock_s = socket(AF_INET, SOCK_STREAM, 0);
     if(sock_s < 0){
          perror("ERROR");
          return 1;
     }

     addr.sin_family = AF_INET;
     addr.sin_port = htons(port);
     addr.sin_addr.s_addr = INADDR_ANY;

     bind(sock_s, (struct sockaddr*)&addr, sizeof(addr));

     listen(sock_s, 5);

     length = sizeof(client);
     sock_c = accept(sock_s, (struct sockaddr*)&client, &length);

     printf("Connection from   : %s\n", inet_ntoa(client.sin_addr));
     printf("Port              : %d\n", ntohs(client.sin_port));

     printf("Listning...\n");

     int n;
     char buffer[array_size];


     ledstr.channel[0].count = led_count;
     ws2811_init(&ledstr);

     while(1)
     {
          n = read(sock_c, buffer, array_size);

          if(n == 1080)
          {
               update_leds(buffer, array_size);
          }
          else if(n == 1)
          {
               int size = 32;
               char cmd[size];
               char what[size];
               char ans[size];
               read(sock_c, cmd, size);
               printf("command %s\n", cmd);
               if(strcmp(cmd, "get") == 0){
                    read(sock_c, what, size);
                    printf("what %s\n", what);
                    if(strcmp(what, "width") == 0){
                         sprintf(ans, "%d", width);
                         write(sock_c, ans, size);
                    }
                    if(strcmp(what, "height") == 0){
                         sprintf(ans, "%d", height);
                         write(sock_c, ans, size);
                    }
               }
          }
          else if(n <= 0)
          {
               break;
          }
     }

     close(sock_c);
     close(sock_s);
     ws2811_fini(&ledstr);

     return 0;
}

                              

ただ配列を受け取って、update_ledsでledを更新しているだけです。それと無理やり幅と高さをゲットできるように。

Github https://github.com/habitatlakai/ws281x_matrix/

雨降らし

データを送る側のライブラリを少し作った。横1、縦2の粒を100個作って、ランダムに上から降らした。x,yはLED配列の範囲外だとそのままさようならする。

これのコードはgithubにあります。(client_rain.c)