1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NITech-KatolabAdvent Calendar 2024

Day 6

IoTデバイスの作り方[Bluno Beetle BLE]

Last updated at Posted at 2024-11-29

この記事では、BLE機能を持つマイコンにセンサーを繋げてIoTデバイスを作成、センサー値を自作スマホアプリで受け取れるところまで基礎的な内容を教えます。
提供するサンプルコードを改造することで様々なIoTデバイスが作成できるはずです!

準備するもの

iPhone(BLE機能を備える4s以降の機種)
筆者がAndroid端末を使用したことがないため
Mac
アプリ開発にXcodeを使用するため
Xcode
アプリ開発用
Arduino IDE
マイコンのプログラミング用
LightBlue
BLEのUUIDを確認するため
BLE機能を持つマイコン

Beetle BLE等の小型なものが望ましい

お好みのセンサー

秋月電子のセンサー検索結果

以下の記事ではBeetle BLEの使用を前提に説明

マイコンの配線

VIN:電源の+線
GND:電源の-線、センサーのGNDピン
5V:センサーの電源ピン
A0:センサーの出力ピン

Arduino IDEの準備

ツールのボードからArduino Unoを選択
書き込みポートはUSBを選択

マイコンのプログラミング

Beetle BLEのボーレート:
115200bps

IoT_device.ino
int data = 0;

void setup() {
  Serial.begin(115200);
}

void loop() {
  uint16_t value = analogRead(A0);
  if (data == 0 && Serial.available() > 0) {
    data = Serial.read();
    if (data == 1) {
      Serial.write((uint8_t*)&value, sizeof(value));
      data = 0;
    }
  }
}

スマホから信号値1を受け取ると、アナログピンA0に繋がれたセンサーの値をスマホに返します。

XcodeとiPhoneの準備

Xcodeで作成したアプリを実機デバイスでテストする方法
上記記事を参考に準備してください

アプリ実装

LightBlueでBLEのUUIDを調べます。

デバイス名:Bluno
Beetle BLEのUUID:
DFB0:サービスUUID(端末と接続するのに使用)
DFB1:Serial通信(データのやり取りで使用)
DFB2:AT Ctrl(今回は使わない)

ViewController.swift
import UIKit
import CoreBluetooth

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
    
    // MARK: - プロパティ
    var centralManager: CBCentralManager!
    var connectedPeripheral: CBPeripheral?
    var rxCharacteristic: CBCharacteristic?
    
    // MARK: - IBOutlet
    @IBOutlet weak var SSIDUILabel: UILabel!
    
    // MARK: - ライフサイクル
    override func viewDidLoad() {
        super.viewDidLoad()
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }
    
    // MARK: - CBCentralManagerDelegate メソッド
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
            scanBLEModule()
        case .poweredOff:
            SSIDUILabel.text = "Bluetoothがオフです"
            print("Bluetooth is Off")
        default:
            SSIDUILabel.text = "Bluetooth使用不能"
            print("Bluetooth state is not supported: \(central.state.rawValue)")
        }
    }
    
    func scanBLEModule() {
        let serviceUUID = CBUUID(string: "DFB0")
        centralManager.scanForPeripherals(withServices: [serviceUUID], options: nil)
        SSIDUILabel.text = "スキャン中..."
        print("Scanning for BLE devices...")
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
        print("Discovered device: \(peripheral.name ?? "Unknown")")
        
        if connectedPeripheral == nil {
            connectedPeripheral = peripheral
            connectedPeripheral?.delegate = self
            centralManager.connect(peripheral, options: nil)
            centralManager.stopScan()
            SSIDUILabel.text = "接続中: \(peripheral.name ?? "Unknown Device")"
        }
    }
    
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        print("Connected to \(peripheral.name ?? "Unknown Device")")
        SSIDUILabel.text = "接続済み: \(peripheral.name ?? "Unknown Device")"
        peripheral.discoverServices([CBUUID(string: "DFB0")])
    }
    
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        if let error = error {
            print("Disconnected with error: \(error.localizedDescription)")
        } else {
            print("Disconnected from \(peripheral.name ?? "Unknown Device")")
        }
        SSIDUILabel.text = "切断されました"
        connectedPeripheral = nil
    }
    
    // MARK: - CBPeripheralDelegate メソッド
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let error = error {
            print("Service discovery failed: \(error.localizedDescription)")
            return
        }
        
        if let services = peripheral.services {
            for service in services {
                if service.uuid == CBUUID(string: "DFB0") {
                    peripheral.discoverCharacteristics(nil, for: service)
                }
            }
        }
    }
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let error = error {
            print("Characteristic discovery failed: \(error.localizedDescription)")
            return
        }
        
        if let characteristics = service.characteristics {
            for characteristic in characteristics {
                if characteristic.uuid == CBUUID(string: "DFB1") {
                    rxCharacteristic = characteristic
                    peripheral.setNotifyValue(true, for: characteristic)
                    print("RX Characteristic discovered and notifications enabled: \(characteristic)")
                }
            }
        }
    }
    
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            print("Failed to enable notifications: \(error.localizedDescription)")
            return
        }

        if characteristic.isNotifying {
            print("Notifications enabled for characteristic: \(characteristic)")
        } else {
            print("Notifications disabled for characteristic: \(characteristic)")
        }
    }
    
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            print("Failed to receive data: \(error.localizedDescription)")
            return
        }
        
        if let data = characteristic.value {
            print("Data received: \(data as NSData)")
            processData(data)
        } else {
            print("No data received.")
        }
    }
    
    func processData(_ data: Data) {
        if data.count == 2 {
            let receivedValue = data.withUnsafeBytes { $0.load(as: UInt16.self) }
            print("Received 2-byte UInt16 value: \(receivedValue)")
            
            DispatchQueue.main.async {
                self.SSIDUILabel.text = "受信データ: \(receivedValue)"
            }
        } else {
            print("Invalid data size: \(data.count) bytes. Expected 2 bytes.")
            DispatchQueue.main.async {
                self.SSIDUILabel.text = "エラー: 不正なデータサイズ (\(data.count))"
            }
        }
    }
    
    // MARK: - デバイスへのデータ送信
    func sendCommand(value: Int) {
        guard let peripheral = connectedPeripheral,
              let characteristic = rxCharacteristic else {
            print("No connected device or RX characteristic.")
            return
        }
        
        var val = value
        let data = Data(bytes: &val, count: MemoryLayout<Int>.size)
        peripheral.writeValue(data, for: characteristic, type: .withResponse)
        print("Sent Int value: \(value)")
    }
    
    // MARK: - IBAction
    @IBAction func sendButtonTapped(_ sender: UIButton) {
        sendCommand(value: 1)
    }
}

ボタンを押すとセンサー値の要求信号値1をデバイスに送ります。
受け取ったセンサー値をSSIDUILabelに表示します。
サンプルコードgitリンク
サンプルコードを利用する場合は、TeamとBundle identiferを入力してください。

スクリーンショット 2024-11-29 23.10.39.png

おわりに

I先輩へ
この内容で土壌湿度センサーを繋げば、1年前に依頼されていた観葉植物のためのIoTデバイスが作れるかと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?