#KBeacon IOS SDK Instruction DOC(English)
With this SDK, you can scan and configure the KBeacon device. The SDK include follow main class:
-
KBeaconsMgr: Global definition, responsible for scanning KBeacon devices advertisement packet, and monitoring the Bluetooth status of the system;
-
KBeacon: An instance of a KBeacon device, KBeaconsMgr creates an instance of KBeacon while it found a physical device. Each KBeacon instance has three properties: KBAdvPacketHandler, KBAuthHandler, KBCfgHandler.
-
KBAdvPacketHandler: parsing advertisement packet. This attribute is valid during the scan phase.
-
KBAuthHandler: Responsible for the authentication operation with the KBeacon device after the connection is established.
-
KBCfgHandler:Responsible for configuring parameters related to KBeacon devices
Scanning Stage
in this stage, KBeaconsMgr will scan and parse the advertisement packet about KBeacon devices, and it will create "KBeacon" instance for every founded devices, developers can get all advertisements data by its allAdvPackets or getAdvPacketByType function.
Connection Stage
After a KBeacon connected, developer can make some changes of the device by modifyConfig.
To make your development easier, we have an IOS demos in GitHub. They are:
- KBeaconProDemo_Ios: The app can scan KBeacon devices and configure iBeacon related parameters.
Development environment:
min IOS Version 11.0
kbeaconlib2 is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'kbeaconlib2','1.1.9'
This library is also open source, please refer to this link.
kbeaconlib
- Add the Bluetooth permissions declare in your project plist file (Target->Info). As follows:
- Privacy - Bluetooth Always Usage Description
- Privacy - Bluetooth Peripheral Usage Description
- Initialize KBeaconMgr instance in Activity, also your application should implementation the KBeaconMgr's KBeaconMgrDelegate.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
mBeaconsMgr = KBeaconsMgr.sharedBeaconManager
mBeaconsMgr!.delegate = self
...
}
- Implementation KBeaconMgrDelegate
func onBeaconDiscovered(beacons:[KBeacon])
{
for beacon in beacons
{
//found device
...
}
beaconsTableView.reloadData()
}
func onCentralBleStateChange(newState:BLECentralMgrState)
{
if (newState == BLECentralMgrState.PowerOn)
{
//the app can start scan in this case
NSLog("central ble state power on")
}
}
- Start scanning
After app startup, the BLE state was set to unknown, so the app should wait a few milliseconds before start scanning.
let scanResult = mBeaconsMgr!.startScanning()
if (scanResult)
{
NSLog("start scan success");
self.mScanButton.title = NSLocalizedString("ACTION_STOP_SCAN", comment:"")
}
else
{
NSLog("start scan failed");
}
- Implementation KBeaconMgr delegate to get scanning result.
The SDK will cache the last packet of each advertisement type that it scans, and it may cache up to 6 packet (iBeacon, URL, TLM, UID, KSensor, System). the application can call removeAdvPacket() in onBeaconDiscovered to delete the cached packet.
//example for print all scanned packet
func onBeaconDiscovered(beacons:[KBeacon])
{
for beacon in beacons
{
printScanPacket(beacon)
}
}
func printScanPacket(_ advBeacon: KBeacon)
{
//check if has packet
guard let allAdvPackets = advBeacon.allAdvPackets else{
return
}
print("--------scan device advertisment packet---------")
for advPacket in allAdvPackets
{
switch advPacket.getAdvType()
{
case KBAdvType.IBeacon:
//get majorID and minorID from advertisement packet
//notify: this is not standard iBeacon protocol, we get minor ID from KKM private
//scan response message
if let iBeaconAdv = advPacket as? KBAdvPacketIBeacon
{
print("-----iBeacon----")
print("major:\(iBeaconAdv.majorID)")
print("minor:\(iBeaconAdv.minorID)")
}
case KBAdvType.EddyURL:
if let urlAdv = advPacket as? KBAdvPacketEddyURL
{
print("-----URL----")
print("url:\(urlAdv.url)")
}
case KBAdvType.EddyUID:
if let uidAdv = advPacket as? KBAdvPacketEddyUID
{
print("-----UID----")
print("nid:\(uidAdv.nid ?? "")")
print("nid:\(uidAdv.sid ?? "")")
}
case KBAdvType.EddyTLM:
if let tlmAdv = advPacket as? KBAdvPacketEddyTLM
{
print("-----TLM----")
print("secondCount:\(tlmAdv.secCount/10)")
print("batt:\(tlmAdv.batteryLevel)")
print("temp:\(tlmAdv.temperature)")
print("temp:\(tlmAdv.temperature)")
}
case KBAdvType.Sensor:
if let sensorAdv = advPacket as? KBAdvPacketSensor
{
print("-----Sensor----")
//check if has battery level
if (sensorAdv.batteryLevel != KBCfgBase.INVALID_UINT16)
{
print("batt:\(sensorAdv.batteryLevel)")
}
//check if has temperature
if (sensorAdv.temperature != KBCfgBase.INVALID_FLOAT)
{
print("temp:\(sensorAdv.temperature)")
}
//check if has humidity
if (sensorAdv.humidity != KBCfgBase.INVALID_FLOAT)
{
print("humidity:\(sensorAdv.humidity)")
}
//check if has acc sensor
if let axisValue = sensorAdv.accSensor
{
print(" xAis:\(axisValue.xAis)")
print(" yAis:\(axisValue.yAis)")
print(" zAis:\(axisValue.zAis)")
}
//check if has pir indication
if (KBCfgBase.INVALID_UINT8 != sensorAdv.pirIndication)
{
print("PIR indication:\(sensorAdv.pirIndication)")
}
//check if has light level
if (KBCfgBase.INVALID_UINT16 != sensorAdv.luxLevel)
{
print("Light level:\(sensorAdv.luxLevel)")
}
}
case KBAdvType.System:
if let systemAdv = advPacket as? KBAdvPacketSystem
{
print("-----System----")
print("mac:\(systemAdv.macAddress!)")
print("batt:\(systemAdv.batteryPercent)")
print("modelNo:\(systemAdv.model)")
print("ver:\(systemAdv.firmwareVersion)")
}
case KBAdvType.EBeacon:
if let encryptAdv = advPacket as? KBAdvPacketEBeacon
{
print("-----EBeacon----")
print("Decrypt UUID:\(encryptAdv.uuid)")
print("ADV UTC:\(encryptAdv.utcSecCount)")
print("Reference power:\(encryptAdv.measurePower)")
}
default:
print("unknown packet")
}
}
//remove buffered packet
advBeacon.removeAdvPacket()
}
- Clean scanning result and stop scanning
After start scanning, The KBeaconMgr will buffer all found KBeacon device. If the app want to remove all buffered KBeacon device, the app can:
mBeaconsMgr!.clearBeacons()
If the app wants to stop scanning:
mBeaconsMgr!.stopScanning()
- If the app wants to change the device parameters, then it need connect to the device.
//connect to device with default parameters
self.beacon!.connect(beaconPwd, timeout: 15.0, delegate: self)
//or
//connect to device with specified parameters
//When the app is connected to the KBeacon device, the app can specify which the configuration parameters to be read,
//The parameter that can be read include: common parameters, advertisement parameters, trigger parameters, and sensor parameters
let connPara = KBConnPara()
connPara.syncUtcTime = true //sync the phone's time to device
connPara.readCommPara = true //only read basic parameters (KBCfgCommon)
connPara.readTriggerPara = false //not read trigger parameters
connPara.readSlotPara = false //not read advertisement parameters
connPara.readSensorPara = false
self.beacon!.connectEnhanced(beaconPwd, timeout: 15.0, connPara: connPara, delegate: self)
- Password: device password, the default password is 0000000000000000
- timeout: max connection time, unit is second.
- the app should implementation the KBeacon's delegate for get connection status:
func onConnStateChange(_ beacon:KBeacon, state:KBConnState, evt:KBConnEvtReason)
{
if (state == KBConnState.Connecting)
{
self.txtBeaconStatus.text = "Connecting to device";
}
else if (state == KBConnState.Connected)
{
self.txtBeaconStatus.text = "Device connected";
self.updateDeviceToView()
}
else if (state == KBConnState.Disconnected)
{
self.txtBeaconStatus.text = "Device disconnected";
if (evt == KBConnEvtReason.ConnAuthFail)
{
NSLog("auth failed");
self.showPasswordInputDlg(self.beacon!)
}
}
...
}
- Disconnect from the device.
self.beacon!.disconnect()
KBeacon devices can support broadcasting multiple type advertisement packets in parallel.
For example, advertisement type was set to “iBeacon + TLM + System”, then the device will send advertisement packet like follow.
Slot No. | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
Adv type |
iBeacon | TLM | System | None | None |
Adv Mode |
Legacy | Coded PHY | 2M PHY | Legacy | Legacy |
Adv Interval(ms) |
1022.5 | 8000.0 | 8000.0 | NA | NA |
Tx power(dBm) |
0 | 4 | -12 | NA | NA |
Notify:
For the advertisement period, Apple has some suggestions that make the device more easily discovered by IOS phones. (The suggest value was: 152.5 ms; 211.25 ms; 318.75 ms; 417.5 ms; 546.25 ms; 760 ms; 852.5 ms; 1022.5 ms; 1285 ms). For more information, please refer to Section 3.5 in "Bluetooth Accessory Design Guidelines for Apple Products". The document link: https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf.
After the app connect to KBeacon success. The KBeacon will automatically read current parameters from physical device. so the app can update UI and show the parameters to user after connection setup.
func onConnStateChange(_ beacon:KBeacon, state:KBConnState, evt:KBConnEvtReason)
{
...
if (state == KBConnState.Connected)
{
self.updateActionButton()
}
...
}
//update device's configuration to UI
func updateDeviceToView()
{
//if the device had read common parameters and advertisement parameters during connection,
//then the app can print the parameters
if let pCommonCfg = self.beacon!.getCommonCfg()
{
print("support max adv slot:\(pCommonCfg.getMaxSlot())")
print("support max trigger:\(pCommonCfg.getMaxTrigger())")
print("support iBeacon adv:\(pCommonCfg.isSupportIBeacon())")
print("support eddy URL adv:\(pCommonCfg.isSupportEddyURL())")
print("support eddy TLM adv:\(pCommonCfg.isSupportEddyTLM())")
print("support eddy UID adv :\(pCommonCfg.isSupportEddyUID())")
print("support KSensor adv:\(pCommonCfg.isSupportKBSensor())")
print("support System adv:\(pCommonCfg.isSupportKBSystem())")
print("support button:\(pCommonCfg.isSupportButton())")
print("support beep:\(pCommonCfg.isSupportBeep())")
print("support accSensor:\(pCommonCfg.isSupportAccSensor())")
print("support humidity:\(pCommonCfg.isSupportHumiditySensor())")
print("support max Tx power:\(pCommonCfg.getMaxTxPower())")
print("support min Tx power:\(pCommonCfg.getMinTxPower())")
print("support pir:\(pCommonCfg.isSupportPIRSensor())")
print("support light sensor:\(pCommonCfg.isSupportLightSensor())")
//adv type list
if let advSlotList = self.beacon!.getSlotCfgList(){
var advTypeDescs = ""
for advSlot in advSlotList{
let advDesc = KBAdvType.getAdvTypeString(advSlot.getAdvType())
let slotIndex = advSlot.getSlotIndex()
advTypeDescs = "\(advTypeDescs) | slot:\(slotIndex):\(advDesc)"
}
self.labelBeaconType.text = advTypeDescs
}
self.txtName.text = pCommonCfg.getName()
self.labelModel.text = pCommonCfg.getModel()
self.labelVersion.text = pCommonCfg.getVersion()
self.mLabelHardwareVersion.text = pCommonCfg.getHardwareVersion()
//check if has iBeacon advertisement para
if let iBeaconList = self.beacon!.getSlotCfgByAdvType(KBAdvType.IBeacon),
let firstIBeaconAdv = iBeaconList[0] as? KBCfgAdvIBeacon
{
self.txtTxPower.text = "\(firstIBeaconAdv.getTxPower())"
self.txtAdvPeriod.text = "\(firstIBeaconAdv.getAdvPeriod())"
self.txtBeaconUUID.text = "\(firstIBeaconAdv.getUuid() ?? "")"
self.txtBeaconMajor.text = "\(firstIBeaconAdv.getMajorID())"
self.txtBeaconMinor.text = "\(firstIBeaconAdv.getMinorID())"
}
else
{
print("does not found iBeacon configruation in device")
}
}
else
{
print("get common parameters failed, maybe the app does not requesting read common paramaters")
}
}
After app connects to device success, the app can update parameters of device.
The app can modify the basic parameters of KBeacon through the KBCfgCommon class. The KBCfgCommon has follow parameters:
- name: device name, the device name must <= 18 character
- alwaysPowerOn: if alwaysPowerOn was setting to true, the beacon will not allowed turn off by long press button.
- refPower1Meters: the rx power at 1 meters
- password: device password, the password length must >= 8 character and <= 16 character.
Warning:
Be sure to remember the new password, you won’t be able to connect to the device if you forget the new password.
Example: Update common parameters
func updateCommPara()
{
let commCfg = KBCfgCommon()
//check if parameters was changed
if (txtName.tag == DeviceViewController.TXT_DATA_MODIFIED),
let newName = txtName.text,
newName.count > 0
{
commCfg.setName(newName)
}
else
{
print("no need configruation")
return
}
//set device to always power on
//the autoAdvAfterPowerOn is enable, the device will not allowed power off by long press button
commCfg.setAlwaysPowerOn(true)
self.beacon!.modifyConfig(obj: commCfg, callback: { (result, exception) in
if (result)
{
print("config common para success")
}
else
{
print("config common para failed");
}
})
}
For all broadcast messages, such as iBeacon or Eddystone protocols, they include the following public parameters:
- slotIndex: the advertisement instance No.
- txPower: the tx power of the advertisement packet.
- advType: advertisement type, can be setting to iBeacon, KSesnor, Eddy TLM/UID/ etc.,
- advPeriod: this slot advertisement period, the value can be set to 100~20000ms
- advMode : advertisement mode.
- advTriggerOnly : When it is true, it means that this slot is not broadcast by default, it is only start broadcast when the Trigger event occurs.
- advConnectable: is this slot advertisement can be connectable.
Warning:
If all slot was setting to un-connectable, the app cannot connect to it again unless: 1. The KBeacon button was pressed while button trigger are not enable. or 2. The device was power on again and the device will be connectable in first 30 seconds after power on.
iBeacon parameters:
The app can enable iBeacon broadcast through the KBCfgIBeacon class. The KBCfgIBeacon has follow parameters:
- uuid: iBeacon UUID
- majorID: iBeacon major ID
- minorID: iBeacon minor ID
example: set the slot0 to broadcasting iBeacon packet
//example: update KBeacon to iBeacon
func updateIBeaconPara()
{
if (self.beacon!.state != KBConnState.Connected)
{
print("beacon not connected")
return;
}
let iBeaconCfg = KBCfgAdvIBeacon()
iBeaconCfg.setSlotIndex(0) //must be paramaters
//tx power
if let strTxPower = txtTxPower.text,
let nTxPower = Int(strTxPower)
{
iBeaconCfg.setTxPower(nTxPower)
}
else
{
self.showDialogMsg("error", message:"tx power is invalid")
return
}
//set adv period
if let strAdvPeriod = txtAdvPeriod.text,
let fAdvPeriod = Float(strAdvPeriod),
(fAdvPeriod <= KBCfgAdvIBeacon.MAX_ADV_PERIOD && fAdvPeriod >= KBCfgAdvIBeacon.MIN_ADV_PERIOD )
{
iBeaconCfg.setAdvPeriod(fAdvPeriod)
}
else
{
self.showDialogMsg("error", message:"adv period is invalid")
return
}
//modify ibeacon uuid
if let uuid = txtBeaconUUID.text,
uuid.isUUIDString()
{
iBeaconCfg.setUuid(uuid)
}
else
{
self.showDialogMsg("error", message:"uuid format is invalid")
return
}
//modify ibeacon major id
if let strMajorID = txtBeaconMajor.text,
let nMajor = UInt(strMajorID)
{
iBeaconCfg.setMajorID(nMajor)
}
else
{
self.showDialogMsg("error", message:"major id format is invalid")
return
}
//modify ibeacon major id
if let strMinorID = txtBeaconMinor.text,
let nMinor = UInt(strMinorID)
{
iBeaconCfg.setMinorID(nMinor)
}
else
{
self.showDialogMsg("error", message:"major id format is invalid")
return
}
//set always advertisement (not trigger only)
iBeaconCfg.setAdvTriggerOnly(false)
iBeaconCfg.setAdvConnectable(true) //allowed connectable
self.beacon!.modifyConfig(obj: iBeaconCfg, callback: { (result, exception) in
if (result)
{
self.showDialogMsg("success", message: "config success")
}
else if (exception != nil)
{
if (exception!.errorCode == KBErrorCode.CfgBusy)
{
NSLog("Config busy, please make sure other configruation complete")
}
else if (exception!.errorCode == KBErrorCode.CfgTimeout)
{
NSLog("Config timeout")
}
self.showDialogMsg("Failed", message:"config other error:\(exception!.errorCode)")
}
})
}
example: set the slot0/slot1 to hybrid iBeacon/EddyTLM.
sometimes we need KBeacon broadcasting both iBeacon and TLM packet (battery level, Temperature, power on times, etc., )
func updateKBeaconToIBeaconTLM_unconnectable()
{
if (self.beacon!.state != KBConnState.Connected)
{
NSLog("beacon not connected")
return
}
//iBeacon parameters
let iBeaconAdv = KBCfgAdvIBeacon()
iBeaconAdv.setSlotIndex(0)
iBeaconAdv.setTxPower(KBAdvTxPower.RADIO_Neg12dBm) //only used for nearby application
iBeaconAdv.setAdvConnectable(false) //not allowed connect
iBeaconAdv.setAdvPeriod(1000.0)
iBeaconAdv.setUuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")
iBeaconAdv.setMajorID(6454)
iBeaconAdv.setMinorID(1458)
//TLM parameters
let eddyTLMAdv = KBCfgAdvEddyTLM()
eddyTLMAdv.setSlotIndex(1)
eddyTLMAdv.setTxPower(KBAdvTxPower.RADIO_0dBm)
eddyTLMAdv.setAdvConnectable(false)
eddyTLMAdv.setAdvPeriod(8000.0)
//start configruation
self.beacon!.modifyConfig(array:[iBeaconAdv, eddyTLMAdv], callback: { (result, exception) in
if (result)
{
print("config iBeacon&TLM para success")
}
else
{
print("config iBeacon&TLM para failed");
}
})
}
The app can modify the eddystone parameters of KBeacon through the KBCfgEddyURL and KBCfgEddyUID class.
The KBCfgEddyURL has follow parameters:
- url: eddystone URL address
The KBCfgEddyUID has follow parameters:
- nid: namespace id about UID. It is 10 bytes length hex string value.
- sid: instance id about UID. It is 6 bytes length hex string value.
//example: update KBeacon to Eddy URL
func updateKBeaconToEddyURL()
{
if (self.beacon!.state != KBConnState.Connected)
{
NSLog("beacon not connected")
return
}
//iBeacon parameters
let eddyURLAdv = KBCfgAdvEddyURL()
eddyURLAdv.setSlotIndex(0)
eddyURLAdv.setAdvPeriod(1000.0)
eddyURLAdv.setTxPower(KBAdvTxPower.RADIO_Neg20dBm) //only for nearby (3~4 meters)
eddyURLAdv.setAdvConnectable(true)
eddyURLAdv.setUrl("https://www.google.com/")
//turn off slot 1 advertisement
let slot1NullAdv = KBCfgAdvNull()
slot1NullAdv.setSlotIndex(1)
//start configruation
let configArray = [eddyURLAdv, slot1NullAdv]
self.beacon!.modifyConfig(array:configArray, callback: { (result, exception) in
if (result)
{
print("config iBeacon&TLM para success")
}
else
{
print("config iBeacon&TLM para failed");
}
})
}
In some special cases, we may need to encrypt broadcast packets. Prevent third-party copies of counterfeit beacons. In addition, we may also need to encrypt broadcast content. By setting the broadcast type to EBeacon, we can encrypt the UUID, and the AES key will dynamically change according to the UTC time, so it can prevent copying and decryption.
//example: set device broadcasting encrypt UUID
func setSlot0AdvEncrypt(){
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),commCfg.isSupportEBeacon() else
{
print("device does not support encrypt advertisement")
return
}
//set basic parameters.
let encAdv = KBCfgAdvEBeacon()
encAdv.setSlotIndex(0)
encAdv.setAdvPeriod(1000)
encAdv.setTxPower(KBAdvTxPower.RADIO_0dBm)
//set the UUID that to be encrypt
encAdv.setUuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")
//Set the AES KEY to change every 5 seconds.
encAdv.setEncryptInterval(5)
//set aes type to 0(ECB)
encAdv.setAESType(KBCfgAdvEBeacon.AES_ECB_TYPE)
self.beacon?.modifyConfig(obj: encAdv, callback: { result, error in
if (result)
{
self.showDialogMsg("success", message: "config success")
}
else if (error != nil)
{
if (error!.errorCode == KBErrorCode.CfgBusy)
{
NSLog("Config busy, please make sure other configruation complete")
}
else if (error!.errorCode == KBErrorCode.CfgTimeout)
{
NSLog("Config timeout")
}
self.showDialogMsg("Failed", message:"config other error:\(error!.errorCode)")
}
})
}
In some cases, you may want Beacon to broadcast intermittently. For example, broadcasting for 5 seconds every 2 minutes.
Example: Beacon broadcasts 5 seconds every 2 minutes in Slot1. The advertisement interval is 1 second in advertisement period. That is, the Beacon sleeps for 115 seconds and then broadcasts for 5 seconds.
/**
* Example: Beacon broadcasts 5 seconds every 2 minutes in Slot1.
* The advertisement interval is 1 second in advertisement period.
* That is, the Beacon sleeps for 115 seconds and then broadcasts for 5 seconds.
*/
func setSlot0PeriodicIBeaconAdv(){
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportIBeacon(),
commCfg.isSupportTrigger(KBTriggerType.PeriodicallyEvent) else
{
print("device does not support iBeacon advertisement,or device does not support Periodically Event")
return
}
//setting slot1 parameters
let periodicAdv = KBCfgAdvIBeacon()
periodicAdv.setSlotIndex(1)
//set adv period, unit is ms
periodicAdv.setAdvPeriod(1000)
periodicAdv.setTxPower(KBAdvTxPower.RADIO_0dBm)
periodicAdv.setUuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")
/*
This parameter is very important, indicating that slot1 does
not broadcast by default and only broadcasts when triggered by a Trigger.
*/
periodicAdv.setAdvTriggerOnly(true)
//add periodically trigger
let periodicTrigger = KBCfgTrigger(0, triggerType: KBTriggerType.PeriodicallyEvent)
periodicTrigger.setTriggerAction(KBTriggerAction.Advertisement)
//trigger slot 1 advertisement
periodicTrigger.setTriggerAdvSlot(1)
//set adv duration to 5 seconds
periodicTrigger.setTriggerAdvTime(5);
//set trigger period, unit is ms
periodicTrigger.setTriggerPara(120*1000)
let cfgArray = [periodicAdv, periodicTrigger]
self.beacon?.modifyConfig(array: cfgArray, callback: { (result, exception) in
if (result)
{
self.showDialogMsg("success", message: "config success")
}
else if (exception != nil)
{
if (exception!.errorCode == KBErrorCode.CfgBusy)
{
NSLog("Config busy, please make sure other configruation complete")
}
else if (exception!.errorCode == KBErrorCode.CfgTimeout)
{
NSLog("Config timeout")
}
self.showDialogMsg("Failed", message:"config other error:\(exception!.errorCode)")
}
})
}
Sometimes, in order to reduce the time for configuration, The app can only sending the modified parameters.
Example: checking if the parameters was changed, then send new parameters to device.
func updateModifyParaToDevice()
{
if (!self.beacon!.isConnected())
{
return
}
//First we get the current configuration of SLOT0, and then we only need to send the parameters that modified.
if let oldIBeaconPara = self.beacon!.getSlotCfg(0) as? KBCfgAdvIBeacon
{
var bModification = false
let iBeaconCfg = KBCfgAdvIBeacon();
iBeaconCfg.setSlotIndex(0); //must be parameters
if (!oldIBeaconPara.isAdvConnectable()){
iBeaconCfg.setAdvConnectable(true)
bModification = true
}
if (oldIBeaconPara.isAdvTriggerOnly()){
iBeaconCfg.setAdvTriggerOnly(false)
bModification = true
}
if (oldIBeaconPara.getAdvPeriod() != 1280.0){
iBeaconCfg.setAdvPeriod(1280.0)
bModification = true
}
if (oldIBeaconPara.getTxPower() != KBAdvTxPower.RADIO_Neg4dBm){
iBeaconCfg.setTxPower(KBAdvTxPower.RADIO_Neg4dBm)
bModification = true
}
if (oldIBeaconPara.getUuid()!.compare("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") != .orderedSame){
iBeaconCfg.setUuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")
bModification = true
}
if (oldIBeaconPara.getMajorID() != 645){
iBeaconCfg.setMinorID(645)
bModification = true
}
if (oldIBeaconPara.getMinorID() != 741){
iBeaconCfg.setMinorID(741)
bModification = true
}
//send parameters to device
if (bModification)
{
self.beacon!.modifyConfig(obj:iBeaconCfg, callback: { (result, exception) in
if (result)
{
print("config iBeacon&TLM para success")
}
else
{
print("config iBeacon&TLM para failed");
}
})
}
else
{
print("no need config")
}
}
else
{
//...
}
}
For some KBeacon device that has some motion sensor, temperature&humidity sensor, push button, etc., The application can config the KBeacon to monitor some trigger event. For example, button was pressed, the temperature is too high, or device was motion. The KBeacon can do some action when the trigger condition was met.
Trigger No. | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
Type |
Btn single click | Btn double click | Motion | None | None |
Action |
advertisement | advertisement | advertisement | NA | NA |
Adv slot |
0 | 0 | 1 | NA | NA |
Para |
NA | NA | 4 | NA | NA |
Adv duration |
10 | 10 | 30 | NA | NA |
Adv interval |
400.0 | 1000.0 | 500.0 | NA | NA |
Adv TX power |
4 | -4 | 0 | NA | NA |
The trigger advertisement has follow parameters:
- Trigger No: Trigger instance number, the device supports up to 5 Triggers by default, the No is 0 ~ 4.
- Trigger type: Trigger event type
- Trigger action: Action when trigger event happened. For example: start broadcast, make a sound, or send a notification to the connected App.
- Trigger Adv slot: When the Trigger event happened, which advertisement Slot starts to broadcasting
- Trigger parameters: For motion trigger, the parameter is acceleration sensitivity. For temperature above trigger, you can set to the temperature threshold.
- Trigger Adv duration: The advertisement duration when trigger event happened. Unit is second.
- Trigger Adv TX power: The advertisement TX power when trigger event happened. Unit is dBm.
- Trigger Adv interval: The advertisement interval when trigger event happened. Unit is ms.
Example 1: Trigger only advertisment
The device usually does not broadcast by default, and we want to trigger the broadcast when the button is pressed.
1. Setting slot 0 to iBeacon advertisement(adv period = 211.25ms, trigger only adv = true).
2. Add a single button trigger(Trigger No = 0, Trigger type = Btn single click, Action = advertisement, Adv slot = 0, Adv duration = 20).
Example 2: Trigger advertisment
For some scenario, we need to continuously monitor the KBeacon to ensure that the device was alive. The device usually broadcasting iBeacon1(UUID=xxx1) , and we want to trigger the broadcast iBeacon2(uuid=xxx2) when the button is pressed.
1. Setting slot 0 to iBeacon advertisement(uuid=xxx1, adv period = 1280ms, trigger only adv = false).
2. Setting slot 1 to iBeacon advertisement(uuid=xxx2, adv period = 211.25ms, trigger only adv = true).
We set an larger advertisement interval during alive advertisement and a short advertisement interval when trigger event happened, so we can achieve a balance between power consumption and triggers advertisement be easily detected.
3. Add a single button trigger(Trigger No = 0, Trigger type = Btn single click, Action = advertisement, Adv slot = 1, Adv duration = 20).
The push button trigger feature is used in some hospitals, nursing homes and other scenarios. When the user encounters some emergency event(SOS button), they can click the button and the KBeacon device will start broadcast or the KBeacon device send the click event to connected Android/IOS app. The app can configure single click, double-click, triple-click, long-press the button trigger, oor a combination.
Notify:
- By KBeacon's default setting, long press button used to power on and off. Clicking button used to force the KBeacon enter connectable broadcast advertisement. So when you enable the long-press button trigger, the long-press power off function will be disabled. When you turn on the single/double/triple click trigger, the function of clicking to enter connectable broadcast state will also be disabled. After you disable button trigger, the default function about long press or click button will take effect again.
When you set multiple triggers to the same slot broadcast, you can turn on the Trigger content change mode. When different triggers are triggered, the content of UUID will change by UUID + trigger type. - iBeacon UUID for single click trigger = iBeacon UUID + 0x4
- iBeacon UUID for single double click trigger = iBeacon UUID + 0x5
- iBeacon UUID for single triple click trigger = iBeacon UUID + 0x6
- iBeacon UUID for single long press trigger = iBeacon UUID + 0x3
- Enable or button trigger event to advertisement.
//enable button press trigger event to slot0 advertisement
func enableBtnTriggerEvtToSlot1Advertisement()
{
//check if device can support button trigger capibility
if let commCfg = self.beacon!.getCommonCfg(),
!(commCfg.isSupportTrigger(KBTriggerType.BtnSingleClick))
{
self.showDialogMsg("Fail", message: "device does not support button trigger")
return
}
//slot 0 default advertisement (alive advertisement)
let slot0DefaultAdv = KBCfgAdvIBeacon()
slot0DefaultAdv.setSlotIndex(0)
slot0DefaultAdv.setAdvPeriod(2000.0)
slot0DefaultAdv.setTxPower(KBAdvTxPower.RADIO_0dBm)
slot0DefaultAdv.setAdvConnectable(true)
slot0DefaultAdv.setAdvTriggerOnly(false) //always advertisement
slot0DefaultAdv.setUuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")
slot0DefaultAdv.setMajorID(1)
slot0DefaultAdv.setMinorID(4)
//trigger index is 0
let btnTriggerPara = KBCfgTrigger(0, triggerType: KBTriggerType.BtnSingleClick)
//set trigger action to app
btnTriggerPara.setTriggerAction(KBTriggerAction.Advertisement)
btnTriggerPara.setTriggerAdvSlot(0)
btnTriggerPara.setTriggerAdvTime(10) //advertisement 10 seconds
btnTriggerPara.setTriggerAdvChangeMode(KBTriggerAdvChgMode.KBTriggerAdvChangeModeUUID)
//option trigger para, if the following parameters are omited
//the trigger broadcasting interval is 2000ms and the TX power is 0dBm
btnTriggerPara.setTriggerAdvPeriod(200.0)
btnTriggerPara.setTriggerAdvTxPower(KBAdvTxPower.RADIO_Neg4dBm)
let configArray = [btnTriggerPara, slot0DefaultAdv]
self.beacon!.modifyConfig(array:configArray) { (result, exception) in
if (result)
{
self.showDialogMsg("success", message: "Config button trigger success")
}
else
{
self.showDialogMsg("Error", message: "Config button trigger failed")
}
}
}
- Enable device send button trigger event to connected Andoird/IOS application
In some scenarios, our app will always be connected to the KBeacon device. We need the app can receive a press notification event when the button is pressed.
//implementation KBNotifyDataDelegate
class DeviceViewController : KBNotifyDataDelegate
{
}
//enable button press trigger event to application
func enableBtnTriggerEvtToApp()
{
//check if device can support button trigger capibility
if let commCfg = self.beacon!.getCommonCfg(),
!(commCfg.isSupportTrigger(KBTriggerType.BtnSingleClick))
{
self.showDialogMsg("Fail", message: "device does not support button trigger")
return
}
//trigger index is 0
let btnTriggerPara = KBCfgTrigger(0, triggerType: KBTriggerType.BtnSingleClick)
//set trigger action to app
btnTriggerPara.setTriggerAction(KBTriggerAction.ReportToApp)
self.beacon!.modifyConfig(obj: btnTriggerPara) { (result, exception) in
if (result)
{
print("config trigger success")
}
else
{
print("config trigger failed");
}
}
}
//handle trigger event notify
func onNotifyDataReceived(_ beacon:KBeacon, evt:Int, data:Data)
{
NSLog("recieve event:\(evt), content:\(data.count)")
}
- The app can disable the button trigger by
//disable button trigger
@IBAction func onDisableButtonTrigger(_ sender: Any)
{
if (self.beacon!.state != KBConnState.Connected){
print("device does not connected")
return
}
//turn off trigger instance 0
let btnTriggerPara = KBCfgTrigger(0, triggerType: KBTriggerType.TriggerNull)
btnTriggerPara.setTriggerAction(0)
self.beacon!.modifyConfig(obj: btnTriggerPara) { (result, exception) in
if (result)
{
print("Turn off btn trigger success")
}
else
{
print("Turn off btn trigger failed");
}
}
}
The KBeacon can start broadcasting when it detects motion. Also the app can setting the sensitivity of motion detection.
Notify:
- When the KBeacon enable the motion trigger, the Acc feature(X, Y, and Z axis detected function) in the KSensor broadcast will be disabled.
Enabling motion trigger is similar to push button trigger, which will not be described in detail here.
// the iBeacon broadcast duration is 10 seconds.
@IBAction func onEnableMotionTrigger(_ sender: Any)
{
//check if device can support motion trigger capibility
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportTrigger(KBTriggerType.AccMotion) else
{
print("not allowed to modify motion parameters")
return
}
//trigger index is 0
let accTriggerPara = KBCfgTriggerMotion()
//set trigger action to app
accTriggerPara.setTriggerAction(KBTriggerAction.Advertisement)
accTriggerPara.setTriggerAdvSlot(0)
accTriggerPara.setTriggerAdvChangeMode(KBTriggerAdvChgMode.KBTriggerAdvChangeModeUUID)
accTriggerPara.setTriggerAdvTime(60) //advertisement 5 seconds
accTriggerPara.setTriggerAdvPeriod(200.0)
accTriggerPara.setTriggerAdvTxPower(KBAdvTxPower.RADIO_Neg4dBm)
//add acc motion para
accTriggerPara.setTriggerPara(5) //motion sensitivity, unit is 16mg
accTriggerPara.setAccODR(KBCfgTriggerMotion.ACC_ODR_25_HZ)
accTriggerPara.setWakeupDuration(5)
//we assumption the slot1 already config to iBeacon parameters
//otherwise you need to config the slot1 parameters
//please reference the enableBtnTriggerEvtToSlot1Advertisement for configruation slot1
//...
//enable motion trigger
self.beacon!.modifyConfig(obj:accTriggerPara) { (result, exception) in
if (result)
{
print("Enable motion trigger success")
}
else
{
print("Enable motion trigger failed");
}
}
}
The app can configure KBeacon to start broadcasting after detecting an abnormality humidity&temperature. For example, the temperature exceeds a specified threshold, or the temperature is below a certain threshold. Currently supports the following Trigger
- HTTempAbove
- HTTempBelow
- HTHumidityAbove
- HTHumidityBelow
- Start advertisement when temperature above trigger happened.
@IBAction func onTHTrigger2Adv(_ sender: Any)
{
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportTrigger(KBTriggerType.HTTempAbove) else
{
print("not allowed to modify TH parameters")
return
}
//trigger parameters
let triggerAdv = KBCfgTrigger(0, triggerType: KBTriggerType.HTTempAbove)
triggerAdv.setTriggerAction(KBTriggerAction.Advertisement)
triggerAdv.setTriggerAdvSlot(1) //please makesure the slot 1 was config
triggerAdv.setTriggerAdvTime(10)
triggerAdv.setTriggerPara(30) //trigger when temperature > 30 Celsius degree
//config slot 1 parameters
let slot1TriggerAdv = KBCfgAdvIBeacon()
slot1TriggerAdv.setSlotIndex(1)
slot1TriggerAdv.setAdvPeriod(152.5)
slot1TriggerAdv.setTxPower(KBAdvTxPower.RADIO_0dBm)
slot1TriggerAdv.setAdvConnectable(false)
slot1TriggerAdv.setAdvTriggerOnly(true) //only advertisement when trigger happened
slot1TriggerAdv.setUuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E3")
slot1TriggerAdv.setMajorID(0)
slot1TriggerAdv.setMinorID(2)
//set trigger
let configArray = [triggerAdv, slot1TriggerAdv]
self.beacon!.modifyConfig(array:configArray) { (result, exception) in
if (result)
{
self.showDialogMsg("success", message: "Enable temp above trigger success")
}
else
{
self.showDialogMsg("error", message: "Enable temp above trigger failed")
}
}
}
- Report event to app when temperature above trigger event happened.
//After enable realtime data to app, then the device will periodically send the temperature and humidity data to app whether it was changed or not.
@IBAction func onTHTriggerEvt2App(_ sender: Any)
{
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportHumiditySensor() else
{
print("not allowed to set trigger")
return
}
let triggerApp = KBCfgTrigger(0, triggerType: KBTriggerType.HTTempAbove)
triggerApp.setTriggerAction(KBTriggerAction.ReportToApp)
triggerApp.setTriggerPara(50) //trigger an event to app when temperature > 50 Celsius
self.beacon!.modifyConfig(obj:triggerApp) { (result, exception) in
if (result)
{
//subscribe HT notification
self.beacon!.subscribeSensorDataNotify(KBTriggerType.HTRealTimeReport, notifyDelegate: self) { (result, exception) in
if (result){
print("subscribe trigger notification success")
}else{
print("subscribe trigger notification failed")
}
}
}
else
{
print("enable trigger failed")
}
}
}
- Report temperature&humidity to app periodically
//After enable periodically trigger, then the device will periodically send the temperature and humidity data to app whether it was changed or not.
func enableTHPeriodicallyTriggerRpt2App(_ sender: Any)
{
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportTrigger(KBTriggerType.HTHumidityPeriodically) else
{
print("not allowed to set trigger")
return
}
let triggerApp = KBCfgTrigger(0, triggerType: KBTriggerType.HTHumidityPeriodically)
triggerApp.setTriggerAction(KBTriggerAction.ReportToApp)
triggerApp.setTriggerPara(60) //tx measure result every 60 seconds
self.beacon!.modifyConfig(obj:triggerApp) { (result, exception) in
if (result)
{
//subscribe HT notification
self.beacon!.subscribeSensorDataNotify(KBTriggerType.HTHumidityPeriodically, notifyDelegate: self) { (result, exception) in
if (result){
print("subscribe trigger notification success")
}else{
print("subscribe trigger notification failed")
}
}
}
else
{
print("enable trigger failed")
}
}
}
The Cutoff trigger is suitable for tamper-evident beacon such as W3, W7. Or Door beacon such as the S1.
When the cut-off was detected, the beacon will send the specfic advertisement to the cloud/backend and trigger the alert, the administrator will response and help.
*Wristband Beacon
*CutoffWatchband
- CutoffWatchband
func onEnableCutoffTrigger()
{
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportTrigger(KBTriggerType.CutoffWatchband) else
{
print("device does not support cut off trigger")
return
}
//enable cutoff trigger
let cutoffTrigger = KBCfgTrigger(0, triggerType: KBTriggerType.Cutoff)
cutoffTrigger.setTriggerAction(KBTriggerAction.Advertisement)
cutoffTrigger.setTriggerAdvSlot(0) //please makesure the slot 0 was setting
cutoffTrigger.setTriggerAdvTime(10)
cutoffTrigger.setTriggerAdvChangeMode(KBTriggerAdvChgMode.KBTriggerAdvChangeModeUUID)
self.beacon!.modifyConfig(obj: cutoffTrigger) { (result, exception) in
if (result)
{
print("Enable cutoff trigger success")
}
else
{
print("Enable cutoff trigger failed")
}
}
}
PIR trigger is a detection based on human infrared. The S2 product can detect whether the human body is moving within the range of 0-8 meters. You can set the device to trigger a broadcast when it detected human body moves. You can also set a Trigger to record the event when a human body is detected to move. The S2 device can record up to 40,000 trigger events.
func onEnablePIRTrigger()
{
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportTrigger(KBTriggerType.PIRBodyInfraredDetected) else
{
print("device does not support cut off trigger")
return
}
//Save the PIR event to memory flash and report it to the APP at the same time
let pirTrigger = KBCfgTrigger(0, triggerType: KBTriggerType.PIRBodyInfraredDetected)
pirTrigger.setTriggerAction(KBTriggerAction.Record | KBTriggerAction.ReportToApp)
//If the human infrared is repeatedly detected within 30 seconds, it will no longer be record/reported.
pirTrigger.setTriggerPara(30);
self.beacon!.modifyConfig(obj: pirTrigger) { (result, exception) in
if (result)
{
print("Enable pir trigger success")
}
else
{
print("Enable pir trigger failed")
}
}
}
The S3 device can simultaneously detect light level and human body infrared (PIR). You can set up S3 devices to broadcast light level information. You can also set it to trigger a broadcast when the detected light level exceeds or falls below a specified threshold. S3 devices can record trigger events, up to 4000 trigger events can be recorded.
@IBAction func onEnableLightBelowTrigger(_ sender: Any) {
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportTrigger(KBTriggerType.LightLUXBelow) else
{
print("device does not support light sensor")
return
}
//Save the light below event to memory flash and report it to the APP at the same time
let lightTrigger = KBCfgTrigger(0, triggerType: KBTriggerType.LightLUXBelow)
lightTrigger.setTriggerAction(KBTriggerAction.Record | KBTriggerAction.ReportToApp)
//If the light level < 50, it will trigger an record/report event.
lightTrigger.setTriggerPara(50);
self.beacon!.modifyConfig(obj: lightTrigger) { (result, exception) in
if (result)
{
print("Enable light trigger success")
}
else
{
print("Enable light trigger failed")
}
}
}
For some KBecon devices with 3-axis accelerometers, you can set the device to trigger based on the tilt angle.
Through the Tilt angle trigger, we can set an alarm when the product's tilt angle is below or above a specified threshold. Also, if the product remains in this tilted state, the trigger can also report it repeatedly.
The tilt below angle range is 90 degrees to -90 degrees.
When the product is placed upright, the angle is 90 degrees, and when the product is inverted, it is -90 degrees.
func enableAccAngleTrigger()
{
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportTrigger(KBTriggerType.AccAngle) else
{
print("device does not support cut off trigger")
return
}
//set tilt angle trigger
let angleTrigger = KBCfgTriggerAngle()
angleTrigger.setTriggerAction(KBTriggerAction.Advertisement | KBTriggerAction.ReportToApp)
angleTrigger.setTriggerAdvSlot(0)
//When the Beacon tilt angle <= below angle threshold(45), a trigger event is sent.
angleTrigger.setTriggerPara(45)
//above angle
angleTrigger.setAboveAngle(90)
angleTrigger.setReportingInterval(5) //set repeat report interval to 5 minutes
self.beacon!.modifyConfig(obj: angleTrigger) { (result, exception) in
if (result)
{
print("Enable angle trigger success")
}
else
{
print("Enable angle trigger failed")
}
}
}
If the device has sensors, such as temperature and humidity sensors, we may need to setting the sensor parameters, such as the measurement interval. There are also some beacons, which can save sensor events to non-volatile memory, so that the app or gateway can obtain these historical records. Therefore, we may need to configure the conditions for recording events, such as recording an event when the temperature changes by more than 3 degrees.
For temperature and humidity sensors, we can set the measurement interval. In addition, we can use the device as a temperature and humidity Logger, and we can set the log conditions.
func setTHSensorMeasureParameters()
{
if (!self.beacon!.isConnected())
{
print("Device is not connected")
return
}
//check device capability
if let oldCommonCfg = self.beacon!.getCommonCfg(),
oldCommonCfg.isSupportHumiditySensor()
{
print("Device does not supported ht sensor")
return
}
let sensorHTPara = KBCfgSensorHT()
//enable humidity sensor
sensorHTPara.setLogEnable(true)
//unit is second, set measure temperature and humidity interval
sensorHTPara.setMeasureInterval(3)
//set log interval
sensorHTPara.setLogInterval(300)
//////If the following two parameters are set to 0, the Beacon will log temperature and humidity records every 300 seconds.
//unit is 0.1%, if abs(current humidity - last saved humidity) > 3, then log new record
sensorHTPara.setHumidityLogThreshold(30)
//unit is 0.1 Celsius, if abs(current temperature - last saved temperature) > 0.5, then log new record
sensorHTPara.setTemperatureLogThreshold(5)
self.beacon!.modifyConfig(obj: sensorHTPara) { (result, exception) in
if (result)
{
print("update ht parameters success")
}
else
{
print("update ht parameters failed")
}
}
}
For device has light sensors, we can set the measurement interval. The shorter the measurement interval, the greater the power consumption. In addition, we can use the device as a light level Logger, and we can set the log conditions.
func setLightSensorMeasureParameters()
{
if (!self.beacon!.isConnected())
{
print("Device is not connected")
return
}
//check device capability
if let oldCommonCfg = self.beacon!.getCommonCfg(),
oldCommonCfg.isSupportLightSensor()
{
print("Device does not supported light sensor")
return
}
let sensorPara = KBCfgSensorLight()
//enable light logger
sensorPara.setLogEnable(true)
//unit is second, set measure interval
sensorPara.setMeasureInterval(3)
//if abs(current light level - last saved light level) > 30, then save new record
sensorPara.setLogChangeThreshold(30)
self.beacon!.modifyConfig(obj: sensorPara) { (result, exception) in
if (result)
{
print("update light parameters success")
}
else
{
print("update light parameters failed")
}
}
}
For some sensors, we may not want it to work all the time, such as the Door sensor, we may only want it to work at night. The advantage of this is, the power consumption can be reduced, and the unnecessary trigger can also be reduced. The sensors that support configuring sleep period include: Door sensor(S1), PIR sensor(S2).
//set disable period parameters
func setAlarmSleepPeriod()
{
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportAlarmSensor() else
{
print("device does not support alarm trigger")
return
}
let sensorPara = KBCfgSensorBase()
sensorPara.setSensorType(KBSensorType.Alarm)
//set disable period from 8:00AM to 20:00 PM
let disablePeriod = KBTimeRange()
disablePeriod.localStartHour = 20
disablePeriod.localStartMinute = 0
disablePeriod.localEndHour = 8
disablePeriod.localEndMinute = 0
sensorPara.setDisablePeriod0(disablePeriod)
self.beacon!.modifyConfig(obj: sensorPara) { (result, exception) in
if (result)
{
print("Enable disable period success")
}
else
{
print("Enable disable period failed")
}
}
}
For the parking sensor, we can use this sensor to monitor if there is a vehicle parked at a specified location
//set parking idle
//Parking sensors need to be marked before use. That is, when there is no parking, we need to set
// the sensor to idle. The sensor detects if a vehicle is parked based on the status of the marker.
func setParkingIdleParameters(){
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportGEOSensor()
else
{
print("Device does not supported parking sensors")
return
}
let sensorGeoPara = KBCfgSensorGEO()
//If this parameter is set to true, the sensor initiates the measurement
// and sets the current state to the idle parking state.
sensorGeoPara.setParkingTag(true)
self.beacon?.modifyConfig(obj: sensorGeoPara, callback: { result, error in
if (result)
{
self.showDialogMsg("success", message: "config success")
}
else if (error != nil)
{
if (error!.errorCode == KBErrorCode.CfgBusy)
{
NSLog("Config busy, please make sure other configruation complete")
}
else if (error!.errorCode == KBErrorCode.CfgTimeout)
{
NSLog("Config timeout")
}
self.showDialogMsg("Failed", message:"config other error:\(error!.errorCode)")
}
})
}
func setParkingSensorMeasureParameters() {
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportGEOSensor()
else
{
print("Device does not supported parking sensors")
return
}
let sensorGeoPara = KBCfgSensorGEO()
//Set the geomagnetic offset value of the parking space occupancy relative to the idle parking space
//unit is mg
sensorGeoPara.setParkingThreshold(2000)
//If the setting continuously detects geomagnetic changes for more than 50 seconds,
//the device will generate a parking space occupancy event. the Delay unit is 10 seconds
sensorGeoPara.setParkingDelay(5)
self.beacon?.modifyConfig(obj: sensorGeoPara, callback: { result, error in
if (result)
{
self.showDialogMsg("success", message: "config success")
}
else if (error != nil)
{
if (error!.errorCode == KBErrorCode.CfgBusy)
{
NSLog("Config busy, please make sure other configruation complete")
}
else if (error!.errorCode == KBErrorCode.CfgTimeout)
{
NSLog("Config timeout")
}
self.showDialogMsg("Failed", message:"config other error:\(error!.errorCode)")
}
})
}
The repeater solution refers to Beacon's support for scanning surrounding Beacon while broadcasting iBeacon. When Beacon signals from surrounding devices are scanned, the ID of the surrounding Beacon can be carried in the broadcast message and sent to the Gateway. This approach has the following advantages:
- Expand the positioning range of Beacon. Beacons in weak indoor coverage areas may not be scanned by the gateway. Other beacons will act as repeaters to send the signal of that beacon to the gateway. The gateway can locate the Beacon in weak coverage areas.
- Increase the positioning accuracy of Beacon. Multiple Anchor Beacons can be deployed in fixed locations, and mobile Beacons can periodically scan for Anchor Beacons and send the scanned Anchor Beacon information to the gateway. The gateway can accurately locate the mobile Beacons based on the Anchor Beacon information.
func enableRepeaterScanner()
{
guard self.beacon!.isConnected(),
let commCfg = self.beacon!.getCommonCfg(),
commCfg.isSupportScanSensor()
else
{
print("Device does not support scan sensors")
return
}
// set scanner parameters
let scanPara = KBCfgSensorScan()
// set scan duration 1seconds, unit is 10 ms
scanPara.setScanDuration(100)
//only scan BLE4.0 legacy advertisement
scanPara.setScanModel(KBAdvMode.Legacy)
scanPara.setScanRssi(-80) //Scan devices with signals greater than -80dBm
//The scanning advertisement channel mask is 3 bit, channel 37(bit0), channel 38(bit1)
// channel 39(bit2). if the chanel bit is 1, then the Beacon will not scan on the channel
//for example, if the advChannelMask is 0x3(0B'011), then the beacon only scan BLE channel 37
scanPara.setScanChanelMask(3)
//The maximum number of peripheral devices during each scan
// When the number of devices scanned exceed 20, then stop scanning.
scanPara.setScanMax(20);
// Set a Trigger to periodically trigger scanning
let periodicTrigger = KBCfgTrigger(0, triggerType: KBTriggerType.PeriodicallyEvent);
//When a trigger occurs, it triggers a BLE scan and carries the scanned parameters in the broadcast.
periodicTrigger.setTriggerAction(KBTriggerAction.BLEScan | KBTriggerAction.Advertisement);
periodicTrigger.setTriggerAdvSlot(0)
periodicTrigger.setTriggerAdvPeriod(500.0)
periodicTrigger.setTriggerAdvTime(10)
periodicTrigger.setTriggerAdvTxPower(0)
//When a trigger occurs, change the UUID to carry the MAC address of the scanned peripheral device.
periodicTrigger.setTriggerAdvChangeMode(KBTriggerAdvChgMode.KBTriggerAdvChangeModeUUID);
//Set to start scanning every 60 seconds, unit is ms
periodicTrigger.setTriggerPara(60*1000)
let repeaterScanParas = [scanPara, periodicTrigger]
self.beacon?.modifyConfig(array:repeaterScanParas, callback: { result, error in
if (result)
{
self.showDialogMsg("success", message: "config success")
}
else if (error != nil)
{
self.showDialogMsg("Failed", message:"config repeater scan error:\(error!.errorCode)")
}
})
}
Other sensors, such as PIR sensors and VOC sensors, have a similar method for setting parameters, and will not be given example here.
func setPIRSensorParameters()
{
let sensorPara = KBCfgSensorPIR()
//enable logger
sensorPara.setLogEnable(true)
//unit is second, set measure interval
sensorPara.setMeasureInterval(2)
//set backoff time to 30 seconds
//After the beacon detects and log a PIR event, if a new PIR is detected in the next 30 seconds,
//the event will be ignored.
sensorPara.setLogBackoffTime(30)
self.beacon!.modifyConfig(obj: sensorPara) { (result, exception) in
if (result)
{
print("update pir parameters success")
}
else
{
print("update pir parameters failed")
}
}
}
For some beacon devices, it can record trigger events into memory flash. Currently, the following events can be record:
- Door open and close events
- PIR detection events
- temperature and humidity events
- light events
For these devices, we can read these saved histories record through the APP or Gateway.
With this command, we can read the total number of records and the number of unread records in the device. Next, we can read the specified record. Or read the records that have not been read.
//read temperature and humidity history record info
func readHTSensorDataInfo()
{
beacon!.readSensorDataInfo(KBSensorType.HTHumidity, callback: { (result, obj, exception) in
if (!result)
{
//read ht record info failed
print("read ht record info failed")
return
}
if let infRsp = obj
{
if (infRsp.unreadRecordNumber == 0)
{
print("no unread data in device")
}
else
{
print("there is \(infRsp.unreadRecordNumber) temperature record in device")
}
}
})
}
//read Alarm history info example
func readAlarmSensorDataInfo()
{
beacon!.readSensorDataInfo(KBSensorType.Alarm, callback: { (result, obj, exception) in
if (!result)
{
print("read record info failed")
return
}
if let infRsp = obj
{
if (infRsp.unreadRecordNumber == 0)
{
print("no unread data in device")
}
else
{
print("there are \(infRsp.unreadRecordNumber) alarm record in device")
}
}
})
}
The SDK provides the following three ways to read records.
-
KBSensorReadOption.NewRecord: read history records and move next. After app reading records, the KBeacon device will move the pointer to the next un-read record. If the app send read request again, the KBeacon device sends next unread records and move the pointer to next.
-
KBSensorReadOption.NormalOrder: Read records without pointer moving. The app can read records from old to recently. To read records in this way, the app must specify the record no to be read.
-
KBSensorReadOption.ReverseOrder: Read records without pointer moving. The app can read records from recently to old. To read records in this way, the app must specify the record no to be read.
Example1: The app read the temperature and humidity records. Each time the records was read, the pointer will move to next.
//The following demo can read a maximum of 200 records, and if there are only 100 new records in the device, the device returns 100 records.
//If the device's sensor history is greater than 200, the device returns 200 records
func readTempHistoryRecordExample()
{
self.beacon!.readSensorRecord(KBSensorType.HTHumidity,
number: KBRecordDataRsp.INVALID_DATA_RECORD_POS,
option: KBSensorReadOption.NewRecord,
max: 200,
callback: { (result, recordRsp, exception) in
if (!result)
{
print("read history record failed:%d", exception!.errorCode)
return
}
if let dataRsp = recordRsp
{
for record in dataRsp.readDataRspList
{
if let tempRecord = record as? KBRecordHumidity
{
let date = Date(timeIntervalSince1970: Double(tempRecord.utcTime))
let formatter = DateFormatter()
formatter.dateFormat = "YYYY/MM/dd HH:mm:ss"
let dateString = formatter.string(from: date)
print("record time:\(dateString), temp:\(tempRecord.temperature), hum:\(tempRecord.humidity)")
}
}
if (dataRsp.readDataNextPos == KBRecordDataRsp.INVALID_DATA_RECORD_POS)
{
print("read all un-read record complete")
}
else
{
print("next un-read record no:\(dataRsp.readDataNextPos)")
}
}
})
}
Example2: The app read the temperature and humidity records without moving pointer.
The device has 100 records sorted by time, the app want to reading 10 records and start from the No 99. The Kbeacon will send records #99 ~ #90 to app by reverse order.
If the app does not known the last record no, then the value can set to INVALID_DATA_RECORD_POS.
var mNextReadReverseIndex = KBRecordDataRsp.INVALID_DATA_RECORD_POS;
func readTempHistoryRecordReverseExample()
{
self.beacon!.readSensorRecord(KBSensorType.HTHumidity,
number: mNextReadReverseIndex,
option: KBSensorReadOption.ReverseOrder,
max: 10,
callback: { (result, recordRsp, exception) in
if (!result)
{
print("read history record failed:%d", exception!.errorCode)
return
}
if let dataRsp = recordRsp
{
for record in dataRsp.readDataRspList
{
if let tempRecord = record as? KBRecordHumidity
{
let date = Date(timeIntervalSince1970: Double(tempRecord.utcTime))
let formatter = DateFormatter()
formatter.dateFormat = "YYYY/MM/dd HH:mm:ss"
let dateString = formatter.string(from: date)
print("record time:\(dateString), temp:\(tempRecord.temperature), hum:\(tempRecord.humidity)")
}
}
if (dataRsp.readDataNextPos == KBRecordDataRsp.INVALID_DATA_RECORD_POS)
{
print("read all record complete")
}
else
{
self.mNextReadReverseIndex = dataRsp.readDataNextPos
print("move index to privous no:\(self.mNextReadReverseIndex)")
}
}
})
}
Example3: The app read the temperature and humidity records without moving pointer. The device has 100 records sorted by time, the app want to reading 20 records and start from No 10. The Kbeacon will send records #10 ~ #29 to app.
var mNextReadNormalIndex = UInt32(10);
public func readTempHistoryRecordNormalExample()
{
self.beacon!.readSensorRecord(KBSensorType.HTHumidity,
number: mNextReadNormalIndex,
option: KBSensorReadOption.NormalOrder,
max: 10,
callback: { (result, recordRsp, exception) in
if (!result)
{
print("read history record failed:%d", exception!.errorCode)
return
}
if let dataRsp = recordRsp
{
for record in dataRsp.readDataRspList
{
if let tempRecord = record as? KBRecordHumidity
{
let date = Date(timeIntervalSince1970: Double(tempRecord.utcTime))
let formatter = DateFormatter()
formatter.dateFormat = "YYYY/MM/dd HH:mm:ss"
let dateString = formatter.string(from: date)
print("record time:\(dateString), temp:\(tempRecord.temperature), hum:\(tempRecord.humidity)")
}
}
if (dataRsp.readDataNextPos == KBRecordDataRsp.INVALID_DATA_RECORD_POS)
{
print("read all record complete")
}
else
{
self.mNextReadNormalIndex = dataRsp.readDataNextPos
print("move index to next record no:\(self.mNextReadNormalIndex)")
}
}
})
}
Example1: read light events history records.
@IBAction func onReadLightEventHistory(_ sender: Any) {
beacon!.readSensorRecord(KBSensorType.Light,
number:KBRecordDataRsp.INVALID_DATA_RECORD_POS,
option: KBSensorReadOption.NewRecord,
max: 200) { result, obj, error in
if (!result)
{
//read light record info failed
NSLog("read light record history failed")
return
}
//utc time format
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd HH:mm:ss"
//foreach records
if let dataRsp = obj
{
for record in dataRsp.readDataRspList
{
if let lightRecord = record as? KBRecordLight
{
//utc second to local time
let date = Date(timeIntervalSince1970: Double(lightRecord.utcTime))
let dateString = formatter.string(from: date)
NSLog("read light record:, utc:%@, type:%d, light level:%d", dateString, lightRecord.type, lightRecord.lightLevel)
}
}
if (dataRsp.readDataNextPos == KBRecordDataRsp.INVALID_DATA_RECORD_POS)
{
NSLog("read history complete")
}
}
}
}
After app connect to device success, the app can send command to device.
All command message between app and KBeacon are JSON format. Our SDK provide Hash Map to encapsulate these JSON message.
For some KBeacon device that has buzzer function. The app can ring device. For ring command, it has 5 parameters:
- msg: msg type is 'ring'
- ringTime: unit is ms. The KBeacon will start flash/alert for 'ringTime' millisecond when receive this command.
- ringType: 0x1:beep alert only; 0x2 led flash ; 0x4 moto, 0x0 turn off ring;
- ledOn: optional parameters, unit is ms. The LED will flash at interval (ledOn + ledOff). This parameters is valid when ringType set to 0x0 or 0x1.
- ledOff: optional parameters, unit is ms. the LED will flash at interval (ledOn + ledOff). This parameters is valid when ringType set to 0x0 or 0x1.
@IBAction func onRingDevice(_ sender: Any)
{
guard self.beacon!.isConnected() else
{
print("devie not allowed beep")
return
}
var paraDicts = [String:Any]()
paraDicts["msg"] = "ring"
//ring times, uint is ms
paraDicts["ringTime"] = 10000
//ring type 0x1: beep, 0x2: led flash
paraDicts["ringType"] = 0x2
//led flash on time. valid when ringType set to 0x0 or 0x2
paraDicts["ledOn"] = 200
//led flash off time. valid when ringType set to 0x0 or 0x2
paraDicts["ledOff"] = 1800
self.beacon?.sendCommand(paraDicts, callback: { (result, except) in
if (result)
{
NSLog("send ring command to device success");
}
else
{
NSLog("send ring command to device failed");
}
})
}
The app can use follow command to power off device. For all beacons with buttons, it can support power off operation. After shutting down, you can turn on the device by long pressing the device button.
- msg: message type is 'admin'
//set parameter to default
@IBAction func powerOffDevice(_ sender: Any)
{
guard self.beacon!.isConnected() else
{
print("devie not connected")
return
}
var paraDicts = [String:Any]()
paraDicts["msg"] = "admin"
paraDicts["stype"] = "pwroff"
self.beacon!.sendCommand(paraDicts, callback: { (result, except) in
if (result)
{
NSLog("send power off command to device success");
}
else
{
NSLog("send power off command to device failed");
}
})
}
The app can use follow command to reset all configurations to default.
- msg: message type is 'reset'
//set parameter to default
@IBAction func onResetParametersToDefault(_ sender: Any)
{
guard self.beacon!.isConnected() else
{
print("devie not connected")
return
}
var paraDicts = [String:Any]()
paraDicts["msg"] = "admin"
paraDicts["stype"] = "reset"
self.beacon!.sendCommand(paraDicts, callback: { (result, except) in
if (result)
{
NSLog("send reset command to device success");
}
else
{
NSLog("send reset command to device failed");
}
})
}
App may get errors during the configuration. The KBException has follow values.
- KBErrorCode.CfgReadNull: Device return null parameters
- KBErrorCode.CfgBusy: device is busy, please make sure last configuration complete
- KBErrorCode.CfgFailed: device return failed.
- KBErrorCode.CfgTimeout: configuration timeout
- KBErrorCode.CfgInputInvalid: input parameters data not in valid range
- KBErrorCode.CfgStateError: device is not in connected state
- KBErrorCode.CfgNotSupport: device does not support the parameters
{
...another code
//start configuration
self.beacon!.modifyConfig(obj: iBeaconCfg, callback: { (result, exception) in
if (result)
{
self.showDialogMsg("success", message: "config success")
}
else if (exception != nil)
{
if (exception!.errorCode == KBErrorCode.CfgBusy)
{
NSLog("Config busy, please make sure other configruation complete")
}
else if (exception!.errorCode == KBErrorCode.CfgTimeout)
{
NSLog("Config timeout")
}
self.showDialogMsg("Failed", message:"config other error:\(exception!.errorCode)")
}
})
}
Through the DFU function, you can upgrade the firmware of the device. Our DFU function is based on Nordic's DFU library. In order to make it easier for you to integrate the DFU function, We add the DFU function into ibeacondemo demo project for your reference. The Demo about DFU includes the following class:
- KBDFUViewController: DFU UI activity and procedure about how to download latest firmware.
- KBFirmwareDownload: Responsible for download the JSON or firmware from KKM clouds.
- DFUService: This DFU service that implementation Nordic's DFU library.
Edit Podfile: The DFU Demo need download the firmware from KKM clouds by AFNNetworking library. Also the DFU demo using nordic DFU library for update.
platform :ios, '11.0'
use_frameworks!
target 'KBeaconProDemo' do
pod 'iOSDFULibrary'
pod 'MJRefresh'
pod 'kbeaconlib2'
end
- Start DFU
@IBAction func onDFUClick(_ sender: Any)
{
if self.beacon!.isConnected(),
let commCfg = self.beacon?.getCommonCfg(),
commCfg.isSupportSecurityDFU()
{
self.performSegue(withIdentifier: "seqCfgDFU", sender: self)
}
}
If you want to known more details about getting the Device's latest firmware from KKM cloud, or deploied the latest firmware on you cloud. Please contact KKM sales(sales@kkmcn.com) and she/he will send you a detail document.
Also for more detail nordic DFU library, please refer to https://github.com/NordicSemiconductor/IOS-Pods-DFU-Library
- 2024.1.20 v1.33 Add tilt angle trigger
- 2023.5.20 v1.32 Add VOC and CO2 sensor
- 2022.6.1 v1.31 Add PIR sensor
- 2021.6.20 v1.30 Support slot adv
- 2021.1.30 v1.24 Support alarm trigger action
- 2020.11.1 v1.23 Support humidity sensor
- 2020.6.1 v1.22 Add DFU library
- 2020.3.1 v1.21 change the advertisement period from integer to float.
- 2020.1.11 v1.2 add trigger function.
- 2019.10.11 v1.1 add KSesnor function.
- 2019.4.1 v1.0 first version.