PICマイコンと接続して使用するための予備知識2、M5 ATOM Lite2台で ”Host-Client bluetooth通信”

# ATOM Liteを2台使用して、bluetoothの双方向通信を行います。

![IMG_4863.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/75be5d29-1ac0-325b-1df7-fa2f2e54dfda.jpeg)

![全体図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/91672d0c-2f02-2348-6128-b23511b062f0.jpeg)

:::note info
**開発環境 VSCODE + PlatformIO**

使用したライブラリ
**FastLED by Daniel Garcia
M5Atom by M5Stack
LovyanGFX by lovyan03**
:::


Macアドレス探索で参考にさせてもらったブログ

https://rikoubou.hatenablog.com/entry/2022/05/27/161447

:::note
今回は、bluetoothのMacアドレスを指定して１対１の通信を行います。
※bluetooth Classicモードでは、マスターは複数接続できるはずです。
:::

:::note
マスター(Host)サイドで、ペアリング先のbluetoothデバイスを、bluetoothMacアドレスで指定します。

前もって、スレーブ(Client)サイドのmacアドレスをUSBシリアルに出力して、メモしておきます。
```CPP:Slave(client)側 自身のbluetooth Macアドレスを出力するコード
  uint8_t bt_mac[6];
  //bluetoothクライアントアドレスの表示
  esp_read_mac(bt_mac, ESP_MAC_BT);
  Serial.printf("%02X:%02X:%02X:%02X:%02X:%02X\n",
                 bt_mac[0], bt_mac[1], bt_mac[2], bt_mac[3], bt_mac[4], bt_mac[5]);
```

マスターサイドの接続関数で、相手先のmacアドレスを指定します。
```CPP:master(Host)側コード通信相手を探索、接続

    uint8_t ClientAtomlite_address[6]={0xA1,0xB1,0x09,0xC1,0x76,0xB8};
    //bluetoothデバイスサーチと接続
    //advertisingパケット受信。
    //Client 側のアドバタイズは停止していないはず。
    
    int times=0;
    bool connected;
    int btConnectCounter;

    //相手先探索
    do
    {
        connected = SerialBT.connect(ClientAtomlite_address);//10秒でタイムアウト
        Serial.printf("%d connected=%d ",times,connected);
        times++;
        if(times==30)
        {
            Serial.println("error:connect timeout");
            break;
        }
    }while(!connected);
```
相手先とのペアリングシーケンスが成功すれば、接続完了です。
:::

:::note info
# Master(Host)サイドのコード
**platformIOの設定と使用ライブラリ**
[env:m5stack-atom]
platform = espressif32
board = m5stack-atom
framework = arduino
lib_deps = 
	m5stack/M5Atom@^0.1.2
	fastled/FastLED@^3.7.0
	lovyan03/LovyanGFX@^1.1.16
upload_port = COM7

```CPP: Master側 main.cpp
/*
**********************************************************************************
M5 ATOM Lite 
SK6812 LED グラデーション表示
   ※using libratries=> M5ATOM, FastLED
**********************************************************************************
*/
#include <M5Atom.h>
#include "mySerialBT.hpp"
#include "myTask.hpp"

/// @brief セットアップ
void setup() 
{
  int i;

  // 本体初期化（UART有効, I2C無効, LED有効）
  M5.begin(true,false,true);
  delay(50);
  Serial.println("Start");
  uint8_t bt_mac[6];
  
  //自身のbluetooth macアドレス表示
  esp_read_mac(bt_mac, ESP_MAC_BT);
  Serial.printf("%02X:%02X:%02X:%02X:%02X:%02X\n",
                 bt_mac[0], bt_mac[1], bt_mac[2], bt_mac[3], bt_mac[4], bt_mac[5]);
  
  //起動を示すLED点滅
  for(i=0; i<=1; i++)
  {
    M5.dis.drawpix(0, 0x0F0000);
    delay(500);
    M5.dis.drawpix(0, 0x000000);
    delay(500); 
  }

  //bluetooth開始　Master MODE-----------------
  bluetoothMasterBegin(ClientAtomlite_address);
  
  //タスク開始---------------------
  setupRxTask();
  
}

int counter;
/// @brief メインループ
void loop() 
{
  M5.dis.drawpix(0, 0x00000F);
  SerialBT.printf("cnt=%d\n",counter++);
  delay(500);
  M5.dis.drawpix(0, 0x0F0F00);
  SerialBT.printf("cnt=%d\n",counter++);
  delay(500); 
}


```

```CPP: Master側 mySerialBT.hpp
#ifndef MYSERIALBT_HPP
#define MYSERIALBT_HPP

#include <BluetoothSerial.h>

extern uint8_t ClientAtomlite_address[6];

extern BluetoothSerial SerialBT;
extern void bluetoothMasterBegin(uint8_t *address);
#endif
```

```CPP: Master側 mySerialBT.cpp
#include "mySerialBT.hpp"

BluetoothSerial SerialBT;

//相手先bluetooth MACアドレス
uint8_t ClientAtomlite_address[6]={0x??,0x??,0x??,0xA1,0xC6,0x13};


/// @brief bluetoothマスターシリアル通信初期化
/// @param address 
void bluetoothMasterBegin(uint8_t *address)
{
    //bluetoothモジュールの起動
    SerialBT.begin("M5 ATOM master", true);
    Serial.println("begin connect");
   
    //bluetoothデバイスサーチと接続
    //advertisingパケット受信。
    //ATOMLite側のアドバタイズは停止していない。
    int times=0;
    bool connected;
    int btConnectCounter;

    //相手先探索
    do
    {
        connected = SerialBT.connect(address);//10秒でタイムアウト
        Serial.printf("%d connected=%d ",times,connected);
        times++;
        if(times==30)
        {
            Serial.println("error:connect timeout");
            break;
        }
    }while(!connected);

    Serial.println("");
    delay(30);
    
    if (connected) 
    {//接続成功
        btConnectCounter++;
        Serial.printf("Connected Succesfully! %d\n\r",btConnectCounter);
    } else 
    {//接続失敗
        while (!SerialBT.connected(10000))
        {
            Serial.println("Failed to connect.");
        }
    }

}
```
```CPP: Master側 myTask.hpp
#ifndef MYTASK_HPP
#define MYTASK_HPP

#include <stdint.h>
#include <M5ATOM.h>

#define INTERVAL_TASK 33

extern void setupRxTask(void);
extern void bluetoothRXtask(void *arg);
#endif
```

```CPP: Master側 myTask.cpp
#include "myTask.hpp"
#include "mySerialBT.hpp"

void setupRxTask()
{
    xTaskCreate(bluetoothRXtask, "bluetoothRXtask", 2048, NULL, 1, NULL);
}

void bluetoothRXtask(void *arg) 
{
  while (1) 
  {
    if(SerialBT.available()>1)
    {
        String str = SerialBT.readStringUntil('\n');
        uint length = str.length();
        Serial.print("echo=> ");
        Serial.print(str);
        Serial.printf("length=%d\r",length);
    }
    vTaskDelay(INTERVAL_TASK / portTICK_RATE_MS);
  }
}
```
:::

:::note info
# Slave(Client)サイドのコード
**PlatformIOの環境と、使用ライブラリ**
[env:m5stack-atom]
platform = espressif32
board = m5stack-atom
framework = arduino
lib_deps = 
	fastled/FastLED@^3.7.0
	m5stack/M5Atom@^0.1.2
	lovyan03/LovyanGFX@^1.1.16
upload_port = COM19

```CPP: Slave側 mainClinet.cpp
#include <M5Atom.h>
#include "mySerialBT.hpp"
#include "myGraphics.hpp"
#include "myTask.hpp"

CRGB color[3]={0x000F00,0x00000F,0x000000};

void setup(void)
{
  uint8_t bt_mac[6];
  M5.begin(true,false,true);
  pinMode(22,OUTPUT); //Lチカ用

  //bluetoothクライアントアドレスの表示
  esp_read_mac(bt_mac, ESP_MAC_BT);
  Serial.printf("%02X:%02X:%02X:%02X:%02X:%02X\n",
                 bt_mac[0], bt_mac[1], bt_mac[2], bt_mac[3], bt_mac[4], bt_mac[5]);
  
  //bluetooth　クライアントとして初期化開始
  bluetoothClientBegin();
  
  //SSD1306初期化-----------------
  lcd.init();
  canvas.createSprite(lcd.width(), lcd.height());
  canvas.setTextWrap(true);      //自動折返し：無し
  canvas.fillScreen(TFT_BLACK);   //背景塗りつぶし
  canvas.setTextColor(TFT_WHITE); //文字色

  SerialBT.println("Start\n");
  //シリアル通信受信タスクスタート
  setUPserialRxTask();
}
 
void loop(void)
{
  //GP22のLEDと、本体LEDの点滅
  digitalWrite(22,HIGH);
  M5.dis.drawpix(0,color[0]);
  delay(500);

  digitalWrite(22,LOW);
  M5.dis.drawpix(0,color[1]);
  delay(500);
}
```

```CPP: Slave側 mySerialBT.hpp
#ifndef MYSERIALBT_HPP
#define MYSERIALBT_HPP

#include <BluetoothSerial.h>

extern BluetoothSerial SerialBT;    //bluetoothインスタンス
extern void bluetoothClientBegin(void);  

#endif
```
```CPP: Slave側 mySerialBT.cpp
#include "mySerialBT.hpp"

/// @brief bluetoothインスタンス
BluetoothSerial SerialBT;

/// @brief bluetooth初期化関数
/// @param address 
void bluetoothClientBegin(void)
{
    SerialBT.begin("M5 ATOM01",false);
    Serial.println("begin connect");
}
```

```CPP:Slave側 myTask.hpp
#ifndef MYTASK_HPP
#define MYTASK_HPP

#include <stdint.h>
#include <M5ATOM.h>

#define INTERVAL_TASK 33

extern void setUPserialRxTask(void);
extern void serialRxTask(void *arg);
#endif
```

```CPP:Slave側 myTask.cpp
#include "myTask.hpp"
#include "mySerialBT.hpp"
#include "myGraphics.hpp"

/// @brief シリアル通信受信タスク初期化
void setUPserialRxTask()
{
    xTaskCreate(serialRxTask, "serialRxTask", 2048, NULL, 1, NULL);
}

/// @brief Bluetoothシリアル通信受信タスク
/// @param arg 
void serialRxTask(void *arg) 
{
  while (1) 
  {
    if(SerialBT.available()>1)
    {
        String str = SerialBT.readStringUntil('\n');
        uint length = str.length();
        SerialBT.print("echo=> ");
        SerialBT.print(str);
        SerialBT.printf("length=%d\n",length);
        Serial.printf("%s\n",str);
        //SSD1306 表示------------------------------------
        canvas.startWrite();//通信を開始する。（ペリフェラルを占有する）
        canvas.fillScreen(TFT_BLACK);    // 背景塗り潰し
        canvas.setTextSize(1);
        canvas.setCursor(0,0, &Font4);
        canvas.println("BT Client");
        canvas.setCursor(0,20, &Font4);
        canvas.printf("%s\n",str);
        canvas.pushSprite(0,0);
        canvas.endWrite();//通信を終了する。（ペリフェラルの占有を終了する)
    }
    vTaskDelay(INTERVAL_TASK / portTICK_RATE_MS);
  }
}
```

```CPP:Slave側 myGraphics.hpp
#ifndef __MYGRAPHICS_HPP__
#define __MYGRAPHICS_HPP__

#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <Wire.h>

class LGFX_SSD1306 : public lgfx::LGFX_Device
{
  lgfx::Panel_SSD1306 _panel_instance;  // SSD1306
  lgfx::Bus_I2C       _bus_instance;    // バス設定用構造体

  //コンストラクタ
  public:
    LGFX_SSD1306()
    {
      //I2Cバスクラス構造体
      {
        auto cfg = _bus_instance.config();  //I2Cバス構造体インスタンスの取得
        cfg.i2c_port = 1; //Wire1を選択
        cfg.freq_write  = 400000;     // 送信時のクロック ｸﾛｯｸ周波数
        cfg.freq_read   = 400000;     // 受信時のクロック ｸﾛｯｸ周波数
        cfg.pin_sda     = 25;         // SDAを接続しているピン番号 SDAピン
        cfg.pin_scl     = 21;         // SCLを接続しているピン番号 SCLピン
        cfg.i2c_addr    = 0x3C;       // I2Cデバイスのアドレス スレーブアドレス
        _bus_instance.config(cfg);
        _panel_instance.setBus(&_bus_instance);
      }
      {
        auto cfg = _panel_instance.config();
        cfg.offset_rotation = 2;
        _panel_instance.config(cfg);
      }
      setPanel(&_panel_instance);
    }
};

```

```CPP:Slave側 myGraphics.cpp
#include "myGraphics.hpp"

//LovyanGFXインスタンス***********************************************
LGFX_SSD1306 lcd;
LGFX_Sprite  canvas(&lcd);
```