Arduinoからの文字列をシリアル通信で受け取ってopenFrameworksで表示する


# 1. 概要

Arduinoに接続されているNeoPixelを光らせるためのデータをoFから送るというプログラムを作成している。しかし、上手く動かないためにoF側でもArduinoがちゃんとデータを受け取れているのか見たい。そこでArduinoで`Serial.println();`したデータをoFで表示するプログラムを作成したので、その部分をメモしておく。

# 2. 用意するもの

* Arduino UNO (今回はDuemilanoveを使用)
* NeoPixel (今回はLEDが19個つながっているものを使用)
* PC (MacBook Pro(Retina, 13-inch, Mid 2014) & macOS Sierra 10.12.3)
* Arduino IDE 1.8.1
* openFramewokrs v0.9.8

# 3. 参考サイト

## 3.1 oF参考サイト

1. [Serial read string from Arduino - 【oF】openFrameworks](https://sites.google.com/site/ofauckland/examples/serial-read-string-from-arduino)
	* このページ以外にもいろいろなサンプルが載っていて便利そう
2. [I touchs: openFrameworks: Serial Read String From Arduino Simulated Sensor](http://itouchs.blogspot.jp/2015/10/openframeworks-serial-read-string-from.html)
	* 1.のサイトを実装したもの
3. [oF: HT16K33をopenFrameworksから制御する - Qiita](http://qiita.com/hisahayashi/items/fc3b3ca2ead1e11383b5)
	* シリアル送信の参考にした
	* `serial.writeBytes(writeByte, writerSerial.length() + 1);`を`serial.writeBytes(writeByte, writerSerial.length());`に変更した
	* 私の場合は、載っている通りだとデータが2回送られてしまっていた。
	* [ofSerial | openFrameworks](http://openframeworks.cc/documentation/communication/ofSerial/#show_writeBytes)では`+ 1`されていな気がしたので


## 3.2 Arduino参考サイト

1. [ArduinoでNeoPixelのLEDを使ってみる。–あかさたな](http://www.ningendesu.net/?p=579)
	* NeoPixelのプログラムの参考に
2. [How do I receive an entire string as opposed to 1 character at a time on the arduino? - Electrical Engineering Stack Exchange](https://electronics.stackexchange.com/a/45551)
	* 指定の文字(\0)まで読み込むプログラムの参考に
3. [arduinoでカンマ区切りのシリアル通信(入力) - なすぶろぐ](http://nascola.hatenablog.com/entry/2015/12/22/014805)
	* カンマ区切りで文字列を分ける参考に

# 4. プログラム

## 4.1 oF側のプログラム

NeoPixelを点灯させるためのデータ形式を「R,G,B,Brightness;」として、キーボードから送信する。
Arduinoからのデータを画面に表示する。

```cpp:ofApp.h
#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();

		void keyReleased(int key);
    
    // ofSerial
    ofSerial	serial;
    
    string ofxGetSerialString(ofSerial &serial, char until);
    string ofxTrimStringRight(string str);
    string ofxTrimStringLeft(string str);
    string ofxTrimString(string str);
    string str;
    string msg;
		
};

```

```cpp:ofApp.cpp
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    ofSetFrameRate(60);
    
    // ofxSerial
    serial.setup("/dev/cu.usbserial-A600euN6", 9600);
    
}

//--------------------------------------------------------------
void ofApp::update(){

    str = ofxGetSerialString(serial, '\n');
    if (str.length() > 0) {
        msg = str;
    }

}

//--------------------------------------------------------------
void ofApp::draw(){

    ofSetColor(0);
    ofDrawBitmapString(msg, 100, 100);
    
}

string ofApp::ofxGetSerialString(ofSerial &serial, char until) {
    static string str;
    stringstream ss;
    char ch;
    int ttl=1000;
    while ((ch=serial.readByte())>0 && ttl-->0 && ch!=until) {
        ss << ch;
    }
    str+=ss.str();
    if (ch==until) {
        string tmp=str;
        str="";
        return ofxTrimString(tmp);
    } else {
        return "";
    }
}

// trim trailing spaces
string ofApp::ofxTrimStringRight(string str) {
    size_t endpos = str.find_last_not_of(" \t\r\n");
    return (string::npos != endpos) ? str.substr( 0, endpos+1) : str;
}

// trim trailing spaces
string ofApp::ofxTrimStringLeft(string str) {
    size_t startpos = str.find_first_not_of(" \t\r\n");
    return (string::npos != startpos) ? str.substr(startpos) : str;
}

string ofApp::ofxTrimString(string str) {
    return ofxTrimStringLeft(ofxTrimStringRight(str));;
}


//--------------------------------------------------------------
void ofApp::keyReleased(int key){
    if( key == '1') {
        string writerSerial = "0,0,0,0;";
        cout << "writerSerial" << writerSerial << endl;
        unsigned char* writeByte = (unsigned char*) writerSerial.c_str();
        int result = serial.writeBytes(writeByte, writerSerial.length() + 1);    }
    else if( key == '2') {
        string writerSerial = "100,100,100,100;";
        unsigned char* writeByte = (unsigned char*) writerSerial.c_str();
        int result = serial.writeBytes(writeByte, writerSerial.length() + 1);
    }
    else if( key == '3') {
        string writerSerial = "255,0,255,100;";
        unsigned char* writeByte = (unsigned char*) writerSerial.c_str();
        int result = serial.writeBytes(writeByte, writerSerial.length() + 1);
    }
}

```

# 4.2 Arduino側のプログラム

oFからのデータを終端文字`\0`区切りで受け取り、その後`,`と`;`でRGB,Brightnessに分ける。

```cpp:test.ino
#include <Adafruit_NeoPixel.h> 
#define PIN            6   //Arduinoで使うピン
#define NUMPIXELS      19   //LEDの数。

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

int numBlue = 0;
int numGreen = 0;
int numRed = 0;
int numBrightness = 0;

String inData;

void setup() {
  pixels.begin(); // ライブラリ使用開始
  Serial.begin(9600);
  color(10,10,10,100);
}

void loop() {
  if(Serial.available()>0){
  
    char recieved = Serial.read();
    inData += recieved; 

    if (recieved == '\0')
    {
      Serial.print("Arduino Received: ");
      Serial.println(inData);
  
      numRed = atoi(strtok(inData.c_str(),",")); // .c_str()でString->char変換、strtokでカンマ区切りで分割、atoiでchar->int変換
      numGreen = atoi(strtok(NULL,","));
      numBlue = atoi(strtok(NULL,","));
      numBrightness = atoi(strtok(NULL,";"));
  
      color(int(numRed),int(numGreen),int(numBlue), int(numBrightness));
  
      inData = ""; // Clear recieved buffer
    }
  }
  pixels.show(); 
}


void color(int cred, int cgreen, int cblue, int brightness) {
  pixels.setBrightness(brightness);
  for(int i=0; i < NUMPIXELS; i++){
    pixels.setPixelColor(i,pixels.Color(cred,cgreen,cblue));
  }
}
```

# 5.実行結果

Arduinoにプログラムを書き込むとNeoPixelが白く光る。
oFのプログラムを実行し、キーボードを押すとNeoPixelの色が変わり、実行画面にArduinoからのデータが表示される。

![スクリーンショット 2017-05-10 10.42.47.png](https://qiita-image-store.s3.amazonaws.com/0/94069/c435a7b7-ebe9-1097-0de1-f22c144a4b1a.png "スクリーンショット 2017-05-10 10.42.47.png")