●ドラッグコントロールを追加(iOS)
前回開発したiPhoneアプリに、画面をドラッグして歩行をコントロールする機能を追加します。右手と左手の親指をロボットの両足に見立てて画面上を歩くような動作で操縦できます。
ドラッグコントロールモードにするためのボタンと指の動きを拾うための部品(dragView)は既に設置されていますので、Main.storyboardとViewController.swiftとの関連付けと行ってください。
@IBOutlet weak var scanBtn: UIButton! @IBOutlet weak var connectBtn: UIButton! @IBOutlet weak var ltrnBtn: UIButton! @IBOutlet weak var fwrdBtn: UIButton! @IBOutlet weak var rtrnBtn: UIButton! @IBOutlet weak var leftBtn: UIButton! @IBOutlet weak var stopBtn: UIButton! @IBOutlet weak var rghtBtn: UIButton! @IBOutlet weak var ledonBtn: UIButton! @IBOutlet weak var bwrdBtn: UIButton! @IBOutlet weak var ledoffBtn: UIButton! @IBOutlet weak var sndBtn: UIButton! @IBOutlet weak var deviceNameLabel: UILabel! @IBOutlet weak var walkBtn: UIButton! @IBOutlet weak var dragView: UIView! @IBAction func tapScanBtn(_ sender: Any) { startScan() } @IBAction func tapConnectBtn(_ sender: Any) { self.centralManager.connect(blePeripheral, options: nil) } @IBAction func tapLtrnBtn(_ sender: Any) { write(byteValue: [1]) } @IBAction func tapFwrdBtn(_ sender: Any) { write(byteValue: [2]) } @IBAction func tapRtrnBtn(_ sender: Any) { write(byteValue: [3]) } @IBAction func tapLeftBtn(_ sender: Any) { write(byteValue: [4]) } @IBAction func tapStopBtn(_ sender: Any) { write(byteValue: [5]) } @IBAction func tapRghtBtn(_ sender: Any) { write(byteValue: [6]) } @IBAction func tapLedonBtn(_ sender: Any) { write(byteValue: [7]) } @IBAction func tapBwrdBtn(_ sender: Any) { write(byteValue: [8]) } @IBAction func tapLedoffBtn(_ sender: Any) { write(byteValue: [9]) } @IBAction func tapSndBtn(_ sender: Any) { write(byteValue: [0]) } @IBAction func tapWalkBtn(_ sender: Any) { }
次にWALKボタンが押された時の処理とdragView上で指を動かした時の処理を追記します。
import UIKit import CoreBluetooth let kBLEService_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b" let kBLE_Characteristic_uuid_Tx = "beb5483e-36e1-4688-b7f5-ea07361b26a8" let MaxCharacters = 20 let BLEService_UUID = CBUUID(string: kBLEService_UUID) let BLE_Characteristic_uuid_Tx = CBUUID(string: kBLE_Characteristic_uuid_Tx) var txCharacteristic : CBCharacteristic? class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate { private let NORMAL = 0 private let WALK = 1 var mode:Int = 0 var counter:Int = 0 @IBOutlet weak var scanBtn: UIButton! @IBOutlet weak var connectBtn: UIButton! @IBOutlet weak var ltrnBtn: UIButton! @IBOutlet weak var fwrdBtn: UIButton! @IBOutlet weak var rtrnBtn: UIButton! @IBOutlet weak var leftBtn: UIButton! @IBOutlet weak var stopBtn: UIButton! @IBOutlet weak var rghtBtn: UIButton! @IBOutlet weak var ledonBtn: UIButton! @IBOutlet weak var bwrdBtn: UIButton! @IBOutlet weak var ledoffBtn: UIButton! @IBOutlet weak var sndBtn: UIButton! @IBOutlet weak var deviceNameLabel: UILabel! @IBOutlet weak var walkBtn: UIButton! @IBOutlet weak var dragView: UIView! @IBAction func tapScanBtn(_ sender: Any) { startScan() } @IBAction func tapConnectBtn(_ sender: Any) { self.centralManager.connect(blePeripheral, options: nil) } @IBAction func tapLtrnBtn(_ sender: Any) { write(byteValue: [1]) mode = NORMAL dragView.isUserInteractionEnabled = false dragView.backgroundColor = UIColor.lightGray } @IBAction func tapFwrdBtn(_ sender: Any) { write(byteValue: [2]) mode = NORMAL dragView.isUserInteractionEnabled = false dragView.backgroundColor = UIColor.lightGray } @IBAction func tapRtrnBtn(_ sender: Any) { write(byteValue: [3]) mode = NORMAL dragView.isUserInteractionEnabled = false dragView.backgroundColor = UIColor.lightGray } @IBAction func tapLeftBtn(_ sender: Any) { write(byteValue: [4]) mode = NORMAL dragView.isUserInteractionEnabled = false dragView.backgroundColor = UIColor.lightGray } @IBAction func tapStopBtn(_ sender: Any) { write(byteValue: [5]) mode = NORMAL dragView.isUserInteractionEnabled = false dragView.backgroundColor = UIColor.lightGray } @IBAction func tapRghtBtn(_ sender: Any) { write(byteValue: [6]) mode = NORMAL dragView.isUserInteractionEnabled = false dragView.backgroundColor = UIColor.lightGray } @IBAction func tapLedonBtn(_ sender: Any) { write(byteValue: [7]) } @IBAction func tapBwrdBtn(_ sender: Any) { write(byteValue: [8]) mode = NORMAL dragView.isUserInteractionEnabled = false dragView.backgroundColor = UIColor.lightGray } @IBAction func tapLedoffBtn(_ sender: Any) { write(byteValue: [9]) } @IBAction func tapSndBtn(_ sender: Any) { write(byteValue: [0]) } @IBAction func tapWalkBtn(_ sender: Any) { mode = WALK dragView.isUserInteractionEnabled = true dragView.backgroundColor = UIColor.blue } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { for touch in touches { let location = touch.location(in: dragView) Swift.print(location) } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { var byteArray:[UInt8] = [0x00, 0x00, 0x00, 0x00, 0x00] let width = dragView.frame.width let height = dragView.frame.height if(mode == WALK){ byteArray[0] = 127 for touch in touches { let location = touch.location(in: dragView) Swift.print(location) if(location.x > width/2){ byteArray[1] = 0 }else{ byteArray[1] = 1 } if(location.y < height*0.2){ byteArray[2] = 0 }else if(location.y > height*0.8){ byteArray[2] = 100 }else{ byteArray[2] = (UInt8)(100 * (location.y - height*0.2) / (height*0.6)) } counter=counter+1 if(counter%4==0){ write(byteValue: byteArray) } } } } var centralManager: CBCentralManager! var blePeripheral: CBPeripheral! var timer = Timer() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. centralManager = CBCentralManager(delegate: self, queue: nil) mode = NORMAL self.view.isMultipleTouchEnabled = true } func startScan() { print("Now Scanning...") self.timer.invalidate() centralManager.scanForPeripherals(withServices: nil , options: [CBCentralManagerScanOptionAllowDuplicatesKey:false]) Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(self.cancelScan), userInfo: nil, repeats: false) } @objc func cancelScan() { self.centralManager.stopScan() print("Scan Stopped") } func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { print("discovered device : \(peripheral)") if peripheral.name == "MyESP32" { blePeripheral = peripheral self.centralManager.connect(blePeripheral, options: nil) cancelScan() deviceNameLabel.text = "MyESP32" } } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { print("succeeded to connect") blePeripheral.delegate = self blePeripheral.discoverServices(nil) } func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { print("failed to connect") connectBtn.isUserInteractionEnabled = true connectBtn.backgroundColor = UIColor.blue } func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if ((error) != nil) { print("Error discovering services: \(error!.localizedDescription)") return } guard let services = peripheral.services else { return } for service in services { peripheral.discoverCharacteristics(nil, for: service) } print("Discovered Services: \(services)") } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { if ((error) != nil) { print("Error discovering services: \(error!.localizedDescription)") return } guard let characteristics = service.characteristics else { return } print("Found \(characteristics.count) characteristics!") for characteristic in characteristics { if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Tx){ txCharacteristic = characteristic print("Tx Characteristic: \(characteristic.uuid)") ltrnBtn.isUserInteractionEnabled = true ltrnBtn.backgroundColor = UIColor.blue fwrdBtn.isUserInteractionEnabled = true fwrdBtn.backgroundColor = UIColor.blue rtrnBtn.isUserInteractionEnabled = true rtrnBtn.backgroundColor = UIColor.blue leftBtn.isUserInteractionEnabled = true leftBtn.backgroundColor = UIColor.blue stopBtn.isUserInteractionEnabled = true stopBtn.backgroundColor = UIColor.blue rghtBtn.isUserInteractionEnabled = true rghtBtn.backgroundColor = UIColor.blue ledonBtn.isUserInteractionEnabled = true ledonBtn.backgroundColor = UIColor.blue bwrdBtn.isUserInteractionEnabled = true bwrdBtn.backgroundColor = UIColor.blue ledoffBtn.isUserInteractionEnabled = true ledoffBtn.backgroundColor = UIColor.blue sndBtn.isUserInteractionEnabled = true sndBtn.backgroundColor = UIColor.blue walkBtn.isUserInteractionEnabled = true walkBtn.backgroundColor = UIColor.blue } } } func write(byteValue: [UInt8]) { let data:[UInt8] = byteValue let writeData = NSData(bytes: data, length: byteValue.count) if let blePeripheral = blePeripheral{ if let txCharacteristic = txCharacteristic { print("write") blePeripheral.writeValue(writeData as Data, for: txCharacteristic, type: CBCharacteristicWriteType.withResponse) } } } func centralManagerDidUpdateState(_ central: CBCentralManager) { print("state: \(central.state)") if central.state == CBManagerState.poweredOn { print("Bluetooth Enabled") scanBtn.isUserInteractionEnabled = true scanBtn.backgroundColor = UIColor.blue } else { } } }
変更箇所の簡単な説明をします。
private let NORMAL = 0 private let WALK = 1 var mode:Int = 0 var counter:Int = 0
ドラッグコントロールの時と通常のボタンによる操縦の場合とで一応モードを分けています。counterはドラッグ時の座標アップデートがあまり頻繁にならないようにするための対策に使っています。
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { var byteArray:[UInt8] = [0x00, 0x00, 0x00, 0x00, 0x00] let width = dragView.frame.width let height = dragView.frame.height if(mode == WALK){ byteArray[0] = 127 for touch in touches { let location = touch.location(in: dragView) Swift.print(location) if(location.x > width/2){ byteArray[1] = 0 }else{ byteArray[1] = 1 } if(location.y < height*0.2){ byteArray[2] = 0 }else if(location.y > height*0.8){ byteArray[2] = 100 }else{ byteArray[2] = (UInt8)(100 * (location.y - height*0.2) / (height*0.6)) } counter=counter+1 if(counter%4==0){ write(byteValue: byteArray) } } } }
dragView上をドラッグするとtouchesMovedイベントが呼ばれます。指を動かすとdragView上の座標が取得できます。座標が領域の右側か左側か、縦方向の位置を0-100の値に変換してコマンドとして送信します。コマンドはbyteArray[0]:WALKモードであることの識別値(127)、byteArray[1]:右か左か(右:1 左:0)、byteArray[2]:縦方向位置(0-100)となっていて、ESP32側で歩行プログラムの配列から関節角度を計算します。
<= マイクロマシーン チュートリアル(15)
チュートリアルベース
マイクロマシーン チュートリアル (17) =>
Pingback: マイクロマシーン チュートリアル(17) | RoboCreators | Meuse Robotics
Pingback: ロボットキット チュートリアル インデックス | RoboCreators | Meuse Robotics
Pingback: マイクロマシーン チュートリアル(17) | cog | Meuse Robotics