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 上运行