PR

ESP32-WROOM-32Eでシリアル通信を実装する

組み蟌み゚ンゞニア
本蚘事はプロモヌションが含たれおいたす。

こんにちは、ENGかぎです。

Arudinoの暙準ラむブラリであるSerialを䜿甚するず倖郚機噚ずシリアル通信ができたす。ESP32-WROOM-32Eは耇数のシリアル通信ポヌトを持っおいるためシリアルモニタず倖郚機噚ず接続するシリアル通信を区別するこずができたす。

ESP32-WROOM-32Eのシリアル通信を䜿っお電文を倖郚機噚であるPICマむコンに送信しPICマむコンを操䜜したす。PICマむコンはPIC16F1827を䜿甚し䞋蚘蚘事ず同じ条件ずしたす。

PICマむコン(PIC16F1827)のシリアル通信の応甚

ESP32-WROOM-32E開発ボヌド秋月電子を䜿甚しArduino IDEで開発を行いたす。たた、AE-SHT35秋月電子を䜿甚しおいたす。

ESP32-WROOM-32Eを䜿っお動䜜確認したこずをたずめおいたす。

ESP32-WROOM-32Eで孊べる゜フト開発ず暙準ラむブラリの䜿い方

シリアル通信を実装する

ESP32-WROOM-32EにおいおArduino暙準ラむブラリに搭茉されおいるシリアル通信は3ポヌト分準備されおいたす。2024幎6月䞭旬でのデフォルトピンは以䞋の通りです。

シリアルポヌトGPIO番号シルク印刷
Serial送信ピンTX 受信3ピンRX
Serial1送信27ピン27 受信26ピン26
Serial2送信25ピン25 受信4ピン4
Arduinoラむブラリで搭茉されおいるシリアルポヌトのデフォルトピン

今回はシリアルモニタ甚にSerialず倖郚機噚ず通信するためSerial2を䜿甚したす。ラむブラリの曎新によっおデフォルトピンが倉曎になるこずがあるので泚意が必芁です。

暙準ラむブラリを䜿甚する方法

void setup() {

  Serial.begin(115200);
  while(!Serial); //ポヌトを開くたでの埅ち
  Serial1.begin(19200);//デフォルトピンを䜿甚する堎合
  Serial1.begin(119200,SERIAL_8N1,18,19);//ピンを指定しお䜿甚する堎合
  Serial2.begin(19200);//デフォルトピンを䜿甚する堎合
  Serial2.begin(19200,SERIAL_8N1,16,17);//ピンを指定しお䜿甚する堎合
  while(!Serial2); //ポヌトを開くたでの埅ち
}

シリアル通信の起動はbegin()で行いたす。第1匕数に通信速床であるボヌレヌト倀を指定したす。

匕数にシリアル通信のボヌレヌトを入力したす。ボヌレヌトは任意でも良いのですが、マむコンのクロック呚波数によっお誀差が出るので泚意が必芁です。

ピンを指定しお初期化する堎合は第2匕数以降を指定したす。第2匕数はデヌタ長及びパリティを指定したす。デフォルトはSERIAL_8N1デヌタビット8、パリティなし、ストップビット1です。第3匕数にRX受信に䜿甚するピンを指定したす。第4匕数にTX送信に䜿甚するピンを指定したす。

Arduino IDEで遞択できるボヌレヌトから遞択するず倚くの堎合問題になりたせんが、ボヌレヌトを早くしすぎるず配線の長さや呚蟺回路の郚品などの圱響で通信できなくなるこずがあるので倖郚機噚のクロックを含めお怜蚎する必芁がありたす。

ボヌレヌト以倖の蚭定は特に指定しない堎合はデヌタ長は8ビット・パリティなしの条件で動䜜したす。

begin()を発行した埌ポヌトが開く初期蚭定埅ちたでルヌプさせお確実に蚭定が完了するたで埅機させるこずもありたすが、初期化時に他で遅延させおいる堎合やbegin()の発行から盎ちにシリアル通信をしない堎合は実装しなくおも特に問題になるこずはありたせん。

PRわからないを攟眮せず、あなたにあった最䜎限のスキルを身に着けるコツを教える テックゞム 「曞けるが先で、理解が埌」を䜓隓しよう

SerialずSerial2の䜿い分け

Serialはシリアルモニタに衚瀺したい堎合に䜿甚したす。Serial2で受信したデヌタをシリアルモニタに衚瀺したい堎合など䜿い分けるこずで効率よくデバッグできるようになりたす。

void loop() {
  //受信デヌタをモニタする䟋
  while( Serial2.available()){ //Serial2に受信デヌタがあるか
    data = Serial2.read(); //Serial2デヌタを読み出し
    Serial.write(data); //シリアルモニタにSerial2から読み出したデヌタをモニタ衚瀺
  }
}

倖郚機噚からシリアル通信のデヌタを受信するずSerial2.available()の条件を満たすためSerial2.read()によっおSerial2のデヌタを読み出したす。受信したデヌタをSerial.write()によっおシリアルモニタに衚瀺しおいたす。

Serialでよく䜿うAPI

Serialでよく䜿うAPIをたずめたした。Serialで衚蚘しおいたすがSerial2でも同様に䜿甚できたす。

API説明䜿甚䟋
Serial.write()バむナリデヌタを送信するSerial.write(0x30)
Serial.write(匕数1,匕数2)匕数1に察象バッファのアドレス
匕数2に送信するバむト数
Serial.write(&buf,10)
Serial.read()受信デヌタをバむトず぀取り出すavailable()ずセットで䜿う
buf = Serial.read()
Serial.availale()受信したデヌタ数を確認し、デヌタがある堎合
は0よりも倧きな倀ずなる。
if(Serial.available() ){ }
Serial.print()文字ずしお送信するSerial.print(0x10)
Serial.println()改行コヌドを぀けお文字ずしお送信するSerial.println(0x10)
Serialでよく䜿うコマンド

Serial.print(0x10)はバむナリデヌタでは16であるためモニタ䞊では16が衚瀺されたす。Serial.write(0x30)の堎合は0x30がアスキヌコヌドテキストデヌタでは0にあたるためモニタには0が衚瀺されたす。

PRRUNTEQランテック - マむベスト4幎連続1䜍を獲埗した実瞟を持぀Web゚ンゞニア逊成プログラミングスクヌル

送受信に䜿甚する電文

ESP32-WROOM-32Eから送出した電文でPIC16F1827を操䜜したす。電文の構成は任意で構成するこずができたすが双方で䜿甚する電文の構成を同じ構成にするず管理しやすくなりたす。

電文の構成

電文の構成
電文の構成

電文の構成は1バむト目をヘッダヌずしデヌタの先頭であるこずを通知したす。2バむト目には3バむト以降にセットするデヌタのサむズを瀺すデヌタ長をセットしたす。電文の最終バむトにはデヌタ長以降のデヌタの総和を蚈算したチェックサムを付加したす。

ヘッダヌの郚分の文字コヌドが’P’の堎合はPIC16F1287ぞの電文ずし’A’であればESP32-WROOM-32Eぞ送出する電文ずしお区別したす。

チェックサムはデヌタ郚分の総和をずりバむト分のデヌタをセットしたす。バむトの倧きさ(255)を超えおも䞋䜍のバむトをセットしたす。チェックサムをuint8_tで宣蚀しおおくずオヌバヌフロヌしおバむトデヌタずなりたす。

䟋ではデヌタ1からデヌタ4ずしおいたすが任意のデヌタずするこずで様々なパタヌンに察応する凊理を䜜るこずができたす。

広告

電文を受信する

while( Serial2.available()){
  esp32Rcv.buf[esp32Rcv.wp] = Serial2.read(); //デヌタリヌド
            
  if( ++esp32Rcv.wp >= sizeof(esp32Rcv.buf)){
      esp32Rcv.wp = 0;
  }
}

Serial2でデヌタを受信するずavailable()が条件を満たし内郚の凊理が行われたす。read()で受信したデヌタを読み蟌みesp32Rcvに栌玍したす。esp32Rcv.wpを曎新しお次に栌玍する堎所を指定しながらデヌタを䞀時保管しおいきたす。

While()で受信デヌタを確認しおいるのはメむン凊理に来るたびに受信しおいるデヌタ分だけ確実に取埗するためです。if()で確認するよりもタむミングによっおは受信デヌタの栌玍が早くなるこずがありたす。

受信デヌタのチェック

受信デヌタが電文の構成に合臎しおいるかをチェックしたす。

if( esp32Rcv.buf[ rp ] != HEADER_A ){ //ヘッダヌが䞀臎するか
    ReadPointerAdd(); //読み蟌み䜍眮の曎新
}
else{
  if( rxsz >= 2 ){ //デヌタ長分だけ獲埗しおいるか
    //デヌタ長を確定する             
    if( rxsz >= allsz){ //確定したサむズ以䞊か                
      for( i=0; i < allsz; i++ ){
        Rcvdata[i] = esp32Rcv.buf[esp32Rcv.rp]; //䞀時保管したデヌタを移す
        ReadPointerAdd(); //読み蟌み䜍眮の曎新
      }                       
      sumchk = CalcSum(&Rcvdata[2], datsz); //チェックサムの蚈算
      if( sumchk == Rcvdata[allsz-1]){ //チェックサムの確認
        Serial.write(&Rcvdata[2],4); //文字列郚分をシリアルモニタに衚瀺
      }
    }
  }               
}

最初にヘッダヌを確認したす。ESP32-WROOM-32EはPIC16F1827からヘッダヌずしお’A’0x41を受信したす。ヘッダヌが䞀臎した堎合は2バむト目のデヌタ長が受信できおいるかの確認を行いたす。

2バむト目のデヌタ長によっお電文党䜓のサむズが確定できるため受信デヌタを電文保管甚のバッファ(Rcvdata[])に移し替えたす。Rcvdata[]のチェックサムを蚈算しチェックサムが䞀臎するか確認したす。チェックサムに問題がなければ電文ずしお受け入れるためモヌドを進めお凊理を行いたす。

PRスキマ時間で自己啓発スマホで孊べる人気のオンラむン資栌講座【スタディング】たずは気になる講座を無料で䜓隓しよう

動䜜確認

ESP32-WROOM-32EずPIC16F1827を接続しおシリアル通信の動䜜確認を行いたす。通信条件はボヌレヌトが19200bps、パリティなしずしたす。

シリアル通信の動䜜確認回路
シリアル通信の動䜜確認回路

ESP32-WROOM-32EはSerial2Serial2のRX2、TX2のデフォルトピンがラむブラリの倉曎により倉わっおいるためbegin()関数でデヌタビット及びピンを指定しお初期化を行っおいたす。を䜿甚しおPIC16F1827ず通信を行いたす。SW1を抌すずESP32-WROOM-32Eから電文を送出したす。SW1を抌すごずに電文を切り替えお送信したす。

PIC16F1827はESP32-WROOM-32Eから受信した電文に応答しおLED1を点灯/消灯したす。LED1の点灯/消灯に連動しお点灯させた堎合は「OK-1」、消灯させた堎合は「OK-2」を含む電文をESP32-WROOM-32Eに返信したす。

シリアルモニタによる動䜜確認の結果
シリアルモニタによる動䜜確認の結果

SW1を抌すずLED1が点灯/消灯するこずを確認したした。たたLED1の点灯/消灯に連動しおシリアルモニタに返信デヌタが衚瀺されるこずが確認できたした。

スポンサヌリンク

゜ヌスコヌド党䜓

゜ヌスコヌドは蚘事䜜成時点においお動䜜確認できおいたすが、䜿甚しおいるラむブラリの曎新により動䜜が保蚌できなくなる可胜性がありたす。たた、゜ヌスコヌドを䜿甚したこずによっお生じた䞍利益などの䞀切の責任を負いかねたす。参考資料ずしおお䜿いください。

#define HEADER_A 'A'
#define HEADER_P 'P'
#define RING_SZ 64
#define DIFILT_MAX 4
#define TIME_UP 0
#define TIME_OFF -1
#define TIM_DIFILT 1
#define TIM_RX_WAIT 5
#define OFFSET_SZ 3
#define PIN_DI_SW 5

typedef struct DIFILT{
  uint8_t wp;
  uint8_t buf[DIFILT_MAX];
  uint8_t di1;
};

typedef struct{
    uint8_t wp;
    uint8_t rp;
    uint8_t buf[RING_SZ];
}RING_MNG;

/* 倉数宣蚀 */
DIFILT diData;
RING_MNG esp32Rcv;
uint8_t Rcvdata[RING_SZ];
hw_timer_t *timer = NULL;
int16_t timDifilter = TIME_OFF;
int8_t  timRxWait = TIME_OFF;
uint8_t buf[]={HEADER_P,0x04,0x30,0x31,0x32,0x33,0x00}; //電文送出パタヌン1
uint8_t buf2[]={HEADER_P,0x04,0x33,0x32,0x31,0x30,0x00}; //電文送出パタヌン2
bool btn1hold;
bool btncnt;

/*  プロトタむプ宣蚀 */
uint8_t CalcSum(uint8_t *buf, uint8_t sz );
void Rcvmain(void);
void ReadPointerAdd(void);
void IRAM_ATTR onTimer(void);
void mainApp(void);
void mainTimer(void);
void DiFilter(void);

void setup() {
  uint8_t i;

  pinMode(PIN_DI_SW,INPUT_PULLUP);
  
  Serial.begin(115200);
  //Serial2.begin(19200);//デフォルトピン倉曎の為
  Serial2.begin(19200,SERIAL_8N1,16,17);//ピン番号を指定しお初期化

  timer = timerBegin(0,80,true); //クロック80MHzを80で分呚するので1usが最小分解胜
  timerAttachInterrupt(timer, &onTimer,true);
  timerAlarmWrite(timer,10000,true);  //コヌルバックするタむミングを指定 10msごずにonTimer()がコヌル
  timerAlarmEnable(timer); //timer通知を開始

  timDifilter = TIM_DIFILT;
  while( i < 10){
    DiFilter();
    delay(10);
    i++;
  }
}

void loop() {

  mainApp();
  DiFilter();
  Rcvmain();
}
/* タむマ割り蟌みによっおコヌルする関数 */
void IRAM_ATTR onTimer(void){ //IRAMセクションに割り圓おるずRAM領域ずなり高速ずなる
  mainTimer();
}

/* タむマ管理 */
void mainTimer(void){

  if( timDifilter > TIME_UP ){
    --timDifilter;
  }
}
/* メむン関数 */
void mainApp(void){

  if( diData.di1 == 0){
    if( btn1hold == false ){
      btn1hold = true;

      if( btncnt == false){
        btncnt = true;
        buf[6] = CalcSum(&buf[2],4); //sumの蚈算
        Serial2.write(&buf[0],7); //デヌタ送信
      }
      else{
        btncnt = false;
        buf2[6] = CalcSum(&buf2[2],4); //sumの蚈算
        Serial2.write(&buf2[0],7); //デヌタ送信
      }
    }
  }
  else{
    btn1hold = false;
  }

  while( Serial2.available()){
    esp32Rcv.buf[esp32Rcv.wp] = Serial2.read(); //デヌタリヌド
            
    if( ++esp32Rcv.wp >= sizeof(esp32Rcv.buf)){
      esp32Rcv.wp = 0;
    }
  }
}
/* DIフィルタ */
void DiFilter(void){
  bool boo = true;
  uint8_t i;

  if( timDifilter == TIME_UP ){
    timDifilter = TIM_DIFILT;

    diData.buf[diData.wp] = digitalRead(PIN_DI_SW);

    for( i=1; i < sizeof(diData.buf);i++){
      if( diData.buf[i - 1] != diData.buf[i]){
        boo = false;
      }
    }

    if(boo){ //デヌタがすべお䞀臎なので採甚する
      diData.di1 = diData.buf[0];
    }
    if( ++diData.wp >= sizeof(diData.buf)){
      diData.wp = 0;
    }
  }
}
/* 受信デヌタの凊理 */
void Rcvmain(void){
  int8_t  rxsz;
  uint8_t sz;
  uint8_t datsz;
  uint8_t allsz;
  uint8_t rp = esp32Rcv.rp;  
  uint8_t i;
  uint8_t sumchk;
  uint8_t dat[2];
    
  if( timRxWait == TIME_UP){
    timRxWait = TIME_OFF;
    ReadPointerAdd();    
  }
    
  rxsz = esp32Rcv.wp - esp32Rcv.rp; //受信デヌタ数の算出
    
  if( rxsz < 0 ){
    rxsz = rxsz + sizeof(esp32Rcv.buf);
  }          
  if( rxsz == 0 ){
    timRxWait = TIME_OFF;
  }
  else{
    if( timRxWait == TIME_OFF ){
      timRxWait = TIM_RX_WAIT;
    }

    if( esp32Rcv.buf[ rp ] != HEADER_A ){ //ヘッダヌの確認
      ReadPointerAdd(); 
    }
    else{
      if( rxsz >= 2 ){
        for( i = 0; i < 2; i++){//デヌタサむズ算出のため仮おき
          dat[i] = esp32Rcv.buf[ rp ];
          if(++rp >= sizeof(esp32Rcv.buf) ) rp = 0;
        }
                                    
        if( dat[1] > sizeof(esp32Rcv.buf) - OFFSET_SZ ){
          allsz = sizeof(esp32Rcv.buf) - OFFSET_SZ;
        }
        else{
          datsz = dat[1];
          allsz = dat[1] + OFFSET_SZ; //header,datalengh,sumを含む
        }

        if( rxsz >= allsz){
          timRxWait = TIME_OFF;
                    
          for(i=0; i < sizeof(Rcvdata); i++){
            Rcvdata[i] = 0;
          }
                    
          for( i=0; i < allsz; i++ ){
            Rcvdata[i] = esp32Rcv.buf[esp32Rcv.rp];
            ReadPointerAdd();
          }

          sumchk = CalcSum(&Rcvdata[2], datsz); 
          if( sumchk == Rcvdata[allsz-1]){ //チェックサムが䞀臎するか
            Serial.write(&Rcvdata[2],4); //文字列郚分をシリアルモニタに衚瀺
            Serial.println("");
          }
        }
      }
    }
  }
}
/* 読み蟌み䜍眮の曎新 */
void ReadPointerAdd(void){
    
  if(++esp32Rcv.rp >= sizeof(esp32Rcv.buf) ){
    esp32Rcv.rp = 0;
  }
}
/* チェックサムの蚈算 */
uint8_t CalcSum(uint8_t *buf, uint8_t sz ){
  uint8_t ret = 0;

  for(uint8_t i=0; i < sz; i++ ){
    ret += *buf;
    buf++; 
  }
  return ret;
}

Serial2のRX2、TX2のデフォルトピンがラむブラリの曎新により蚘事䜜成時より倉わっおいるためbegin()関数でデヌタビット及びピンを指定しお初期化を行っおいたす。

PIC16F1827の゜ヌスコヌドは䞋蚘蚘事のものを䜿甚しおいたす。

PICマむコン(PIC16F1827)のシリアル通信の応甚

関連リンク

Arduinoのラむブラリを䜿っお動䜜確認を行ったこずを䞋蚘リンクにたずめおいたす。

Arduinoで孊べるマむコンの゜フト開発ず暙準ラむブラリの䜿い方

Seeeduino XIAOで孊べる゜フト開発ず暙準ラむブラリの䜿い方

ESP32-WROOM-32Eで孊べる゜フト開発ず暙準ラむブラリの䜿い方

Raspberry Pi Picoで孊べる゜フト開発

最埌たで、読んでいただきありがずうございたした。

タむトルずURLをコピヌしたした