개발자/Arduino

Arduino Nano 33 BLE Sense 에서 Bluetooth LE 시작하기

지구빵집 2020. 11. 18. 10:30
반응형

 

 

본 문서는 Bluetooth Low Energy를 사용하기 위해 Arduino Nano 33 BLE Sense 보드를 프로그래밍하는 방법을 보여줍니다. 다음 포스팅은 Bluetooth LE를 사용하여 PC와 Nano 33 BLE Sense와 데이터를 전송하는 방법을 올립니다. 기대해주세요.(자신의 스마트 워치를 만든 프로젝트 참고 The B&ND

 

여기서는 Nano 33에서 Bluetooth LE 주변 장치를 구축할 것이지만 주변 장치를 찾아 연결할 중앙 장치 없이는 디버깅하기가 어렵습니다. 이 시리즈의 다음 기사에서는 Python을 사용하여 Bluetooth LE 주변 장치에 연결하는 방법을 보여줍니다. 이렇게 하면 PC에서 Nano 33 보드에 연결할 수 있습니다. 더 많은 Bluetooth LE 콘텐츠는 아래 링크 목록을 참고하세요.

 

Arduino Nano 33 BLE 보드 설치 방법

 

Arduino Nano 33 BLE 보드를 준비했다면 사용하기 위해 약간의 설정이 필요합니다. 먼저 Arduino IDE를 열고 "보드 관리자"로 이동합니다.

 

 

Nano 33 BLE보드를 검색하고 설치합니다. 예전에는 Arduino nRF528xBoards (MBed OS) 였지만 지금 버전에서는 아래 그림과 Arduino mbed-enabled Boards입니다. 여하튼 설치해 주세요.

 

아두이노를 연결했다면 메뉴 - 툴 - 보드 선택에서 아래처럼 Nnao 33 BLE를 선택하시고, 마찬가지로 포트 선택을 해주세요.

 

Arduino는 BLE를 제외하고 Nano 33 보드와 함께 작동할 준비되어 있어야 합니다. 이를 위해서는 다른 라이브러리가 필요합니다. 

 

ArduinoBLE 라이브러리 설치 방법

 

Bluetooth LE를 위한 몇 가지 Arduino 라이브러리가 있습니다. 일반적으로 하드웨어에 따라 다릅니다. 예를 들어 ESP32에서 Bluetooth LE를 사용하려면 다른 라이브러리가 필요합니다. 당면한 문제로 돌아갑니다. BLE가 장착된 Arduino 보드 작업을 위한 공식 라이브러리는 다음과 같습니다. IDE에서 라이브러리 관리자를 열어 검색창에 Arduino BLE로 검색하셔서 설치해 주시면 됩니다. 

 

 

이제 스케치 프로그램의 상단에 다음과 같은 헤더 파일을 포함할 수 있습니다. 코드에서 전체 API에 접근할 수 있습니다.

 

#include <ArduinoBLE.h>

 

여기에서 BLE에 대해 용어 정리를 이해하고 진행합니다.

 

바쁘신 분은 아래의 전체 코드로 직접 이동하셔도 됩니다. 코드로 이동하기 전에 다음 용어가 혼동되는 경우 EmbeddedFM의 설명을 확인할 수 있습니다. 아래에 정리합니다.

 

  • Peripheral
  • Central
  • Master
  • Slave
  • Server
  • Client

BLE : MASTER, CENTRAL, SLAVE, PERIPHERAL, CLIENT, SERVER


BLE 역할은 연결 전과 연결 후로 나눌 수 있습니다.

 

연결 전: 개시하는 디바이스는 peripaeral 혹은 central 입니다.

 

  • peripheral: 자기 자신이 스스로 알리고 중앙 장치가 연결될 때까지 기다립니다. peripheral은 일반적으로 Fitbit 또는 스마트 워치와 같은 작은 장치입니다.
  • central은 다른 장치를 검색합니다. 중앙은 일반적으로 스마트 폰 또는 PC입니다.
  • peripheral이 연결되면 이를 slave 라고 합니다. central이 연결을 만들고 난 다음 master 라고 부릅니다.

연결 후: BLE 연결이 설정된 후 장치는 client 또는 serveer가 될 수 있습니다.

 

  • 클라이언트는 원격 리소스에 액세스 합니다. 클라이언트는 일반적으로 마스터이지만 필수는 아닙니다. 대신 클라이언트가 슬레이브가 될 수 있습니다.
  • 서버에는 리소스(프로필 / 서비스 / 특성)의 로컬 데이터베이스가 있으며 원격 클라이언트에 리소스를 제공합니다. 서버는 일반적으로 슬레이브이지만 필수는 아닙니다. 대신 서버가 마스터가 될 수 있습니다.
  • 클라이언트는 읽기 및 쓰기 작업을 서버에 보내고 서버는 데이터로 응답합니다 (적절한 경우 로컬 데이터 변경).
  • 서버는 표시 및 알림 작업을 사용하여 읽기 / 쓰기 요청 없이 클라이언트에 데이터를 보낼 수 있습니다. 표시: 클라이언트가 작업을 승인했음을 나타냅니다. 알림: 작업은 클라이언트에 의해 승인되지 않습니다.

 

그리고 여기에 그 텍스트 벽을 요약하려는 내 그림이 있습니다.

 

이 설명과 그래픽이 누군가를 위한 편리한 치트 시트가 되었으면 합니다. 최소한 미래의 자신에게도 유용할 것 같습니다.

 

참고 1 : 대부분의 독서는 BLE 4.0에 중점을 두므로 BLE 4.1 또는 4.2에 적용되지 않는 사항이 있으면 알려주십시오.

참고 2 : 내가 가장 좋아하는 요약 : Bluetooth Low Energy Basics 

 

자, 잘 이해하셨나요? 축하합니다!

 

 

 

프로젝트 설명을 다시 이어서 시작합니다.

 

Arduino 33 BLE Sense가 주변 장치 BLE 장치로 작동하도록 하는 데 초점을 맞출 것입니다. 주변 장치로서, 하나는 읽기 용, 다른 하나는 쓰기 용으로 서비스를 제공한다고 광고합니다.

 

Nano 33 BLE Sense의 마이크에서 정보를 가져와서 원격 연결된 장치로 보냅니다. 마이크로폰 코드를 충분히 설명할 만큼 이해하지 못하기 때문에 여기서는 마이크 코드를 다루지 않겠습니다. 그러나 다음은 몇 가지 참고 문서를 제공합니다.

 

 

코딩

 

아래는 내가 만난 "gotchas"의 주석과 함께 내가 함께 해킹한 것입니다. 마지막으로 주의할 점은 코드를 프로젝트의 기반으로 사용했습니다. Arduino Nano 33 BLE Sense 예제를 단계별로 설명한 곳을 참고하십시오.

 

 아래 소스코드 전체를 싣고 하나씩 분석해 설명할 겁니다. 일단 전체 코드를 아래에 표시합니다. 소스코드의 주소는 여기입니다. 다른 파이선 코드도 보이는데 무엇인지는 모르겠네요. 아래 소스 코드를 blr_sense_33_test.ino 파일로 저장하세요. 코드 상단의 pdm.h 파일을 인클루드 한 것은 nano 33 ble sense 보드의 마이크로폰 센서를 다루기 위함이니 아직 테스트를 하지 못한 분은 pdm MP34DT05 test 링크를 참고하시기 바랍니다.

 

// https://rootsaid.com/arduino-ble-example/
// Characteristic info.
// https://www.arduino.cc/en/Reference/ArduinoBLEBLECharacteristicBLECharacteristic

#include <ArduinoBLE.h>
#include <PDM.h>

// This device's MAC:
// C8:5C:A2:2B:61:86
//#define LEDR        (23u)
//#define LEDG        (22u)
//#define LEDB        (24u)

// Device name
const char* nameOfPeripheral = "MicrophoneMonitor";
const char* uuidOfService = "00001101-0000-1000-8000-00805f9b34fb";
const char* uuidOfRxChar = "00001142-0000-1000-8000-00805f9b34fb";
const char* uuidOfTxChar = "00001143-0000-1000-8000-00805f9b34fb";

// BLE Service
BLEService microphoneService(uuidOfService);

// Setup the incoming data characteristic (RX).
const int WRITE_BUFFER_SIZE = 256;
bool WRITE_BUFFER_FIZED_LENGTH = false;

// RX / TX Characteristics
BLECharacteristic rxChar(uuidOfRxChar, BLEWriteWithoutResponse | BLEWrite, WRITE_BUFFER_SIZE, WRITE_BUFFER_FIZED_LENGTH);
BLEByteCharacteristic txChar(uuidOfTxChar, BLERead | BLENotify | BLEBroadcast);

// Buffer to read samples into, each sample is 16-bits
short sampleBuffer[256];

// Number of samples read
volatile int samplesRead;

/*
 *  MAIN
 */
void setup() {

  // Start serial.
  Serial.begin(9600);

  // Ensure serial port is ready.
  while (!Serial);

  // Prepare LED pins.
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);

  // Configure the data receive callback
  PDM.onReceive(onPDMdata);

  // Start PDM
  startPDM();

  // Start BLE.
  startBLE();

  // Create BLE service and characteristics.
  BLE.setLocalName(nameOfPeripheral);
  BLE.setAdvertisedService(microphoneService);
  microphoneService.addCharacteristic(rxChar);
  microphoneService.addCharacteristic(txChar);
  BLE.addService(microphoneService);

  // Bluetooth LE connection handlers.
  BLE.setEventHandler(BLEConnected, onBLEConnected);
  BLE.setEventHandler(BLEDisconnected, onBLEDisconnected);
  
  // Event driven reads.
  rxChar.setEventHandler(BLEWritten, onRxCharValueUpdate);
  
  // Let's tell devices about us.
  BLE.advertise();
  
  // Print out full UUID and MAC address.
  Serial.println("Peripheral advertising info: ");
  Serial.print("Name: ");
  Serial.println(nameOfPeripheral);
  Serial.print("MAC: ");
  Serial.println(BLE.address());
  Serial.print("Service UUID: ");
  Serial.println(microphoneService.uuid());
  Serial.print("rxCharacteristic UUID: ");
  Serial.println(uuidOfRxChar);
  Serial.print("txCharacteristics UUID: ");
  Serial.println(uuidOfTxChar);
  

  Serial.println("Bluetooth device active, waiting for connections...");
}


void loop()
{
  BLEDevice central = BLE.central();
  
  if (central)
  {
    // Only send data if we are connected to a central device.
    while (central.connected()) {
      connectedLight();

      // Send the microphone values to the central device.
      if (samplesRead) {
        // print samples to the serial monitor or plotter
        for (int i = 0; i < samplesRead; i++) {
          txChar.writeValue(sampleBuffer[i]);      
        }
        // Clear the read count
        samplesRead = 0;
      }
    }
  } else {
    disconnectedLight();
  }
}


/*
 *  BLUETOOTH
 */
void startBLE() {
  if (!BLE.begin())
  {
    Serial.println("starting BLE failed!");
    while (1);
  }
}

void onRxCharValueUpdate(BLEDevice central, BLECharacteristic characteristic) {
  // central wrote new value to characteristic, update LED
  Serial.print("Characteristic event, read: ");
  byte test[256];
  int dataLength = rxChar.readValue(test, 256);

  for(int i = 0; i < dataLength; i++) {
    Serial.print((char)test[i]);
  }
  Serial.println();
  Serial.print("Value length = ");
  Serial.println(rxChar.valueLength());
}

void onBLEConnected(BLEDevice central) {
  Serial.print("Connected event, central: ");
  Serial.println(central.address());
  connectedLight();
}

void onBLEDisconnected(BLEDevice central) {
  Serial.print("Disconnected event, central: ");
  Serial.println(central.address());
  disconnectedLight();
}


/*
 *  MICROPHONE
 */
void startPDM() {
  // initialize PDM with:
  // - one channel (mono mode)
  // - a 16 kHz sample rate
  if (!PDM.begin(1, 16000)) {
    Serial.println("Failed to start PDM!");
    while (1);
  }
}


void onPDMdata() {
  // query the number of bytes available
  int bytesAvailable = PDM.available();

  // read into the sample buffer
  PDM.read(sampleBuffer, bytesAvailable);

  // 16-bit, 2 bytes per sample
  samplesRead = bytesAvailable / 2;
}


/*
 * LEDS
 */
void connectedLight() {
  digitalWrite(LEDR, LOW);
  digitalWrite(LEDG, HIGH);
}


void disconnectedLight() {
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, LOW);
}

 

위 코드를 컴파일 후 업로딩하여 실행시키고 나서 테스트는 nRF Connect를 사용해서 하였습니다. 앱 사용법은 다음 링크를 연결하여 아래를 보시면 나옵니다. 링크1, 링크2

 

초기화 BLE 및 PDM 라이브러리를 로드하여 API에 액세스 하여 마이크 및 라디오 하드웨어와 함께 작동합니다. 

 

#include <ArduinoBLE.h>
#include <PDM.h>

 

서비스 및 특성 서비스를 만들어 보겠습니다. 먼저 광고 패킷에 표시되는 이름을 만들어 사용자가 Arduino를 쉽게 식별할 수 있도록 합니다. 또한 microphoneService라는 서비스를 생성하여 전체 UUID (Universally Unique ID)를 문자열로 전달합니다. UUID를 설정할 때 두 가지 옵션이 있습니다. 16 비트 또는 128 비트 버전. 표준 Bluetooth LE 서비스 중 하나를 사용하는 경우 16 비트 버전이 좋습니다. 그러나 사용자 지정 서비스를 생성하려는 경우 전체 128 비트 UUID 생성을 탐색해야 합니다.

 

여기서는 전체 UUID를 사용하고 있지만 표준 서비스 및 특성을 사용합니다. 전체 UUID가 알려져 있으므로 다른 하드웨어를 프로토타입에 쉽게 연결할 수 있기 때문입니다. UUID를 더 완전히 이해하고 싶다면 Nordic의 기사를 적극 권장합니다.

 

어쨌든 다음 UUID를 사용할 것입니다.

 

  • Microphone Service = 0x1800 – Generic Access
  • rx Characteristic = 0x2A3D – String
  • tx Characteristic = 0x2A58 – Analog

 

Bluetooth 사양을 읽으면 Generic Access를 구현하기 위해 해야 하는 두 가지 필수 특성이 있습니다.

 

 

간단하게 하기 위해 독자에게 맡기겠습니다. 그러나 적절한 Generic Access 서비스를 위해 구현되어야 합니다. 코드로 돌아갑니다. 여기에서 원격 장치에 표시되어야 하는 장치 이름을 정의합니다. 그런 다음 서비스와 두 가지 특성, 하나는 송신용이고 다른 하나는 수신용입니다. 

 

// Device name
const char* nameOfPeripheral = "Microphone";
const char* uuidOfService = "0000181a-0000-1000-8000-00805f9b34fb";
const char* uuidOfRxChar = "00002A3D-0000-1000-8000-00805f9b34fb";
const char* uuidOfTxChar = "00002A58-0000-1000-8000-00805f9b34fb";

 

이제 실제로 microphoneService 라는 BLEService 객체를 인스턴스 화합니다.

 

// BLE Service
BLEService microphoneService(uuidOfService);

 

데이터 수신을 담당하는 rxCharacteristic 에는 Nano 33에게 어떻게 작동해야 하는지 알려주는 몇 가지 매개 변수 쌍이 존재합니다.

 

// Setup the incoming data characteristic (RX).
const int RX_BUFFER_SIZE = 256;
bool RX_BUFFER_FIXED_LENGTH = false;

 

RX_BUFFER_SIZE rx 버퍼를 위해 예약된 공간입니다. 그리고 RX_BUFFER_FIXED_LENGTH 솔직히, 잘 모르겠습니다. ArduinoBLE 라이브러리를 사용하는 올바른 방법을 찾을 때 문서를 참조했습니다.

 

BLECharacteristic()

 

유일한 값이나 버퍼로 charateristic을 초기화시키는 다른 여러 방법들이 있습니다. (예를 들어, BLEByteCharacteristic, BLEFloatCharacteristic 등) 여기서는 rxCharacteristic 을 위한 버퍼로 결정했습니다. 그것이 문제가 된 곳입니다.

 

다음 BLECharacteristic은 버퍼로 초기화하는 것과 관련하여 문서에 나와있는 내용입니다.

 

BLECharacteristic(uuid, properties, value, valueSize)

BLECharacteristic(uuid, properties, stringValue)

...

uuid: 16-bit or 128-bit UUID in string format

properties: mask of the properties (BLEBroadcast, BLERead, etc)

valueSize: (maximum) size of characteristic value

stringValue: value as a string

 

불행히도, 나는 그러한 인자를 가지고 초기화 작업을 하는 BLECharacteristic 이 없다. 나는 마침내 실제 BLECharacteristic 소스를 파고들었고 두 가지 방법으로 초기화하는 것을 발견했습니다

 

BLECharacteristic:

BLECharacteristic(new BLELocalCharacteristic(uuid, properties, valueSize, fixedLength))
BLECharacteristic(new BLELocalCharacteristic(uuid, properties, value))

 

나는 잘못된 정보를 싫어합니다. 좋아, 그 이야기는 제쳐두고 우리 코드로 돌아갑니다. 실제로 rx 및 tx 특성을 선언해 봅시다. 주목할 사항은 rx를 위해 버퍼 된 characteristic을 사용하고 있고,  tx를 위한 단일 바이트 값인 characteristic을 사용한다. 이것은 최적이 아닐 수도 있지만 효과가 있었습니다.

 

// RX / TX Characteristics
BLECharacteristic rxChar(uuidOfRxChar, BLEWriteWithoutResponse | BLEWrite, RX_BUFFER_SIZE, RX_BUFFER_FIXED_LENGTH);
BLEByteCharacteristic txChar(uuidOfTxChar, BLERead | BLENotify | BLEBroadcast);

 

두 번째 인수는 특성의 동작 방식을 정의하는 곳입니다. 각 속성은 단일 값 (마스킹)으로 함께 처리되는 상수 이므로 구분해야 합니다. 다음은 사용 가능한 속성 목록입니다.

 

  • BLEBroadcast – 특성이 광고되도록 합니다.
  • BLERead – 원격 장치가 특성 값을 읽을 수 있도록 합니다.
  • BLEWriteWithoutResponse – 원격 장치가 승인을 기대하지 않고 장치에 쓸 수 있습니다.
  • BLEWrite – 쓰기 성공 확인을 예상하면서 원격 장치에 쓰기 허용
  • BLENotify – 특성 값이 업데이트될 때마다 원격 장치에 알림을 보낼 수 있습니다.
  • BLEIndicate – BLENotify와 동일하지만 원격 장치에서 값을 읽었음을 나타내는 응답을 기대합니다.

 

마이크로폰

 

마이크 데이터를 추적하는 두 개의 전역 변수가 있습니다. 첫 번째는 작은 버퍼인 sampleBuffer로, 마이크에서 최대 256개의 값을 가지고 있습니다. volatile 키워드는 언제고 변할 수 있는 값을 나타냅니다. volatile int samplesRead 변수는 마이크 센서로부터의 즉각적인 값을 보유할 변수입니다. 인터럽트 서비스 루틴 벡터(ISR) 기능에서 사용됩니다. volatile키워드는 변수의 값은 언제든지 변경될 수 있으며 오히려 (프로세서에서 캐시 값에 의존하지 않고, 참조될 때 이 값을 확인해야 아두이노의 C ++ 컴파일러를 알려줍니다. 

 

// Buffer to read samples into, each sample is 16-bits
short sampleBuffer[256];

// Number of samples read
volatile int samplesRead;

 

Setup( )

디버깅에 사용되는 Serial 포트를 초기화합니다. 

 

void setup() {

  // Start serial.
  Serial.begin(9600);

  // Ensure serial port is ready.
  while (!Serial);

 

BLE가 실제로 연결되었는지 확인하기 위해 내장 RGB LED에 연결된 핀을 OUTPUT으로 설정합니다.

 

// Prepare LED pins.
pinMode(LED_BUILTIN, OUTPUT);
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);

 

소스 코드에서 LEDR와 LEDG는 반대로 되어 있습니다. 컴퓨터에서 ARDUINO NANO 33 BLE 폴더를 검색하고 pins_arduino.h 내부 파일을 편집하여이 문제를 해결할 수 있습니다. 다음을 변경하십시오. 

 

이거 찾기가 힘든데 pins_arduino.h 으로 검색하면 많이 나오고, 그 중에서 수정할 파일은 바로  ARDUINO_NANO33BLE 폴더 아래에 있는 파일입니다. 제 경우는 폴더가 무려

 

C:\Users\girin\AppData\Local\Arduino15\packages\arduino\hardware\mbed\1.1.4\variants\ARDUINO_NANO33BLE 여기입니다. ㅠ.ㅠ 참고하십시요. 바꿨는데도 변화가 없습니다. ??????

 

#define PIN_LED     (13u)
#define LED_BUILTIN PIN_LED
#define LEDR        (22u)
#define LEDG        (23u)
#define LEDB        (24u)
#define LED_PWR     (25u)

 

아래처럼 변경하세요.

 

#define PIN_LED     (13u)
#define LED_BUILTIN PIN_LED
#define LEDR        (23u)
#define LEDG        (22u)
#define LEDB        (24u)
#define LED_PWR     (25u)

 

그리고 저장하십시오. 매핑을 수정해야 합니다. onPDMdata( )는 ISR인데, 마이크로폰이 새로운 데이터를 얻을 때마다 동작합니다. 그리고 startPDM( )은 마이크 집적 회로를 시작합니다.

 

  // Configure the data receive callback
  PDM.onReceive(onPDMdata);

  // Start PDM
  startPDM();

 

이제 Bluetooth LE가 설정되었습니다. Bluetooth LE 하드웨어가 Nano 33 내에서 전원이 켜져 있는지 확인합니다. 장치 이름을 설정하고 서비스 광고를 시작합니다. 그런 다음 rx 및 tx특성을 microphoneService에 더해줍니다. 마지막으로  BLE 개체에 microphoneService를 추가합니다. 

 

  // Start BLE.
  startBLE();

  // Create BLE service and characteristics.
  BLE.setLocalName(nameOfPeripheral);
  BLE.setAdvertisedService(microphoneService);
  microphoneService.addCharacteristic(rxChar);
  microphoneService.addCharacteristic(txChar);
  BLE.addService(microphoneService);

 

이제 Bluetooth LE 하드웨어가 켜져 있고, 장치가 연결 또는 연결 해제될 때 실행되는 콜백을 추가합니다. 이러한 콜백은 알림, 설정 및 해체를 추가할 수 있는 좋은 장소입니다.

 

또한 Bluetooth LE 하드웨어에 특성이 기록될 때마다 실행되는 콜백을 추가합니다. 이를 통해 데이터가 유입될 때 데이터를 처리할 수 ​​있습니다. 

 

  // Bluetooth LE connection handlers.
  BLE.setEventHandler(BLEConnected, onBLEConnected);
  BLE.setEventHandler(BLEDisconnected, onBLEDisconnected);

  // Event driven reads.
  rxChar.setEventHandler(BLEWritten, onRxCharValueUpdate);

 

마지막으로 Bluetooth LE 하드웨어에 서비스와 특성을 세계에 알리기 시작하도록 명령합니다. 글쎄, 적어도 +/- 30ft 거리의 세계에 말입니다.

 

  // Let's tell devices about us.
  BLE.advertise();

 

메인 루프를 시작하기 전에 우리가 설정한 모든 하드웨어 정보를 알려주도록 하는 것을 좋아합니다. 이렇게 하면 새로 초기화된 주변 장치에 연결할 다른 응용 프로그램에 쉽게 추가할 수 있습니다.

 

  // Print out full UUID and MAC address.
  Serial.println("Peripheral advertising info: ");
  Serial.print("Name: ");
  Serial.println(nameOfPeripheral);
  Serial.print("MAC: ");
  Serial.println(BLE.address());
  Serial.print("Service UUID: ");
  Serial.println(microphoneService.uuid());
  Serial.print("rxCharacteristic UUID: ");
  Serial.println(uuidOfRxChar);
  Serial.print("txCharacteristics UUID: ");
  Serial.println(uuidOfTxChar);
  

  Serial.println("Bluetooth device active, waiting for connections...");
}

 

Loop( )

 

메인 루프는 BLE 객체에서 central 속성에 대한 참조를 가져옵니다. 그것은 만약 central 이 존재하고 연결되었는지 검사한다. 그렇다면을 connectedLight( )를 호출하여 하드웨어가 제대로 연결되었다는 것을 알려줄 녹색 LED가 켜진다.

 

다음 sampleBuffer배열에 데이터가 있는지 확인하고 , 있다면 txChar에 모든 데이터를 쓴 후 samplesRead변수를 0으로 재설정합니다.

 

마지막으로 장치가 연결되지 않았거나 초기화되지 않은 경우 을 호출하여 연결이 끊어진 조명을 켭니다 disconnectedLight( )를 호출하여 연결되지 않았다는 불을 겹니다.

 

void loop()
{
  BLEDevice central = BLE.central();
  
  if (central)
  {
    // Only send data if we are connected to a central device.
    while (central.connected()) {
      connectedLight();

      // Send the microphone values to the central device.
      if (samplesRead) {
        // print samples to the serial monitor or plotter
        for (int i = 0; i < samplesRead; i++) {
          txChar.writeValue(sampleBuffer[i]);      
        }
        // Clear the read count
        samplesRead = 0;
      }
    }
    disconnectedLight();
  } else {
    disconnectedLight();
  }
}

 

일부는 sampleBuffer이 데이터를 가져오는 방법에 문제가 있음을 알았을 수 있습니다. 이것은 마이크로폰의 ISR 이 txChar 버퍼를 쓰는 도중에 호출되는 조건에서 일어납니다. 이 문제를 해결해야 하는 경우 이 문서를 업데이트하겠습니다.

 

Helper Method

 

startBLE( )

 

이 startBLE()함수는 begin( ) 을 호출하여 Bluetooth LE 하드웨어를 초기화합니다. 하드웨어를 시작할 수 없는 경우 직렬 포트를 통해 표시되고 영원히 고정됩니다. 

 

/*
 *  BLUETOOTH
 */
void startBLE() {
  if (!BLE.begin())
  {
    Serial.println("starting BLE failed!");
    while (1);
  }
}

 

onRxCharValueUpdate( )

 

이 메서드는 연결된 장치에서 새 데이터를 수신할 때 호출됩니다. readValue를 호출하여 rxChar로부터 데이터를 가져오고, 버퍼에서 사용 가능한 바이트 수를 제공합니다. 이 readValue메서드는 읽은 바이트 수를 반환합니다. 그런 다음 tmp 버퍼의 각 바이트를 반복하고, char로 캐스트한 다음 직렬 터미널로 인쇄합니다. 이것은 디버깅할 때 매우 유용합니다.

 

끝내기 전에 우리는 또한 ASCII로 변환할 수 없는 데이터를 받은 경우를 대비하여 읽은 바이트 수를 인쇄합니다. 다시 말하지만 디버깅하는 경우에 도움이 됩니다.

 

void onRxCharValueUpdate(BLEDevice central, BLECharacteristic characteristic) {
  // central wrote new value to characteristic, update LED
  Serial.print("Characteristic event, read: ");
  byte tmp[256];
  int dataLength = rxChar.readValue(tmp, 256);

  for(int i = 0; i < dataLength; i++) {
    Serial.print((char)tmp[i]);
  }
  Serial.println();
  Serial.print("Value length = ");
  Serial.println(rxChar.valueLength());
}

 

LED 표시기

 

여기에서 볼 것이 별로 없습니다. 이러한 함수는 장치가 각각 연결 또는 연결 해제될 때 호출됩니다. 

 

void onBLEConnected(BLEDevice central) {
  Serial.print("Connected event, central: ");
  Serial.println(central.address());
  connectedLight();
}

void onBLEDisconnected(BLEDevice central) {
  Serial.print("Disconnected event, central: ");
  Serial.println(central.address());
  disconnectedLight();
}

/*
 * LEDS
 */
void connectedLight() {
  digitalWrite(LEDR, LOW);
  digitalWrite(LEDG, HIGH);
}


void disconnectedLight() {
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, LOW);
}

 

마이크로폰

 

Arduino에서 제공한 예제에서 이 코드를 훔쳤습니다. 16KHz 샘플 레이트로 PDM 하드웨어(마이크)를 초기화한다고 생각합니다.

 

/*
 *  MICROPHONE
 */
void startPDM() {
  // initialize PDM with:
  // - one channel (mono mode)
  // - a 16 kHz sample rate
  if (!PDM.begin(1, 16000)) {
    Serial.println("Failed to start PDM!");
    while (1);
  }
}

 

마지막으로 onPDMData 콜백은 읽을 수 있는 데이터가 있을 때마다 발생합니다. available( )을 호출하여 사용 가능한 바이트 수를 확인하고 해당 바이트 수를 버퍼로 읽습니다. 마지막으로 데이터가 int16이면 바이트 2수를 읽은 샘플 수로 나눕니다.

 

void onPDMdata() {
  // query the number of bytes available
  int bytesAvailable = PDM.available();

  // read into the sample buffer
  int bytesRead = PDM.read(sampleBuffer, bytesAvailable);

  // 16-bit, 2 bytes per sample
  samplesRead = bytesRead / 2;
}

 

Bluetooth LE는 강력하지만 제대로 하기가 어렵습니다. 명확하게 말하면, 바로 여기에 있다고 말하는 것이 아니라 더 분명해지기를 바랍니다. 문제가 발견되면 댓글을 남기거나 이메일을 보내 주시면 최대한 빨리 수정해 드리겠습니다. 

 

실행을 하고 com 포트를 찾아 연결하고 시리얼모니터를 열어 테스트 한 화면입니다. nRF Connect 앱을 사용하여 접속하면 데이터가 계속 날아오는 모습이 보입니다. 의미는 아직 잘 모르겠습니다.

 

 

아래 화면은 노트북 BLE를 켜고 장치를 연결하는 중인 화면인데 nano ee ble sense 보드에서는 연결 되었다고 나오는 데 노트북에서는 계속 장치를 찾습니다. 딱히 해 볼 게 없는데 노트북 Bluetooth는 끄고 테스트 하시기 바랍니다. 참고하시라고 올려둡니다.

 

 

 

 

참고 문서

 

Getting Started with Bluetooth LE on the Arduino Nano 33 Sense 원문

Bluetooth LE를 사용하여 PC와 Arduino 간에 데이터를 전송하는 방법 

 

 

 

 

반응형