nRF52840で温湿度と気圧を計測しEPS32C3にLED送信

BLE通信

Send側

wikiを参照

  • front indication diagram      

  • back indication diagram      

  • Pin List         

  • 回路図は下記に示します         

  • Battery Charging current

    • //Low Charging Current
      void setup(){
          pinMode (P0_13, OUTPUT);
      }
      void loop() {
          digitalWrite(P0_13, HIGH);
      }
      
    • //High Charging Current
      void setup(){
          pinMode (P0_13, OUTPUT);
      }
      void loop() {
          digitalWrite(P0_13, LOW);
      }
      
  • Q1:My Arduino IDE is stuck when uploading code to the board

    • A1:You can first try to reset the board by clicking the "Reset Button" once. If that does not work, rapidly click it twice to enter bootloader mode. If that also doesn't work, disconnect the board from the PC, and connect the board again.
  • Q2:What are the considerations when using XIAO nRF52840 for battery charging?

    • A2:Currently for this issue, we recommend that users do not turn off the ADC function of P0.14 (D14) or set P0.14 (D14) to high during battery charging.

    • // Buttery電圧取得
      int vbat_raw = analogRead(PIN_VBAT);
      int vbat_mv = vbat_raw * 2400 / 1023; // VREF = 2.4V, 10bit A/D
      vbat_mv = vbat_mv * 1510 / 510;       // 1M + 510k / 510k
      

ソース

/*
  Seed_nRF52840_BLE_Beacon_Send.ino
  
  Seeed XIAO BLE nRF52840でバッテリー電圧等のデータを、BLEのアドバタイズデータにのせて送信する
  
*/

#define DEBUG 1

#include <Arduino.h>
#if DEBUG == 1
#include <Adafruit_TinyUSB.h>   // for Serial
#endif

#include <Wire.h>

#define BMP280_address 0x76
#define BME280_address 0x76
#define Ctrl_register 0xf4
#define osrs_t 0b00100000 // Temp 16bit
#define osrs_p 0b00000100 // Press 16bit
#define mode_normal 0b00000011
#define Config_register 0xf5
#define t_sb 0b00100000 // 62.5
#define filter 0b00000000
#define spi3w_en 0b00000000
#define ID_register 0xd0
#define Temp_register 0xfa
#define Tum_register 0xfd
#define Press_register 0xf7
// #define CONFIG 0xF5
#define CTRL_MEAS 0xF4
#define CTRL_HUM 0xF2

// BME280気温補正データ
uint16_t dig_T1;
int16_t  dig_T2;
int16_t  dig_T3;

// BME280湿度補正データ
uint8_t  dig_H1;
int16_t  dig_H2;
uint8_t  dig_H3;
int16_t  dig_H4;
int16_t  dig_H5;
int8_t   dig_H6;

// BME280気圧補正データ
uint16_t dig_P1;
int16_t  dig_P2;
int16_t  dig_P3;
int16_t  dig_P4;
int16_t  dig_P5;
int16_t  dig_P6;
int16_t  dig_P7;
int16_t  dig_P8;
int16_t  dig_P9;

#define BME280_S32_t int32_t
#define BME280_U32_t uint32_t
#define BME280_S64_t int64_t
int32_t t_fine;
byte readbuffer[2];
unsigned char dac[26];
unsigned int i;

// 設定
const uint16_t DEVICE_ID = 1;     // 子機ID (advData.id)
const uint8_t DEVICE_TYPE = 10;   // 子機種別 (advData.type)

// BLE関連
#include "bluefruit.h"

// 外部QSPI Flash Memory(省電力化のために使用)
#include <Adafruit_SPIFlash.h>
Adafruit_FlashTransport_QSPI flashTransport;
Adafruit_SPIFlash flash(&flashTransport);

// 送信するデータの構造体(nRF52840では2バイト未満はパディングされるので順番に注意)
typedef struct {
  uint8_t maker[4]; // maker_id 子機(nRF52840)の識別用
  uint16_t id;    // 子機ID
  uint8_t type;   // 子機種別
  uint8_t ttl;    // TTL (time to live)
  uint16_t seq;   // シーケンス番号   -- ここまで共通フォーマット
  int16_t pres;   // 気圧データ
  int16_t temp;   // 気温データ
  int16_t humi;   // 湿度データ
  int16_t vbat;   // 電圧データ
} AdvData;
 AdvData advData = {
  .maker = { 0xFF, 0xFF, 0x12, 0x35 },
  .id = DEVICE_ID,
  .type = DEVICE_TYPE,
  .ttl = 0,
  .seq = 1,
 };

// デバッグに便利なマクロ定義 --------
#define sp(x) Serial.println(x)
#define spn(x) Serial.print(x)
#define spp(k,v) Serial.println(String(k)+"="+String(v))
#define spf(fmt, ...) Serial.printf(fmt, __VA_ARGS__)
#define array_length(x) (sizeof(x) / sizeof(x[0]))

// deep sleepモードに入る(復帰はリスタートになる)
void enterDeepSleep() {
  nrf_gpio_cfg_sense_set(NRF_GPIO_PIN_MAP(0,2), NRF_GPIO_PIN_SENSE_HIGH);  // P0.02 = D0ピンでdeep sleepから復帰
  sd_power_system_off();
  // NRF_POWER->SYSTEMOFF = 1;
}

// 測定してデータを送信する
uint32_t adc_P, adc_T, adc_H;

void measure() {
  int32_t  temp_cal;
  uint32_t humi_cal, pres_cal;
  int16_t temp, humi, pres;

  // Buttery電圧取得
  int vbat_raw = analogRead(PIN_VBAT);
  int vbat_mv = vbat_raw * 2400 / 1023; // VREF = 2.4V, 10bit A/D
  vbat_mv = vbat_mv * 1510 / 510;       // 1M + 510k / 510k
  advData.vbat = vbat_mv;

  //測定データ取得
  Wire.beginTransmission(BMP280_address);//I2Cスレーブ「Arduino Unoのデータ送信開始
  Wire.write(0xF7);//出力データバイトを「気圧データ」のアドレスに指定
  Wire.endTransmission();//I2Cスレーブ「Arduino Uno」のデータ送信終了
  Wire.requestFrom(BMP280_address, 8);//I2Cデバイス「BME280」に8Byteのデータ要求
  for (i=0; i<8; i++){
    while (Wire.available() == 0 ){}
    dac[i] = Wire.read();//dacにI2Cデバイス「BME280」のデータ読み込み
    if (DEBUG) Serial.printf(" %2x", dac[i]);
  }
  adc_P = ((uint32_t)dac[0] << 12)
        | ((uint32_t)dac[1] << 4) 
        | ((         dac[2] >> 4) & 0x0F);
  adc_T = ((uint32_t)dac[3] << 12) 
        | ((uint32_t)dac[4] << 4) 
        | ((         dac[5] >> 4) & 0x0F);
  adc_H = ((uint32_t)dac[6] << 8) 
        | ((uint32_t)dac[7]);

  temp_cal = BME280_compensate_T_int32(adc_T);//温度データ補正計算
  humi_cal = bme280_compensate_H_int32(adc_H);//湿度データ補正計算
  pres_cal = BME280_compensate_P_int64(adc_P);//気圧データ補正計算

  advData.pres = (float)pres_cal * 10 / 25600.0;//気圧データを実際の値に計算
  advData.temp = (float)temp_cal * 10 / 100.0;//温度データを実際の値に計算
  advData.humi = (float)humi_cal * 10 / 1024.0;//湿度データを実際の値に計算
  if (DEBUG) Serial.printf("][%d %d %d %d]\n",advData.pres, advData.temp, advData.humi,  advData.vbat);

  // アドバタイズ中なら一旦中断(たぶんしなくていい)
  if (Bluefruit.Advertising.isRunning()) {
    Bluefruit.Advertising.stop();
  }

  // データを送信
  if (advData.seq > 9999) advData.seq = 0;
  Bluefruit.Advertising.clearData();
  Bluefruit.Advertising.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, &advData, sizeof(advData));
  Bluefruit.Advertising.start(18);   // アドバタイズを開始、引数は終了する時間(s)
  advData.seq++;
}

// 初期化
void setup() {

  Wire.begin();  //BME280動作設定

  //BME280動作設定
  Wire.beginTransmission(BMP280_address);
      Wire.write(Config_register);
      Wire.write(t_sb | filter | spi3w_en);
  Wire.endTransmission();
  delay(100);
  //BME280測定条件設定
  Wire.beginTransmission(BMP280_address);
      Wire.write(Ctrl_register);
      Wire.write(osrs_t | osrs_p | mode_normal);
  Wire.endTransmission();
  delay(100);
  //BME280温度測定条件設定
  Wire.beginTransmission(BMP280_address);
  Wire.write(CTRL_HUM);//湿度測定条件設定
  Wire.write(0x01);//「湿度オーバーサンプリングx1」
  Wire.endTransmission();
  //補正データ取得
  readCoefficients();

  if (DEBUG) {
    Serial.begin(115200);
    pinMode(LED_RED, OUTPUT);
    pinMode(LED_GREEN, OUTPUT);
    pinMode(LED_BLUE, OUTPUT);
    digitalWrite(LED_RED, HIGH);
    digitalWrite(LED_GREEN, HIGH);
    digitalWrite(LED_BLUE, HIGH);
  }

 // Buttery
    analogReference(AR_INTERNAL_2_4); // VREF = 2.4V
    analogReadResolution(10);         // 10bit A/D
    pinMode(14, OUTPUT);
    digitalWrite(14, LOW);
  // }

  // オンボードQSPI Flash MemoryをDeep Power-downモードにして省電力化する
  flashTransport.begin();
  flashTransport.runCommand(0xB9);
  delayMicroseconds(5);
  flashTransport.end();


  if (DEBUG) {
    digitalWrite(LED_RED, LOW);
    delay(500);
    digitalWrite(LED_RED, HIGH);
  }

  //BLEの設定
  Bluefruit.begin();
  Bluefruit.autoConnLed(false);
  Bluefruit.setTxPower(4);  // 送信強度 最小 -40, 最大 +8 dBm
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.setType(BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED);
  Bluefruit.Advertising.setFastTimeout(1);  // 高速アドバタイズの終了時間 0=継続(0にするとなぜかstart()が効かない)

  // WDTの設定
  NRF_WDT->CONFIG         = 0x01;     // Configure WDT to run when CPU is asleep
  NRF_WDT->CRV            = 1+32768*60*30;    // CRV = timeout * 32768 + 1
  NRF_WDT->RREN           = 0x01;     // Enable the RR[0] reload register
  NRF_WDT->TASKS_START    = 1;        // Start WDT       
}


// メイン
void loop() {
  
  // int16_t txpowers[4] = { -40, 0, 4, 8 };
  // uint16_t intervals[9] = { 32, 64, 128, 256, 384, 512, 1024, 2048, 4096 };

  //Battery Hight Charging curren
  digitalWrite(13, LOW);

  advData.id = 101;
  advData.seq = 1;
  Bluefruit.setTxPower(4);
  Bluefruit.Advertising.setInterval(256, 256);
  for (int i=0; i<5; i++) {
    loop_sub( );
  }

  digitalWrite(LED_GREEN, HIGH);
  digitalWrite(LED_BLUE, HIGH);
  while (1) {
    digitalWrite(LED_RED, LOW);
    delay(50);
    digitalWrite(LED_RED, HIGH);
    delay(2500);
    // NRF_WDT->RR[0] = WDT_RR_RR_Reload;
  }
}

void loop_sub(  ) {
  if (DEBUG) {
    digitalWrite(LED_GREEN, HIGH);
    digitalWrite(LED_BLUE, LOW);
  }

  uint8_t maker[4]; // maker_id 子機(nRF52840)の識別用
  uint16_t id;    // 子機ID
  uint8_t type;   // 子機種別
  uint8_t ttl;    // TTL (time to live)
  uint16_t seq;   // シーケンス番号   -- ここまで共通フォーマット
  int16_t pres;   // 気圧データ
  int16_t temp;   // 気温データ
  int16_t humi;   // 湿度データ

  measure();  // 測定してデータを送信する

  if (DEBUG) Serial.printf(" [%x]", advData.vbat);
  if (DEBUG) Serial.printf("[%2x", advData.maker[0]);
  if (DEBUG) Serial.printf(" %2x", advData.maker[1]);
  if (DEBUG) Serial.printf(" %2x", advData.maker[2]);
  if (DEBUG) Serial.printf(" %2x", advData.maker[3]);
  if (DEBUG) Serial.printf(" %2x", advData.id >> 8);
  if (DEBUG) Serial.printf(" %2x", advData.id & 0xff);
  if (DEBUG) Serial.printf(" %2x", advData.ttl);
  if (DEBUG) Serial.printf(" %2x", advData.seq >> 8);
  if (DEBUG) Serial.printf(" %2x", advData.seq & 0xff);

  delay(500);
  if (DEBUG) {
    digitalWrite(LED_BLUE, HIGH);
    digitalWrite(LED_GREEN, LOW);
  }
  delay(1000);

  // WDT Update
  NRF_WDT->RR[0] = WDT_RR_RR_Reload;
}

//温度補正 関数
BME280_S32_t BME280_compensate_T_int32(BME280_S32_t adc_T) {
  BME280_S32_t var1, var2, T;
  var1 = ((((adc_T>>3) - ((BME280_S32_t)dig_T1<<1))) * ((BME280_S32_t)dig_T2)) >> 11;
  var2 = (((((adc_T>>4) - ((BME280_S32_t)dig_T1)) * ((adc_T>>4) - ((BME280_S32_t)dig_T1))) >> 12) * ((BME280_S32_t)dig_T3)) >> 14;
  t_fine = var1 + var2;
  T = (t_fine * 5 + 128) >> 8;
  return (BME280_S32_t)T;
}

 //湿度補正 関数
BME280_U32_t bme280_compensate_H_int32(BME280_S32_t adc_H)  {
  BME280_S32_t v_x1_u32r;

  v_x1_u32r = (t_fine - ((BME280_S32_t)76800));
  v_x1_u32r = (((((adc_H << 14) - (((BME280_S32_t)dig_H4) << 20) - (((BME280_S32_t)dig_H5) * v_x1_u32r)) +
    ((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)dig_H6)) >> 10) * (((v_x1_u32r * 
    ((BME280_S32_t)dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) * 
    ((BME280_S32_t)dig_H2) + 8192) >> 14));
  v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)dig_H1)) >> 4));
  v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
  v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
  return (BME280_U32_t)(v_x1_u32r>>12);
}

//気圧補正 関数
BME280_U32_t BME280_compensate_P_int64(BME280_S32_t adc_P)  {
  BME280_S64_t var1, var2, p;
  var1 = ((BME280_S64_t)t_fine) - 128000;
  var2 = var1 * var1 * (BME280_S64_t)dig_P6;
  var2 = var2 + ((var1*(BME280_S64_t)dig_P5)<<17);
  var2 = var2 + (((BME280_S64_t)dig_P4)<<35);
  var1 = ((var1 * var1 * (BME280_S64_t)dig_P3)>>8) + ((var1 * (BME280_S64_t)dig_P2)<<12);
  var1 = (((((BME280_S64_t)1)<<47)+var1))*((BME280_S64_t)dig_P1)>>33;
  if (var1 == 0) {
    return 0; // avoid exception caused by division by zero
  }
  p = 1048576 - adc_P;
  p = (((p<<31) - var2)*3125) / var1;
  var1 = (((BME280_S64_t)dig_P9) * (p>>13) * (p>>13)) >> 25;
  var2 = (((BME280_S64_t)dig_P8) * p) >> 19;
  p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)dig_P7)<<4);
  return (BME280_U32_t)p;
}

void readCoefficients(){
   //BME280補正データ取得
  Wire.beginTransmission(BMP280_address);
  Wire.write(0x88);//出力データバイトを「補正データ」のアドレスに指定
  Wire.endTransmission();

  Wire.requestFrom(BMP280_address, 26);
  for (i=0; i<26; i++){
    while (Wire.available() == 0 ){}
    dac[i] = Wire.read();//dacにI2Cデバイス「BME280」のデータ読み込み
  }
  
  //気温補正データ
  dig_T1 = ((uint16_t)((dac[1] << 8) | dac[0]));
  dig_T2 = ((int16_t)((dac[3] << 8) | dac[2]));
  dig_T3 = ((int16_t)((dac[5] << 8) | dac[4]));

  //気圧補正データ
  dig_P1 = ((uint16_t)((dac[7] << 8) | dac[6]));
  dig_P2 = ((int16_t)((dac[9] << 8) | dac[8]));
  dig_P3 = ((int16_t)((dac[11] << 8) | dac[10]));
  dig_P4 = ((int16_t)((dac[13] << 8) | dac[12]));
  dig_P5 = ((int16_t)((dac[15] << 8) | dac[14]));
  dig_P6 = ((int16_t)((dac[17] << 8) | dac[16]));
  dig_P7 = ((int16_t)((dac[19] << 8) | dac[18]));
  dig_P8 = ((int16_t)((dac[21] << 8) | dac[20]));
  dig_P9 = ((int16_t)((dac[23] << 8) | dac[22]));

 //湿度補正データ
  dig_H1 = ((uint8_t)(dac[25]));
  Wire.beginTransmission(BMP280_address);
  Wire.write(0xE1);//出力データバイトを「補正データ」のアドレスに指定
  Wire.endTransmission();
  
  Wire.requestFrom(BMP280_address, 7);//I2Cデバイス「BME280」に7Byteのデータ要求
  for (i=0; i<7; i++){
    while (Wire.available() == 0 ){}
    dac[i] = Wire.read();//dacにI2Cデバイス「BME280」のデータ読み込み
  }
  
  dig_H2 = ((int16_t)((dac[1] << 8) | dac[0]));
  dig_H3 = ((uint8_t)(dac[2]));
  dig_H4 = ((int16_t)((dac[3] << 4) + (dac[4] & 0x0F)));
  dig_H5 = ((int16_t)((dac[5] << 4) + ((dac[4] >> 4) & 0x0F)));
  dig_H6 = ((int8_t)dac[6]);
  
  delay(100);//1000msec待機(1秒待機)
}

receive側

/*
  XIAO_ESP32C3_LED_Receive.ino

*/

// #include <M5Unified.h>
#include <Arduino.h>

// U8g2
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
#define SDA_PIN 5
#define SCL_PIN 6
U8G2_SSD1306_72X40_ER_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);   // EastRising 0.42" OLED

// LED
int ledPin = 8;    // LED connected to digital pin 8

// 設定
const size_t RCV_CNT_MAX = 10;  // 同時に受信するデバイスの最大数
const int BLE_SCAN_TIME = 5;    // BLEのスキャンを行う時間(s)
const char XIAO[4] = { 0xFF, 0xFF, 0x12, 0x35 };   // XIAO nRF52840識別値(FFFFは固定)

// BLE関連
#include <BLEDevice.h>
BLEScan* pBLEScan;

// シーケンス番号の記憶
#include <map>
std::map<uint16_t, uint16_t> seqHist;

// デバッグに便利なマクロ定義 --------
#define sp(x) Serial.println(x)
#define spn(x) Serial.print(x)
#define spp(k,v) Serial.println(String(k)+"="+String(v))
#define spf(fmt, ...) Serial.printf(fmt, __VA_ARGS__)

// その他定義
struct RcvData {
  uint16_t id;  // 子機ID
  uint8_t type; // 子機種別
  uint8_t ttl;  // TTL (time to live)
  uint16_t seq; // シーケンス番号
  float pres;   // 気圧データ
  float temp;   // 気温データ
  float humi;   // 湿度データ
  float vbat;   // 電圧データ
  int16_t rssi; // 電波強度 RSSI -50非常に強い -80弱い
};

// 初期化
void setup() {
  // auto cfg = M5.config();
  // M5.begin(cfg); 
    Serial.begin(115200);
  delay(1000);
  sp("System Start!");

  // ディスプレイの設定
  Wire.begin(SDA_PIN, SCL_PIN);
  delay(100);
    u8g2.begin();
  // u8g2.setFont(u8g2_font_helvB10_tr); // choose a suitable font
  u8g2.setFont(u8g2_font_lubB10_tr); // choose a suitable font
  // u8g2.setDisplayRotation(U8G2_R2);
  delay(100);

  // BLEの設定
  sp("init BLE device...");
  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setActiveScan(false);   // パッシブスキャンにする
}

// メイン
  int BleTimer = 0;
    void loop() {
  RcvData rcvdatas[RCV_CNT_MAX];
  int rcvcnt = 0;
  float pres,temp,humi;
  uint16_t rssi;

  // BLEのスキャンを行う
  // sp("\nscanning...");
  BLEScanResults foundDevices = pBLEScan->start(BLE_SCAN_TIME);
  int hit = foundDevices.getCount();
  // spf("found", hit);

  // 受信した一覧から対象デバイスを抽出する
  for (int i=0; i<hit; i++) {
    BLEAdvertisedDevice dev = foundDevices.getDevice(i);
    std::string data = dev.getManufacturerData();

    // XIAO nRF52840からのデータだったら値を格納する
    if (data.length() < 18 )continue;
    if (data[0] == XIAO[0] && data[1] == XIAO[1] && data[2] == XIAO[2] && data[3] == XIAO[3]) {
      rcvdatas[rcvcnt].id = data[5] << 8 | data[4];
      rcvdatas[rcvcnt].type = data[6];
      rcvdatas[rcvcnt].ttl = data[7];
      rcvdatas[rcvcnt].seq = data[9] << 8 | data[8];
      if (seqHist[rcvdatas[rcvcnt].id] == rcvdatas[rcvcnt].seq) continue;  // 同じデータは無視
      seqHist[rcvdatas[rcvcnt].id] = rcvdatas[rcvcnt].seq;
      if (rcvdatas[rcvcnt].type == 10) {  // 種別10: 実験用 volt(i2) temp(i2)
        rcvdatas[rcvcnt].pres = (float)(data[11] << 8 | data[10]) / 10.00 ;
        rcvdatas[rcvcnt].temp = (float)(data[13] << 8 | data[12]) / 10.00;
        rcvdatas[rcvcnt].humi = (float)(data[15] << 8 | data[14]) / 10.00;
        rcvdatas[rcvcnt].vbat = (float)(data[17] << 8 | data[16]) / 100;
        rcvdatas[rcvcnt].rssi = (uint16_t)dev.getRSSI() ;  // 電波強度

        rcvcnt ++;
      }
      if (rcvcnt >= RCV_CNT_MAX) break;
    }
  }

  // ディスプレイに表示
  if (rcvcnt > 0) {
    rcvcnt-- ;
    if ( BleTimer > 5 ){
      Serial.printf("\nPres:%.1f,temp:%.1f,humi:%.1f,vbat:%.1f,rssi:%d",
        rcvdatas[rcvcnt].pres - 1000, rcvdatas[rcvcnt].temp, rcvdatas[rcvcnt].humi, rcvdatas[rcvcnt].vbat, abs( rcvdatas[rcvcnt].rssi ) );

      u8g2.clearBuffer();                 // clear the internal memory
      u8g2.setCursor(0,14);
      u8g2.printf("pres:%4d", int (rcvdatas[rcvcnt].pres) );
      u8g2.setCursor(0,27);
      u8g2.printf("temp:%4.1f", rcvdatas[rcvcnt].temp);
      u8g2.setCursor(0,40);
      u8g2.printf("hum: %4.1f", rcvdatas[rcvcnt].humi);
      u8g2.display();
      u8g2.sendBuffer();          // transfer internal memory to the display
    }
    BleTimer = 0;
  } else {
    BleTimer++;

    if ( (BleTimer % 5 ) == 0 ){
      Serial.printf(" %d",BleTimer/5);
    }
  }

  delay(100);
}

プライバシーポリシー |ページトップへ

`