マイコンと接続する

Connect to microcontroller

実際にArduinoを使用してマイコンの無線化を行います。

シリアル通信を行う

PCからArudinoに入力したシリアルデータをTWELITE UARTを介して、もう一台のArduinoに無線で送信し、そのデータを解釈するサンプルです。

用意する電子部品

以下のものを2セット用意してください。

  1. TWELITE UART

  2. Arduino UNO

  3. 抵抗 2.2kΩ(赤・赤・赤)

  4. 抵抗 3.3kΩ(橙・橙・赤)

配線

Arudino UNOは5V系のマイコンで、UART出力のHiレベルが5Vです。TWELITEに5Vを入力するとTWELITEが破損するため、分圧してHiレベルが3Vになるようにしております。

m5StackやRaspberry PiなどのようにHiレベルが3Vのマイコンなどに接続する場合はTWELITEに直接接続してください。

設定

TWELITE UARTをTWELITE R2などを使用して、以下のような設定にしてください。

--- CONFIG/TWE UART APP V1-04-5/SID=0x82018ca0/LID=0x78 -- ---
 a: set Application ID (0x67720103)
 i: set Device ID (120=0x78)
 c: set Channels (18)
 x: set RF Conf (3)
 r: set Role (0x0)
 l: set Layer (0x1)
 b: set UART baud (38400)
 B: set UART option (8N1)
 m: set UART mode (E)
 k: set Tx Trigger (sep=0x0d0a, min_bytes=0 dly=0[ms])
 h: set header format [;%A;%q;*;\n]            <- ここを変更(書式の変更)
 C: set crypt mode (0)
 o: set option bits (0x00010100)               <- ここを変更(ボーレートの変更)
---
 S: save Configuration
 R: reset to Defaults

サンプルSketch

Arduino IDEを起動させ、以下のプログラムをコピー&ペーストし、Arduinoに書き込んでください。

#include <SoftwareSerial.h>

SoftwareSerial MWSerial(2, 3); // RX, TX

#define BLOCK_MAX 10

char *p;
char buf[256];

// データ列を区切り文字で分割してString型の配列に入れる。
int split( char* source, char delimiter, String* result ){
  char* p = source;
  char tmp[81];
  memset(tmp, 0, sizeof(tmp));
  int n = 0;
  int block = 0;

  // ヌル文字が来るか分けたブロック数がBLOCK_MAXになったらやめる。
  while( *p != 0 && block < BLOCK_MAX ){

    // 区切り文字が来たら、String型に変換する。
    if( *p == delimiter ){
      // 分割したデータが0バイトの時は何もしない。
      if( n > 0 ){
        // 念のためヌル文字を末尾に代入。
        tmp[n] = 0;
        result[block] = String(tmp);
        block++;

        n = 0;
        memset(tmp, 0, sizeof(tmp));
      }
    }else{
      tmp[n] = *p;
      n++;
    }
    p++;
  }

  return block;
}

void setup() {
  Serial.begin(38400);
  MWSerial.begin(38400);

  p = buf;
  memset(buf, 0, sizeof(buf));
}

void loop() {
  while (MWSerial.available()) {
    char before = *p;
    *p = MWSerial.read();

    if( before=='\r' && *p=='\n' ){
      *p = 0;                  // 末尾をNull文字にする
      String str[BLOCK_MAX];   // 区切り文字で分割した文字列を入れる
      int len = p-buf;         // シリアルで読み込んだデータ量の計算
      int block = split( buf, ';', str );  // データの分割

      if( block == 3 ){
        Serial.print("Message: ");
        Serial.println(buf);
        Serial.print("Serial No.: ");
        Serial.println(str[0]);
        Serial.print("LQI: ");
        Serial.println(str[1]);
        Serial.print("Data: ");
        Serial.println(str[2]);
        Serial.println();
      }
      
      p = buf;
      memset(buf, 0, sizeof(buf));
    }else{
      if( *p >= 0x20 ){
        p++;
      }
    }
  }

  while(Serial.available()){
    MWSerial.write(Serial.read());
  }
}

使用方法

Arduino IDE のシリアルモニターなどを使用して、送信側のArduinoにメッセージを入力(ここではHELLO)すると受信側のArduinoから以下のようなメッセージがシリアル出力されます。

送信するメッセージには必ず最後に改行コード(CR+LF)をつけてください。

改行コードをつけないとデータが送信されません。

[送信側]
HELLO                                       <- 入力
Message: ;820109F1;000;12,1,HEL...;         <- 出力
Serial No.: 820109F1                        <- 出力
LQI: 000                                    <- 出力
Data: 12,1,HEL...                           <- 出力


[受信側]
Message: ;820109F1;177;HELLO;               <- 出力
Serial No.: 820109F1                        <- 出力
LQI: 177                                    <- 出力
Data: HELLO                                 <- 出力

Sketchの解説

メインループ

下記の関数がシリアル通信の処理を記述したメインループ関数です。

2~30行目ではTWELITEからのシリアル出力を読み込む処理を行っております。 TWELITEからCR+LFが来るまでは内部のバッファに受信したシリアル電文をためておき、CR+LFが来たらsplit()関数で保持していたデータを分割し、PCにシリアル出力します。

32~34行目はPCから来たシリアルデータをそのままTWELITEに送ります。

void loop() {
  while (MWSerial.available()) {
    char before = *p;
    *p = MWSerial.read();

    if( before=='\r' && *p=='\n' ){
      *p = 0;                  // 末尾をNull文字にする
      String str[BLOCK_MAX];   // 区切り文字で分割した文字列を入れる
      int len = p-buf;         // シリアルで読み込んだデータ量の計算
      int block = split( buf, ';', str );  // データの分割

      if( block == 3 ){
        Serial.println(buf);
        Serial.print("Serial No.: ");
        Serial.println(str[0]);
        Serial.print("LQI: ");
        Serial.println(str[1]);
        Serial.print("Data: ");
        Serial.println(str[2]);
        Serial.println();
      }
      
      p = buf;
      memset(buf, 0, sizeof(buf));
    }else{
      if( *p >= 0x20 ){
        p++;
      }
    }
  }

  while(Serial.available()){
    MWSerial.write(Serial.read());
  }
}

文字列を分割する関数

split()は保持しているシリアルデータを区切り文字ごとに分割する関数です。

引数の配列を前から順番に中を確認し、区切り文字になったらそこまでの配列をString型に変換してて保管します。

分割が終わったら、分割した数を戻します。

もとのシリアルデータはString型ではなくchar型の配列で、分割後はString型になりますので、変数の方の取り扱いをご注意ください。

int split( char* source, char delimiter, String* result ){
  char* p = source;
  char tmp[81];
  memset(tmp, 0, sizeof(tmp));
  int n = 0;
  int block = 0;

  // ヌル文字が来るか分けたブロック数がBLOCK_MAXになったらやめる。
  while( *p != 0 && block < BLOCK_MAX ){

    // 区切り文字が来たら、String型に変換する。
    if( *p == delimiter ){
      // 分割したデータが0バイトの時は何もしない。
      if( n > 0 ){
        // 念のためヌル文字を末尾に代入。
        tmp[n] = 0;
        result[block] = String(tmp);
        block++;

        n = 0;
        memset(tmp, 0, sizeof(tmp));
      }
    }else{
      tmp[n] = *p;
      n++;
    }
    p++;
  }

  return block;
}

最終更新