Processingとマイコンの連携/シリアル通信(USB経由)でデータの送受信

Arduino/ESP32,ProcessingcontrolP5,ESP32,Processing,シリアル,モニタ,送受信

先日は何とかProcessingをVSCodeで動かす事ができました。

ここからが本題でして、、

煩わしいセットアップとか動作確認とか、何にも考えずに済ませてしまいたい所ではありますが・・・

さて今回のお題は、マイコン(ESP32)とのシリアル通信です。

マイコン側で何か喋らせて、PCで受信、またはその逆な感じのものを動かしてみようかと。

USBシリアルでは速度に限界がありますが、それでも簡単なデータロガー的な使い方が出来たらいいなという感じで。

Processing / VSCode に Serialライブラリの導入

さてProcessingでシリアル通信を行うには、Serialライブラリを導入する必要がある様です。

前回外部ライブラリも問題なく使える事を確認していますので、これからは外部ライブラリもどんどん導入していくつもりでしたが、、

幸いな事に(?)Serialは基本ライブラリの様ですので、import文を書いてやればそのまま使えるみたい。

なんか準備して損した気分(ぇ

シリアル通信のサンプルを動かしてみる

ここにまんまポートを調べてデータを送り付けるサンプルが書いてありましたので、ちょっとコピペ実行してみます。


// Example by Tom Igoe

import processing.serial.*;

// The serial port:
Serial myPort;       

// List all the available serial ports:
printArray(Serial.list());

// Open the port you are using at the rate you want:
//myPort = new Serial(this, Serial.list()[0], 9600);
myPort = new Serial(this, Serial.list()[0], 115200);        // ESP32用に9600→115200

// Send a capital A out the serial port:
myPort.write(65);

一個目のポートにデータ「65」を送信してそうな感じです。

ProcessingSerialTest01

ターミナルに使えそうなCOMポートのリストが出てきました。

Processingのサンプルではこういう便利っぽいメソッドがちょいちょい散見されますね。

内部ではちゃんとデータを送信しているのでしょうけれど

PC側ではこのCOMポートを掴んでますし、確かめようが無いですな、、、

面倒ですがESP32にシリアルを受け取ってもらう事にします。

Processingから送られたシリアルデータをESP32で受け取る

コードはこんな感じに簡潔に参ります。

ちゃちゃっと済ます為に、今回もAdafruitのSSD1306ライブラリを使わせて頂いております。

#include <Arduino.h>
#include <Wire.h>

#define I2C_Freq 4000000UL // I2C 周波数
#define I2C_Normal 100000UL // I2C 初期値

#define ADDRES_OLED 0x3C

// -------------------------------------------------------------------------------
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, I2C_Freq, I2C_Normal);
// -------------------------------------------------------------------------------

void setup()
{

    while (!Serial);
    //  Serial.begin(9600);
    Serial.begin(115200);

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    //  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3D))
    if (!display.begin(SSD1306_SWITCHCAPVCC, ADDRES_OLED))
    { // Address 0x3D for 128x64
        Serial.println(F("SSD1306 allocation failed"));
        for (;;)
            ; // Don't proceed, loop forever
    }
    display.clearDisplay();
    display.display();

    display.setCursor(0, 0);
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.println("-- Serial monitor --");
    display.println(" display under here.");
    display.display();

}

void loop()
{
    if (Serial.available())
    {
        uint8_t inputchar = Serial.read();

        display.setTextSize(1);
        display.setTextColor(WHITE);

        // 下までいったら消してしまう
        if (display.getCursorY() >= 64)
        {
            display.setCursor(0, 16);
            display.writeFillRect(0, 15, 127, 63, BLACK);
            display.display();
        }

        display.print(inputchar);
        display.print(" ");
        display.display();

    }

    delay(1);
}

本当は画面をスクロールさせたかったのですが、どうやらSSD1306コントローラは縦スクロールはサポートされていない様でした。

ソフトで作るのも面倒ですので垂れ流しな感じで・・・

代わりに画面にちょっとタイトルとかつけて格好をつけてみたり

いぇ、、、

ほらなんか色が違う所に混ぜたく無いっていうか・・・

折角なのでProcessing側も、一個と言わずもっと沢山データを送ってもらう様にします。


// Example by Tom Igoe

import processing.serial.*;

// The serial port:
Serial myPort;

// List all the available serial ports:
printArray(Serial.list());

// Open the port you are using at the rate you want:
myPort = new Serial(this, Serial.list()[0], 115200);        // ESP32用に9600→115200

delay(1000);

// Send a capital A out the serial port:
for (int i = 0; i < 256; ++i) {
    myPort.write(i);
    delay(20);
}

0~255の数字を1バイトづつディレイ20msを噛ませて送信してみます。

さてちゃんと届くかどうかチェックをば

先にESP32の方の電源を入れておき、シリアルポートを掴んだらProcessingの方を実行します。

なんか一応ちゃんと送られてるっぽいですね

沢山データを送る様な時にはコントロールヘッダを付けるとか何か工夫が要るかもですが、ひとまずはこれで。

シンプルな方が何か問題があった時に対処しやすいですしおすし

駄菓子菓子

データを受け取り終わった頃にESP32にリセットがかかってしまいます。

ナンデヤネン

ちょっと調べましたら、Arduino的な仕様でDTSがHighからLowになるとリセットがかかるんだとかなんだとか。

プログラムを書き込む時にかかるリセットみたいな感じですん

更にProcessing側でもシリアル通信をする時に自動でDTSをゴニョゴニョするそうで。

まぁ普通はそうなんでしょうけれど

これを強制OFFも出来るには出来るみたいですが、そうすると後でプログラムを書き込む時に面倒な事になるのでこのままで・・・

ESP32側からもProcessingにデータを送り、Processingで受け取る

今度は逆に、ESP32側からもProcessingにデータを送りつけてProcessingで表示させてみます。

ESP側ではProcessingから受け取ったシリアルデータを、文字列をつけて送り返します。

#include <Arduino.h>
#include <Wire.h>

#define I2C_Freq 4000000UL // I2C 周波数
#define I2C_Normal 100000UL // I2C 初期値

#define ADDRES_OLED 0x3C

// -------------------------------------------------------------------------------
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, I2C_Freq, I2C_Normal);
// -------------------------------------------------------------------------------

void setup()
{

    while (!Serial);
    //  Serial.begin(9600);
    Serial.begin(115200);

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    //  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3D))
    if (!display.begin(SSD1306_SWITCHCAPVCC, ADDRES_OLED))
    { // Address 0x3D for 128x64
        Serial.println(F("SSD1306 allocation failed"));
        for (;;)
            ; // Don't proceed, loop forever
    }
    display.clearDisplay();
    display.display();

    display.setCursor(0, 0);
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.println("-- Serial monitor --");
    display.println(" display under here.");
    display.display();
}

void loop()
{
    if (Serial.available())
    {
        uint8_t inputchar = Serial.read();

        display.setTextSize(1);
        display.setTextColor(WHITE);

        // 下までいったら消してしまう
        if (display.getCursorY() >= 64)
        {
            display.setCursor(0, 16);
            display.writeFillRect(0, 15, 127, 63, BLACK);
            display.display();
        }

        display.print(inputchar);
        display.print(" ");
        display.display();

        // 反撃の狼煙
        Serial.print("Recieved Serial :");
        Serial.println(inputchar);

    }

    delay(1);
}

2行しか追加していませんが・・・( ノД`)

更にProcessing側では、送り返されたデータをシリアルモニタ風に表示してみます。

import processing.serial.*;
import controlP5.*;

Serial myPort;
ControlP5 cp5;
Textarea rcv_textarea;

void setup() {
    size(800, 600);     // ウインドウサイズ
    background(255);    // 背景色を設定

    // 文字いれとく
    PFont font = createFont("MS Pゴシック", 14);    // フォントを指定してインスタンスを作成
    textFont(font);                         // フォントに反映

    fill(0);            // 描画色
    textSize(24);       // テキストサイズを設定
    text("--- 簡易シリアルモニタ的な何か ---", 30, 30);  // テキストを描画

    // controlp5 インスタンス作成
    cp5 = new ControlP5(this);
    // ボタンでも
    cp5.addButton("sendbtn")
        .setLabel("Send to Serial")
        .setPosition(640, 10)
        .setFont(createFont("MS Pゴシック", 16))
        .setSize(150, 50)
        ;

    // テキストエリアを作る
    rcv_textarea = cp5.addTextarea("serial_textarea")
        .setPosition(5,65)
        .setSize(790,530)
        .setLineHeight(14)
        .setFont(createFont("MS Pゴシック", 20))
        .setColorBackground(0)
        .setColor(color(255))
        .setScrollBackground(color(100, 100, 255))
        .setScrollForeground(color(200, 200, 255))
        ;

    // シリアルポートインスタンスを作成
    printArray(Serial.list());
    myPort = new Serial(this, Serial.list()[0], 115200);        // ESP32用に9600→115200
}

void sendbtn() {
    // ボタンを押したらシリアルで数字を送り付ける
    for (int i = 255; i > -1; i--) {
        myPort.write(i);
        delay(1);
    }

}

void recievedSerial() {
    // 受け取った内容をそのまま書き込む 
    String inBuffer = myPort.readString();   
    if (inBuffer != null) {
        rcv_textarea.setText(rcv_textarea.getText() + inBuffer);
        // スクロールが来たらそのポジションにいく?よく判らない
        rcv_textarea.scrolled(1);
    }
}

void draw() {
    // シリアルを受け取ったら画面表示に飛ばす
    if (myPort.available() > 0) {
        recievedSerial();
    }
}

controlP5のTextareaが何だか良さそうでしたので、ささっと画面を作ってみました。

こういうところは本当に便利ですねぇ

ボタンとかテキストエリアとかにつけた名前で関数を作ると勝手にイベントでトリガしてくれる所とか、なんて言うかラクチン杉てワハー💛

Processingに設置した「SEND TO SERIAL」ボタンをクリックすると、ESP32側にデータが256個送信されます。

今度は255→0の順番で送信してみました。

ESP32側ではこれを受け取って、「Recieved Serial:」を付けてProcessing側に送り返します。

送り返されたProcessingはこれをTextareaにガーっと表示していきます。

データの流れは、Processing → ESP32 → Processing の順番です。

こちらも無駄にボタンの位置を調整したり、タイトルつけてみたりして格好を・・・

中身はともかく「らしさ」は大事

ではちょっとポチってみますか

Processing側はWindowsPCですので受け取りパワーに余裕があるっぽいですが、ESP32側はLCDの速度がイマイチなのか少し遅れていますね

それでもデータは取りこぼし無くちゃんと送受信出来ている様でした。

Procesingは描画が得意みたいですから、そのうちプロッターみたいな事をさせてみたいですぬ

ひとまず滞りなく出来たという事で、めでたしめでたし

こういったデータのやり取りが出来る様になると、マイコンを使う魅力も鰻登りですn

今回使ってみたProcessing側のcontrolP5ライブラリとTextareaは、想像以上に使い勝手が良さそうな。

行が増えたら勝手にスクロールバーまで出て来るので、遡ってスクロールして見る事ができます。

また、余談ですがESP32側でリセットがかからなくなりました。

何故かサッパリ判りませんが、、

変わった事といえば、Processing側の送信部分がサブルーチンの下に入った事位か。

ベタ書きとサブルーチン下での実行は、内部で何かが違うのかもしれませんね・・・

それが何かを突き止めるのは面倒そうなので、やりません

誰かご存知の方がいらっしゃったら教えてくだしあ( ノД`)