Swift 3 arduino Uno HM-10 Ble - iPhone 上的通知

Posted

技术标签:

【中文标题】Swift 3 arduino Uno HM-10 Ble - iPhone 上的通知【英文标题】:Swift 3 arduino Uno HM-10 Ble - Notifications on iphone 【发布时间】:2017-05-29 21:32:45 【问题描述】:

我已经为连接到 BLE(Arduino uno 上的 HM-10)的 ios 做了一个应用程序。有温度、光和湿度传感器连接到 Arduino。问题是,iPhone 显示的数据与它在 Arduino 串行显示器上显示的数据相同。而我希望在单击“温度按钮”时显示温度传感器数据,并且对于每个其他传感器都是如此。我想不通的另一件事是,如果应用程序没有运行,我想要通知以防临时。下降到某个点。我该怎么做呢

请帮忙!!

这是 Arduino 代码(是否可以在一行中打印数据而不是在新行中一次又一次地打印)

int sensePin = A1;  //This is the Arduino Pin that will control Relay #1
int sensorValue = 0;    //The variable we will use to store the sensor input
//int sensePin = A0;  //This is the Arduino Pin that will control Relay #1
int sensorInput;    //The variable we will use to store the sensor input
double temp; 


void setup() 
  // put your setup code here, to run once:
  Serial.begin(9600); //Start the Serial Port at 9600 baud (default)


void loop() 
  // put your main code here, to run repeatedly: 
  sensorValue = analogRead(A1);    //read the analog sensor and store it

  sensorInput = analogRead(A0);    //read the analog sensor and store it
  temp = (double)sensorInput / 1024;       //find percentage of input reading
  temp = temp * 5;                 //multiply by 5V to get voltage
  temp = temp - 0.5;               //Subtract the offset 
  temp = temp * 100;               //Convert to degrees 


  if (temp > 28 ) 
   Serial.print("Current Temp is hot i.e.: ");
   Serial.println(temp);
  

  else if (temp < 28) 
    Serial.print("Current Temp is cold i.e.: ");
    Serial.println(temp);
  
   else
    Serial.print("Current Temp: ");
    Serial.println(temp);
    

if (sensorValue > 70 ) 
   Serial.print("Current Light is high i.e.: ");
   Serial.println(sensorValue);
  

  else if (sensorValue < 60) 
    Serial.print("Current Light is low i.e.: ");
    Serial.println(sensorValue);
  
   else
    Serial.print("Light seems good: ");
    Serial.println(sensorValue);
    


  //Serial.print("Current Temp: ");

  //Serial.println(temp);

  //Serial.print("Current Light: ");
  //Serial.println(sensorValue);

  //Serial.end();
  delay(10000);

这是 Iphone 代码

import UIKit
import CoreBluetooth
import QuartzCore

/// The option to add a \n or \r or \r\n to the end of the send message
enum MessageOption: Int 
    case noLineEnding,
         newline,
         carriageReturn,
         carriageReturnAndNewline


/// The option to add a \n to the end of the received message (to make it more readable)
enum ReceivedMessageOption: Int 
    case none,
         newline


final class SerialViewController: UIViewController, UITextFieldDelegate, BluetoothSerialDelegate 

//MARK: IBOutlets

    @IBOutlet weak var mainTextView: UITextView!

    @IBOutlet weak var messageField: UITextField!
    @IBOutlet weak var bottomView: UIView!
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint! // used to move the textField up when the keyboard is present
    @IBOutlet weak var barButton: UIBarButtonItem!
    @IBOutlet weak var navItem: UINavigationItem!




//MARK: Functions

    override func viewDidLoad() 
        super.viewDidLoad()

        // init serial
        serial = BluetoothSerial(delegate: self)

        // UI
        mainTextView.text = ""
        reloadView()

        NotificationCenter.default.addObserver(self, selector: #selector(SerialViewController.reloadView), name: NSNotification.Name(rawValue: "reloadStartViewController"), object: nil)

        // we want to be notified when the keyboard is shown (so we can move the textField up)
        NotificationCenter.default.addObserver(self, selector: #selector(SerialViewController.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(SerialViewController.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

        // to dismiss the keyboard if the user taps outside the textField while editing
        let tap = UITapGestureRecognizer(target: self, action: #selector(SerialViewController.dismissKeyboard))
        tap.cancelsTouchesInView = false
        view.addGestureRecognizer(tap)

        // style the bottom UIView
        bottomView.layer.masksToBounds = false
        bottomView.layer.shadowOffset = CGSize(width: 0, height: -1)
        bottomView.layer.shadowRadius = 0
        bottomView.layer.shadowOpacity = 0.5
        bottomView.layer.shadowColor = UIColor.gray.cgColor


    

    deinit 
        NotificationCenter.default.removeObserver(self)
    

    func keyboardWillShow(_ notification: Notification) 
        // animate the text field to stay above the keyboard
        var info = (notification as NSNotification).userInfo!
        let value = info[UIKeyboardFrameEndUserInfoKey] as! NSValue
        let keyboardFrame = value.cgRectValue

        //TODO: Not animating properly
        UIView.animate(withDuration: 1, delay: 0, options: UIViewAnimationOptions(), animations:  () -> Void in
            self.bottomConstraint.constant = keyboardFrame.size.height
            , completion:  Bool -> Void in
            self.textViewScrollToBottom()
        )
    

    func keyboardWillHide(_ notification: Notification) 
        // bring the text field back down..
        UIView.animate(withDuration: 1, delay: 0, options: UIViewAnimationOptions(), animations:  () -> Void in
            self.bottomConstraint.constant = 0
        , completion: nil)

    

    func reloadView() 
        // in case we're the visible view again
        serial.delegate = self

        if serial.isReady 
            navItem.title = serial.connectedPeripheral!.name
            barButton.title = "Disconnect"
            barButton.tintColor = UIColor.red
            barButton.isEnabled = true
         else if serial.centralManager.state == .poweredOn 
            navItem.title = "Bluetooth Serial"
            barButton.title = "Connect"
            barButton.tintColor = view.tintColor
            barButton.isEnabled = true
         else 
            navItem.title = "Bluetooth Serial"
            barButton.title = "Connect"
            barButton.tintColor = view.tintColor
            barButton.isEnabled = false
        
    

    func textViewScrollToBottom() 
        let range = NSMakeRange(NSString(string: mainTextView.text).length - 1, 1)
        mainTextView.scrollRangeToVisible(range)
        //mainTextView.text = "";
    


//MARK: BluetoothSerialDelegate

    func serialDidReceiveString(_ message: String) 
        // add the received text to the textView, optionally with a line break at the end
        mainTextView.text! += message
        let pref = UserDefaults.standard.integer(forKey: ReceivedMessageOptionKey)
        if pref == ReceivedMessageOption.newline.rawValue  mainTextView.text! += "\n" 
        textViewScrollToBottom()

    

    func serialDidDisconnect(_ peripheral: CBPeripheral, error: NSError?) 
        reloadView()
        dismissKeyboard()
        let hud = MBProgressHUD.showAdded(to: view, animated: true)
        hud?.mode = MBProgressHUDMode.text
        hud?.labelText = "Disconnected"
        hud?.hide(true, afterDelay: 1.0)
    

    func serialDidChangeState() 
        reloadView()
        if serial.centralManager.state != .poweredOn 
            dismissKeyboard()
            let hud = MBProgressHUD.showAdded(to: view, animated: true)
            hud?.mode = MBProgressHUDMode.text
            hud?.labelText = "Bluetooth turned off"
            hud?.hide(true, afterDelay: 1.0)
        
    


//MARK: UITextFieldDelegate

    func textFieldShouldReturn(_ textField: UITextField) -> Bool 
        if !serial.isReady 
            let alert = UIAlertController(title: "Not connected", message: "What am I supposed to send this to?", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default, handler:  action -> Void in self.dismiss(animated: true, completion: nil) ))
            present(alert, animated: true, completion: nil)
            messageField.resignFirstResponder()
            return true
        

        // send the message to the bluetooth device
        // but fist, add optionally a line break or carriage return (or both) to the message
        let pref = UserDefaults.standard.integer(forKey: MessageOptionKey)
        var msg = messageField.text!
        switch pref 
        case MessageOption.newline.rawValue:
            msg += "\n"
        case MessageOption.carriageReturn.rawValue:
            msg += "\r"
        case MessageOption.carriageReturnAndNewline.rawValue:
            msg += "\r\n"
        default:
            msg += ""
        

        // send the message and clear the textfield
        serial.sendMessageToDevice(msg)
        messageField.text = ""
        return true
    

    func dismissKeyboard() 
        messageField.resignFirstResponder()
    


//MARK: IBActions

    @IBAction func barButtonPressed(_ sender: AnyObject) 
        if serial.connectedPeripheral == nil 
            performSegue(withIdentifier: "ShowScanner", sender: self)
         else 
            serial.disconnect()
            reloadView()
        
    

CoreBluetooth 代码。

import UIKit
import CoreBluetooth
var serial: BluetoothSerial!   // Global serial handler, don't forget to initialize it with init(delgate:)
// Delegate functions
protocol BluetoothSerialDelegate 
// ** Required **

/// Called when de state of the CBCentralManager changes (e.g. when bluetooth is turned on/off)
func serialDidChangeState()

/// Called when a peripheral disconnected
func serialDidDisconnect(_ peripheral: CBPeripheral, error: NSError?)

// ** Optionals **

/// Called when a message is received
func serialDidReceiveString(_ message: String)

/// Called when a message is received
func serialDidReceiveBytes(_ bytes: [UInt8])

/// Called when a message is received
func serialDidReceiveData(_ data: Data)

/// Called when the RSSI of the connected peripheral is read
func serialDidReadRSSI(_ rssi: NSNumber)

/// Called when a new peripheral is discovered while scanning. Also gives the RSSI (signal strength)
func serialDidDiscoverPeripheral(_ peripheral: CBPeripheral, RSSI: NSNumber?)

/// Called when a peripheral is connected (but not yet ready for cummunication)
func serialDidConnect(_ peripheral: CBPeripheral)

/// Called when a pending connection failed
func serialDidFailToConnect(_ peripheral: CBPeripheral, error: NSError?)

/// Called when a peripheral is ready for communication
func serialIsReady(_ peripheral: CBPeripheral)

// Make some of the delegate functions optional extension BluetoothSerialDelegate 
func serialDidReceiveString(_ message: String) 
func serialDidReceiveBytes(_ bytes: [UInt8]) 
func serialDidReceiveData(_ data: Data) 
func serialDidReadRSSI(_ rssi: NSNumber) 
func serialDidDiscoverPeripheral(_ peripheral: CBPeripheral, RSSI: NSNumber?) 
func serialDidConnect(_ peripheral: CBPeripheral) 
func serialDidFailToConnect(_ peripheral: CBPeripheral, error: NSError?) 
func serialIsReady(_ peripheral: CBPeripheral) 


final class BluetoothSerial: NSObject,CBCentralManagerDelegate,CBPeripheralDelegate 

// MARK: Variables

/// The delegate object the BluetoothDelegate methods will be called upon
var delegate: BluetoothSerialDelegate!

/// The CBCentralManager this bluetooth serial handler uses for... well, everything really
var centralManager: CBCentralManager!

/// The peripheral we're trying to connect to (nil if none)
var pendingPeripheral: CBPeripheral?

/// The connected peripheral (nil if none is connected)
var connectedPeripheral: CBPeripheral?

/// The characteristic 0xFFE1 we need to write to, of the connectedPeripheral
weak var writeCharacteristic: CBCharacteristic?

/// Whether this serial is ready to send and receive data
var isReady: Bool 
    get 
        return centralManager.state == .poweredOn &&
               connectedPeripheral != nil &&
               writeCharacteristic != nil
    


/// Whether this serial is looking for advertising peripherals
var isScanning: Bool 
    return centralManager.isScanning


/// Whether the state of the centralManager is .poweredOn
var isPoweredOn: Bool 
    return centralManager.state == .poweredOn


/// UUID of the service to look for.
var serviceUUID = CBUUID(string: "FFE0")

/// UUID of the characteristic to look for.
var characteristicUUID = CBUUID(string: "FFE1")

/// Whether to write to the HM10 with or without response. Set automatically.
/// Legit HM10 modules (from JNHuaMao) require 'Write without Response',
/// while fake modules (e.g. from Bolutek) require 'Write with Response'.
private var writeType: CBCharacteristicWriteType = .withoutResponse


// MARK: functions

/// Always use this to initialize an instance
init(delegate: BluetoothSerialDelegate) 
    super.init()
    self.delegate = delegate
    centralManager = CBCentralManager(delegate: self, queue: nil)


/// Start scanning for peripherals
func startScan() 
    guard centralManager.state == .poweredOn else  return 

    // start scanning for peripherals with correct service UUID
    centralManager.scanForPeripherals(withServices: [serviceUUID], options: nil)

    // retrieve peripherals that are already connected
    // see this *** question http://***.com/questions/13286487
    let peripherals = centralManager.retrieveConnectedPeripherals(withServices: [serviceUUID])
    for peripheral in peripherals 
        delegate.serialDidDiscoverPeripheral(peripheral, RSSI: nil)
    


/// Stop scanning for peripherals
func stopScan() 
    centralManager.stopScan()


/// Try to connect to the given peripheral
func connectToPeripheral(_ peripheral: CBPeripheral) 
    pendingPeripheral = peripheral
    centralManager.connect(peripheral, options: nil)


/// Disconnect from the connected peripheral or stop connecting to it
func disconnect() 
    if let p = connectedPeripheral 
        centralManager.cancelPeripheralConnection(p)
     else if let p = pendingPeripheral 
        centralManager.cancelPeripheralConnection(p) //TODO: Test whether its neccesary to set p to nil
    


/// The didReadRSSI delegate function will be called after calling this function
func readRSSI() 
    guard isReady else  return 
    connectedPeripheral!.readRSSI()


/// Send a string to the device
func sendMessageToDevice(_ message: String) 
    guard isReady else  return 

    if let data = message.data(using: String.Encoding.utf8) 
        connectedPeripheral!.writeValue(data, for: writeCharacteristic!, type: writeType)
    


/// Send an array of bytes to the device
func sendBytesToDevice(_ bytes: [UInt8]) 
    guard isReady else  return 

    let data = Data(bytes: UnsafePointer<UInt8>(bytes), count: bytes.count)
    connectedPeripheral!.writeValue(data, for: writeCharacteristic!, type: writeType)


/// Send data to the device
func sendDataToDevice(_ data: Data) 
    guard isReady else  return 

    connectedPeripheral!.writeValue(data, for: writeCharacteristic!, type: writeType)



// MARK: CBCentralManagerDelegate functions

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) 
    // just send it to the delegate
    delegate.serialDidDiscoverPeripheral(peripheral, RSSI: RSSI)


func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) 
    // set some stuff right
    peripheral.delegate = self
    pendingPeripheral = nil
    connectedPeripheral = peripheral

    // send it to the delegate
    delegate.serialDidConnect(peripheral)

    // Okay, the peripheral is connected but we're not ready yet!
    // First get the 0xFFE0 service
    // Then get the 0xFFE1 characteristic of this service
    // Subscribe to it & create a weak reference to it (for writing later on), 
    // and find out the writeType by looking at characteristic.properties.
    // Only then we're ready for communication

    peripheral.discoverServices([serviceUUID])


func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) 
    connectedPeripheral = nil
    pendingPeripheral = nil

    // send it to the delegate
    delegate.serialDidDisconnect(peripheral, error: error as NSError?)


func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) 
    pendingPeripheral = nil

    // just send it to the delegate
    delegate.serialDidFailToConnect(peripheral, error: error as NSError?)


func centralManagerDidUpdateState(_ central: CBCentralManager) 
    // note that "didDisconnectPeripheral" won't be called if BLE is turned off while connected
    connectedPeripheral = nil
    pendingPeripheral = nil

    // send it to the delegate
    delegate.serialDidChangeState()



// MARK: CBPeripheralDelegate functions

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) 
    // discover the 0xFFE1 characteristic for all services (though there should only be one)
    for service in peripheral.services! 
        peripheral.discoverCharacteristics([characteristicUUID], for: service)
    


func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) 
    // check whether the characteristic we're looking for (0xFFE1) is present - just to be sure
    for characteristic in service.characteristics! 
        if characteristic.uuid == characteristicUUID 
            // subscribe to this value (so we'll get notified when there is serial data for us..)
            peripheral.setNotifyValue(true, for: characteristic)

            // keep a reference to this characteristic so we can write to it
            writeCharacteristic = characteristic

            // find out writeType
            writeType = characteristic.properties.contains(.write) ? .withResponse : .withoutResponse

            // notify the delegate we're ready for communication
            delegate.serialIsReady(peripheral)
        
    


func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) 
    // notify the delegate in different ways
    // if you don't use one of these, just comment it (for optimum efficiency :])
    let data = characteristic.value
    guard data != nil else  return 

    // first the data
    delegate.serialDidReceiveData(data!)

    // then the string
    if let str = String(data: data!, encoding: String.Encoding.utf8) 
        delegate.serialDidReceiveString(str)
     else 
        //print("Received an invalid string!") uncomment for debugging
    

    // now the bytes array
    var bytes = [UInt8](repeating: 0, count: data!.count / MemoryLayout<UInt8>.size)
    (data! as NSData).getBytes(&bytes, length: data!.count)
    delegate.serialDidReceiveBytes(bytes)


func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) 
    delegate.serialDidReadRSSI(RSSI)

完整代码如下:https://github.com/vari217/aw

【问题讨论】:

Core Bluetooth 编程指南中有一整章是关于在后台使用蓝牙的。您将需要您的应用实现后台模式并在收到相应数据时发布本地通知 以及如何单行打印数据/更新数据?? 一行是什么意思?您的意思是使用单个打印语句吗?为什么不简单地发送用逗号分隔的两个传感器值,然后由应用程序进行阈值判断? 【参考方案1】:

如果您只想在点击按钮时获取值,则必须阻止 Arduino 在收到 BT 的标志后执行此操作。

不要在主循环中使用这么多的延迟,10 秒太多了。弄清楚如何处理事件而不是保持循环,您可以处理其他任务。

我没有看到你的整个代码,但从你发布的内容来看,我会遵循这条路径。

对于您的 Arduino,请等待读取和假脱机,直到您的应用收到请求,如果不使用 CPU,请不要浪费时间。 如果您想每 XX 秒缓冲一次值,请放置一个例程,每 XX 秒验证一次传感器并将其保存在您的 rom 上(如果确实需要)。

如果没有,我的建议是:

char val;         // variable to receive data from the serial port [BT]
char buffer[50];
void setup()


  Serial.begin(115200);       // start serial communication at 115200bps



void loop() 

  if( Serial.available() )       // if data is available to read
  
    val = Serial.read();         // read it and store it in 'val'
  

  if( val == 'T' )               // if 'T' was received read temperature
  
    sensorInput = analogRead(A0);    //read the analog sensor and store it
    temp = (double)sensorInput / 1024;       //find percentage of input reading
    temp = temp * 5;                 //multiply by 5V to get voltage
    temp = temp - 0.5;               //Subtract the offset 
    temp = temp * 100;               //Convert to degrees 


    if (temp > 28 ) 

      sprintf(buffer, "Current Temp is hot i.e.: %d", temp);
      Serial.println(buffer);
    

    else if (temp < 28) 
      sprintf(buffer, "Current Temp is cold i.e.: %d", temp);
      Serial.println(buffer);
    
    else 
      sprintf(buffer, "Current Temp %d", temp);
      Serial.println(buffer);
    
  

  if( val == 'L' )               // if 'L' was received read light 
  
    sensorValue = analogRead(A1);    //read the analog sensor and store it
    if (sensorValue > 70 ) 
      sprintf(buffer, "Current Light is high i.e.: %d", sensorValue);
      Serial.println(buffer);
    
    else if (sensorValue < 60) 
      sprintf(buffer, "Current Light is low i.e.: %d", sensorValue);
      Serial.println(buffer);
    
    else 
      sprintf(buffer, "Light seems good: %d", sensorValue);
      Serial.println(buffer);      
    
  
  val = '';

对于您的 swift 代码,类似这样:

@IBAction func getTemperature(sender: AnyObject)

  var str:NSString = "T"
  data = str.dataUsingEncoding(NSUTF8StringEncoding)!
  peripheral.writeValue(data, forCharacteristic: arrCharacteristics!.objectAtIndex(1) as CBCharacteristic , type: CBCharacteristicWriteType.WithoutResponse)


@IBAction func getLight(sender: AnyObject)

  var str:NSString = "L"
  data = str.dataUsingEncoding(NSUTF8StringEncoding)!
  peripheral.writeValue(data, forCharacteristic: arrCharacteristics!.objectAtIndex(1) as CBCharacteristic , type: CBCharacteristicWriteType.WithoutResponse)

对于您关于通知的问题,我会遵循@Paulw11 的建议,iOS 背景存在一些陷阱,遵循教程是实现它的最佳方式。

关于单行打印的问题,请查看我的代码中使用的sprintf 函数。

【讨论】:

非常感谢您抽出宝贵的时间。下班后我会尽力按照你说的去做,并让你知道。再次感谢。 很高兴为您提供帮助。如果我的回答对您有所帮助,请不要忘记接受它作为您接受的答案。谢谢 谢谢,您的 Arduino 回答对我有很大帮助,直截了当,非常简单。再次感谢。我仍然没有得到iPhone。请你解释一下,我是ios新手。 ---- 我已经改变了你的代码,如下所示,我不知道它是否正确,如果我是正确的,我如何才能从数组中知道正确的字符 var _peripheral: CBPeripheral? var _characteristics: [CBCharacteristic]? @IBAction func getTemp(_ sender: Any) var str:NSString = "L" let data = str.data(using: String.Encoding.utf8.rawValue)! _peripheral.writeValue(data, forCharacteristic: arrCharacteristics!.objectAtIndex(1) as CBCharacteristic , type: CBCharacteristicWriteType.WithoutResponse) 您的serial = BluetoothSerial(delegate: self)“串行”类型是什么?它应该具有 write 方法,因为它是您的 BT 管理器。然后您可以将我的代码中的“外围设备”替换为“串行”。无论如何,您可以按照前一段时间在我与 BT 合作时指导我的本教程进行操作。 ladvien.github.io/robots/ios-manager

以上是关于Swift 3 arduino Uno HM-10 Ble - iPhone 上的通知的主要内容,如果未能解决你的问题,请参考以下文章

是否可以使用带有 Arduino 的 HM-10 或 HM-11 BLE 模块与 iOS 设备 (ANCS) 进行通信?

AT 命令无法通过 Arduino 在 BLE HM 10 上运行

HM10与Arduino和Android BLE之间的蓝牙低功耗大数据传输

arduino uno r3 上的这么多针脚都有啥用?

arduino uno r3 上的这么多针脚都有啥用

arduino uno/nano工作频率