# BeaconSET Plus Android Software Development Kit Guide

  • Updating...

This set of SDK only supports devices based on miniBeaconPlus firmware and no longer supports products produced by other companies. Compared with the previous single-channel device SDK, the current set of BeaconSET Plus SDK supports more features. Of course, we also encountered many problems during the development process. We will explain the design reasons and precautions in this document. So, please read this document carefully to get started with project development as soon as possible.

Please read the part of this document which you need, part 1 we will explain the architecture of this SDK, part 2 will help developers to get started, part 3 will explain notes in your developing progress, part 4 you can get error codes and descriptions.

# Design instructions

We divide the communications between SDK and devices into two stages: Scanning stage, Connection stage. For ease of understanding, let's take a look at the related classes and the relationships between them.

MTCentralManager: global manager, check system's bluetooth status, listen status changes, the most important is scan and connect to devices;

MTPeripheral: instance of devices, MTCentralManager will create an MTPeripheral instance while it found a physical device, a device corresponds to an instance. every MTPeripheral instance has a MTFrameHandler property and a MTConnectionHandler property(These two properties are explained below);

MTFrameHandler: Advertisement frame analysis, it can analyze all frames from advertisement data of a device. in other words, it's the core of the scanning stage;

MTConnectionHandler: connection Operator. this class control all the operation of device in connection stage.

MTSensorHandler: sensor Operator, this class control all the sensor operation of device in connection stage.

**MinewFrame:**data frame, every minewframe(or subclass) instance corresponds to a data frame, usually created by "MTFrameHandler", if a device advertises mutiple data frames(such as iBeacon, UID, URL) "MTFrameHandler" will create multiple corresponding MinewFrame instances.

MTOTAManager: OTA, use this class for update device's firmware only.

# Overall structure

BeaconSET+

in this stage, MTCentralManager will scan and analyze the advertisement data of miniBeaconPlus devices, MTCentralManager will create "MTPeripheral" instance for every physical devices, developers can get all advertisement data by its MTFrameHandler property.

# Connection Stage

After a MTPeripheral connected, developer can make some changes of the device by MTConnectionHandler(a Property of MTPeripheral instance).

# OTA Operations

In general, OTA operations is complicated, so we encapsulates the MTOTAManager to perform firmware update operations, please note developer can update devices only in connection status.

# Get Started

# Prepare

# Development environment:

  • Android Studio
  • minSdkVersion 21

# Import to Project

  1. Copy the development kit MTBeaconPlus.jar file into the libs directory and add dependencies in build.gradle. As shown below:

    make jar

    add jar

    After the 20180502 version, the dfu function will use external dependencies. Please add under build.gradle

    // Add dependency library
     dependencies {
        implementation files('libs/MTBeaconPlus.jar')
        implementation 'no.nordicsemi.android:dfu:1.6.1'
     }
    
    1
    2
    3
    4
    5

    Otherwise it will crash when dfu.add code to proguard-rules.pro file:

     keep public class no.nordicsemi.android.**{*;}
    
    1
  2. Add the Bluetooth permissions and the corresponding component registration under the AndroidManifest.xml file. As follows:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

//When the targetSdkVersion version is greater than or equal to 31(Android 12)Bluetooth permissions are added as follows
<uses-permission android:name="android.permission.BLUETOOTH"
    android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
    android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
    tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<service android:name="com.minew.beaconplus.sdk.ConnectService"/>
<service android:name="com.minew.beaconplus.sdk.services.DfuService"/>
<receiver android:name="com.minew.beaconplus.sdk.receivers.BluetoothChangedReceiver">
    <intent-filter> 
        <action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
    </intent-filter>
</receiver>
1
2
3
4
5
6
7

# Start Developing

# Get sharedInstance of Manager

First of all, the developer should get the sharedInstance of Manager:

// get sharedInstance
MTCentralManager mtCentralManager = MTCentralManager.getInstance(this);

// listen the change of iPhone bluetooth
// *** also get from "bluetoothstate" property.
// *** this SDK will work normally only while state == PowerStatePoweredOn!
mtCentralManager.setBluetoothChangedListener(new OnBluetoothStateChangedListener() {
  @Override
  public void onStateChanged(BluetoothState state) {
  }
});
1
2
3
4
5
6
7
8
9
10
11

# Scan devices

start scanning task to find devices around you, then you can get their advertisement content, connect to device and change parameters.

Attempting to start scanning while already in scanning mode, Return "Operation Failed: Currently in scanning mode, no need to restart scanning."

/*
  mtCentralManager: sharedInstance of MTCentralManager
*/

// start scanning task
mtCentralManager.startScan();
// Traverse the List,get instance of every device.
// ** you can also display them on views.
mtCentralManager.setMTCentralManagerListener(new MTCentralManagerListener() {
    @Override
    public void onScanedPeripheral(final List<MTPeripheral> peripherals) {
        for (MTPeripheral mtPeripheral : peripherals) {
            // get FrameHandler of a device.
            MTFrameHandler mtFrameHandler = mtPeripheral.mMTFrameHandler;
            String mac = mtFrameHandler.getMac(); 		//mac address of device
            String name = mtFrameHandler.getName();		// name of device
            int battery = mtFrameHandler.getBattery();	//battery
            int rssi = mtFrameHandler.getRssi();		//rssi
            long lastUpdate = mtFrameHandler.getLastUpdate();  //last updated time
            
            // all data frames of device(such as:iBeacon,UID,URL...)
            ArrayList<MinewFrame> advFrames = mtFrameHandler.getAdvFrames();
            for (MinewFrame minewFrame : advFrames) {
            	//Last updated time of each frame
                Log.v("beaconplus", "lastUpdate:" + minewFrame.getLastUpdate());
            }
        }
    }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

at the sometime, you can stop the scanning task in this way.

Attempting to stop scanning when not in scanning mode,Return "Operation Failed: Currently not in scanning mode, no need to stop scanning."

// stop scanning task.
mtCentralManager.stopScan();
1
2

Get scan status

//true: scanning, false: not scanning
mtCentralManager.isScanning();
1
2

The SDK will cache all scanned devices and return them. If you need to clear the cache, you need to stop scanning first and then clear the cache.

When trying to clear the cache while currently scanning, it returns "Operation failed: Currently in scanning state, the cache cannot be cleared, please stop scanning first."

Code example for pull-down refresh:

// Stop scanning
mtCentralManager.stopScan();
// Clear cache
mtCentralManager.clear();
// Start scanning
mtCentralManager.startScan();
1
2
3
4
5
6

now the developer can get all advertisement data of device in this way. Next, we will talk about how to get frame data, such as iBeacon, URL, UID and etc...

Please refer to the code example below.

/*
  mtFrameHandler: a MTFrameHandler instance.
*/
ArrayList<MinewFrame> advFrames = mtFrameHandler.getAdvFrames();
for (MinewFrame minewFrame : advFrames) {
    FrameType frameType = minewFrame.getFrameType();
    switch (frameType) {
        case FrameiBeacon://iBeacon
            IBeaconFrame iBeaconFrame = (IBeaconFrame) minewFrame;
            Log.v("beaconplus", iBeaconFrame.getUuid() + iBeaconFrame.getMajor() + iBeaconFrame.getMinor());
            break;
        case FrameUID://uid
            UidFrame uidFrame = (UidFrame) minewFrame;
            Log.v("beaconplus", uidFrame.getNamespaceId() + uidFrame.getInstanceId());
            break;
        case FrameAccSensor:
            AccFrame accFrame = (AccFrame) minewFrame;//acc
            Log.v("beaconplus", accFrame.getXAxis() + accFrame.getYAxis() + accFrame.getZAxis());
            break;
        case FrameHTSensor:
            HTFrame htFrame = (HTFrame) minewFrame;//ht
            Log.v("beaconplus", htFrame.getTemperature() + htFrame.getHumidity());
            break;
        case FrameTLM:
            TlmFrame tlmFrame = (TlmFrame) minewFrame;//tlm
            Log.v("beaconplus", tlmFrame.getTemperature() + tlmFrame.getBatteryVol() + tlmFrame.getSecCount() + tlmFrame.getAdvCount());
            break;
        case FrameURL:
            UrlFrame urlFrame = (UrlFrame) minewFrame;//url
            Log.v("beaconplus", "Link:" + urlFrame.getUrlString() + "Rssi @ 0m:" + urlFrame.getTxPower());
            break;  
        case FrameLightSensor:
            LightFrame lightFrame = (LightFrame) minewFrame;//light
            Log.v("beaconplus", "battery:" + lightFrame.getBattery() + "%" + lightFrame.getLuxValue());
            break; 
        case FrameForceSensor:
            ForceFrame forceFrame = ((ForceFrame) minewFrame);//force
            Log.v("beaconplus", "battery:" + forceFrame.getBattery() + "%" + "force:" +  forceFrame.getForce() + "gram");
            break;
        case FramePIRSensor:
            PIRFrame pirFrame = ((PIRFrame) minewFrame);//pir
            Log.v("beaconplus", "battery:" + pirFrame.getBattery() + "%" + "PIR:", pirFrame.getValue());
            break;
       case FrameTempSensor://temp
            TemperatureFrame temperatureFrame = (TemperatureFrame) minewFrame;
            Log.v("beaconplus", "battery:" + temperatureFrame.getBattery() + "%,temperature:" + String.format("%.2f", temperatureFrame.getValue()) + "°C");
            break;
       case FrameTVOCSensor://tvoc                  
            TvocFrame tvocFrame = (TvocFrame) minewFrame;
            Log.v("beaconplus", "battery:" + tvocFrame.getBattery() + ",TVOC:" tvocFrame.getValue() + "ppb," + "battery:" +tvocFrame.getBattery() + "mV");
            break;
       case FrameLineBeacon://FrameLineBeacon
            FrameLineBeacon lineBeacon = ((FrameLineBeacon) minewFrame);
            Log.v("beaconplus", "Hwid:" + lineBeacon.getHwid() 
                  + ",Rssi@1m:", lineBeacon.getTxPower() 
                  + ",authentication:"lineBeacon.getAuthentication() 
                  + ",timesTamp:" + lineBeacon.getTimesTamp());
            break;
       case FrameTamperProof://TamperProofFrame
            TamperProofFrame tamperProofFrame = ((TamperProofFrame) minewFrame);//
            Log.v("beaconplus", "battery:" + tamperProofFrame.getBattery());
            break;
       case FrameInfo://InfoFrame
            InfoFrame infoFrame = ((InfoFrame) minewFrame);//
            Log.v("beaconplus", infoFrame.getMajor() + infoFrame.getMinor() + 	infoFrame.getBatteryVoltage());
            break;     
       case FrameGInfo://GInfoFrame
            GInfoFrame GinfoFrame = ((GInfoFrame) minewFrame);//
            Log.v("beaconplus", GinfoFrame.getMac() + GinfoFrame.getBattery() + 	GinfoFrame.getUuid());
            break;            
       default:
			break;  
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

Important: for all types of sensor value, if it equals to Double.MIN_VALUE OR Integer.MIN_VALUE, it means that this value is not available.

Please refer to the code example below:

/*
 * htFrame: HTFrame
 * lightFrame:LightFrame
 * accFrame:AccFrame
 * forceFrame:ForceFrame
 * pirFrame:PIRFrame
 */

if (htFrame.getTemperature == Double.MIN_VALUE) {
    Log.v("tag",this value is not available.");
}

if (lightFrame.getLuxValue == Integer.MIN_VALUE) {
    Log.v("tag",this value is not available.");
}

if (accFrame.getXAxis() == Double.MIN_VALUE) {
	Log.v("tag",this value is not available.");
}

if (forceFrame.getXAxis() == Double.MIN_VALUE) {
	Log.v("tag",this value is not available.");
}

if (pirFrame.getXAxis() == Double.MIN_VALUE) {
	Log.v("tag",this value is not available.");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# Connect to device

Connect to the device in order to do more operations(change parameter, OTA). you will find that when connect to device password may need, so ,Please note that callback status PASSWORDVALIDATING:

Please refer to the code example below.

/*
  mtCentralManager: a MTCentralManager sharedInstance
  mtPeripheral: a MTPeripheral instance
*/
mtCentralManager.connect(mtPeripheral, connectionStatueListener);

/*
there is mutiple state when connect to the device,
only "status == StatusCompleted" means connection operations success.
if exception appears, error will not be NULL
*/
private ConnectionStatueListener connectionStatueListener = new ConnectionStatueListener() {
    @Override
    public void onUpdateConnectionStatus(final ConnectionStatus connectionStatus, final GetPasswordListener getPasswordListener) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                switch (connectionStatus) {
                       	case CONNECTING:
                            Log.e("minew_tag", ": CONNECTING")
                            break;
                        case CONNECTED:
                           	Log.e("minew_tag", ": CONNECTED")
                            break;
                        case CONNECTED:
                           	Log.e("minew_tag", ": CONNECTED")
                            break;
                        case READINGINFO:
                        //Advanced exercise can be notified when reading firmware 								  information
                            Log.e("minew_tag", ": Read device firmware information")
                            break;
                       case DEVICEVALIDATING://After successfully verifying the device, 											write the verification key to the device
                            LogUtils.e("DEVICEVALIDATING");
                            break;   
                        //If you need the connect password, will call back this state.
                        // !!!:Warnning: passwordRequire must not be NULL!!!
                        // the length of password string must be 8.
                       case PASSWORDVALIDATING:
                        	String password = "minew123";
                        	getPasswordListener.getPassword(password);
                        	break;
                       case SYNCHRONIZINGTIME://Password verification completed
                         	Log.e("minew_tag", ": SYNCHRONIZINGTIME")
                            break;
                       case READINGCONNECTABLE:
                            Log.e("minew_tag", ": Read device firmware information")
                            LogUtils.e("READINGCONNECTABLE");
                            break;
                       case READINGFEATURE:
                            LogUtils.e("READINGFEATURE");
                            break;
                        case READINGFRAMES:
                            LogUtils.e("READINGFRAMES");
                            break;
                        case READINGTRIGGERS:
                            LogUtils.e("READINGTRIGGERS");
                            break;
                        case READINGSENSORS:
                            LogUtils.e("READINGSENSORS");
                            break;
                        case COMPLETED:
                        	//At this point the connection is successful and the 									parameters are written to the device
                        	break;
            }
        });
    }

    @Override
    public void onError(MTException mtException) {

    }
};

// disconnec from the device.
mtCentralManager.disconnect(mtPeripheral);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

# Get base information

the developer can get device information and modify parameters when a device connected.

the code example below let the developer get the base data of the device.

/*
 *  mtPeripheral:a MTPeripheral instance
 */
MTConnectionHandler mtConnectionHandler = mtPeripheral.mMTConnectionHandler;
// current connection status
ConnectState connectState = mtConnectionHandler.getConnectState();
// password require or not. None, Require
PasswordState passwordState = mtConnectionHandler.getPasswordState();
// device info, such as:(Firmware Version: 0.9.1);
HashMap<String, String> systeminfos = mtConnectionHandler.systeminfos;
String manufacturer = systeminfos.get(Constants.manufacturer);
String modlenumber = systeminfos.get(Constants.modlenumber);
String macAddress = systeminfos.get(Constants.serialnumber);
String hardware = systeminfos.get(Constants.hardware);
String firmware = systeminfos.get(Constants.firmware);
String software = systeminfos.get(Constants.software);

// device features
MTConnectionFeature mtConnectionFeature = mtConnectionHandler.mTConnectionFeature;
// atitude of slot(s), 
int slotAtitude = mtConnectionFeature.getSlotAtitude();
 // parameters can be modified:none,adv,txpower,adv/txpower
FeatureSupported featureSupported = mtConnectionFeature.getFeatureSupported();
// // frames supported(multiple)
List<FrameType> supportedSlots = mtConnectionFeature.getSupportedSlots();
// Txpower supported(multiple)
byte[] supportedTxpowers = mtConnectionFeature.getSupportedTxpowers();
// trigger supported(multiple)
ArrayList<TriggerType> supportTriggers = mtConnectionFeature.supportTriggers;
// Version of firmware;
Version version = mtConnectionFeature.getVersion();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

PS: MTConnectionHandler’s Version property.

The version value comes form firmware version, check the version value to find out functions the device support:
 
// init value
UNDEFIND

// base version, base function only.
VersionBase

// this version or above support custom connection password and device info frame.
Version0_9_8

// this version or above support remote shut down.
Version0_9_9

// this version or above support triggers.
Version2_0_0

// this version or above support triggers with adv configs.
Version2_2_60

//The difference between Bluetooth 4.2 and 5.0
VERSION2_5_X

//new version
VERSION2_NEW

// ideal value, support all features.
VersionMax = 1000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# Get data of each Slot

Next, let talk about the parameter data of every slot, the reason why we speak about this is because all the content of changing parameters is here.

Slot :Before read the code, let's talk about slot, you can think slot as a advertisement tool, it advertise the content of advertisement data even "don't know" what the content is. such as the 6 slots may like this:

Slot Number 0 1 2 3 4 5
Adv content iBeacon TLM UID URL HTSensor None
Adv interval(ms) 1000 2000 500 900 3000 0
RSSI@0/1m(dbm) -1 0 -4 -10 -3 0
Txpower(dbm) 4 0 -4 4 0 0

By meaning of the table, the No.0 slot will advertise iBeacon data, the No.1 slot will advertise TLM data, …, the No.5 slot will not advertise any data. each of them have their own advertising interval, Txpower and Calibration RSSI. They are independent of each other.

PS: Calibration RSSI(RSSI@0/1m) is the RSSI when device @ 0/1m(iBeacon is 1, others 0).

How to get the slot advertisement data? please refer to the code example below.

/*
 * mtConnectionHandler: a MTConnectionHandler instance
 */

// each slot has a frame of current device.
// atitude of this List is equals to slot atitude(feature.slotAtitude)
// 
// In strict accordance with the order of the slot,such as:0. -> MinewiBeacon,1. -> MinewUID ...
ArrayList<MinewFrame> allFrames = mtConnectionHandler.allFrames;

/*
   We assume the No.3 slot has iBeacon data, the No.4 slot has UID data.
   let's try to analyze the data of No.3 slot.
*/

// due to the defualt object is MinewFrame(the superclass of MinewiBeacon), so we need make a conversion.

// get the frame of No.3 slot
MinewFrame minewFrame = allFrames.get(2);

// check this slot has iBeacon data or not,
// *You can not check if you confirm its type.
if(minewFrame.getFrameType()==FrameType.FrameiBeacon){
  IBeaconFrame iBeaconFrame = (IBeaconFrame) minewFrame;
  
  // iBeacon data
  String uuid = iBeaconFrame.getUuid(); //uuid
  int major = iBeaconFrame.getMajor();  //major
  int minor = iBeaconFrame.getMinor();	//minor
  // the other parameters of this slot
  int curSlot = iBeaconFrame.getCurSlot();// number of the slot
  int advInterval = iBeaconFrame.getAdvInterval();// advertisement interval
  int advtxPower = iBeaconFrame.getAdvtxPower();// RSSI@0/1m
  int radiotxPower = iBeaconFrame.getRadiotxPower();// radio Txpower  
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

You can analyze the data of other slots in the same way, it's worth noting that when frameType == FrameNone means the slot has no data: From another perspective it means this slot is closed.

# Change slot data

The developer can modify advertisement and parameters of every slot freely via our APi.

We divide the data frame into static frames and dynamic frames:

  • Static frame: advertisement data will not change when it was advertise, such as iBeacon, UID, URL
  • Dynamic frame: advertisement data will change while it was advertise, such as TLM, Sensor

Please refer to the code example below:

/*
   mtConnectionHandler: a MTConnectionHandler instance
*/

// create a uid instance
UidFrame uidFrame = new UidFrame();
uidFrame.setFrameType(FrameType.FrameUID);
// set "namespaceId" and "instanceId" of UID
uidFrame.setNamespaceId("0123456789abdcdcba12");
uidFrame.setInstanceId("0123456789dc");
// the other parameters of slot
uidFrame.setRadiotxPower(4); // Radio txpower.
uidFrame.setAdvInterval(600);// advertisement interval
uidFrame.setAdvtxPower(-3); // RSSI@0m

// write to device.
// detail: 1.let No.1 slot advertise UID data, namespaceId:0123456789abdcdcba12 instanceId:0123456789dc
//         2.set No.1 slot advertisement interval to 600ms, RSSI@0m to -3dbm, radio txpower to 4dbm 
//The second parameter is the curSlot number
 mtConnectionHandler.writeSlotFrame(uidFrame, 1, new MTCOperationCallback() {
     @Override
     public void onOperation(boolean success, MTException mtException) {
         if(success){
             Log.v("beaconplus","Success!");
         }else{
             Log.v("beaconplus",mtException.getMessage);
         }
     }
 });


//set LineaBeaconFrame
LineBeaconFrame lineBeaconFrame = new LineBeaconFrame();
lineBeaconFrame.setFrameType(FrameType.FrameLineBeacon);
String hwid = "123456abfc";//Hwid长度为 10,包含 a-f/A-F/0-9
String vendorKey = "123456ad";//VendorKey长度为 8,包含 a-f/A-F/0-9
Stirng lotKey = "123456";//LotKey长度为 16 包含 a-f/A-F/0-9
lineBeaconFrame.setHwid(hwid);
lineBeaconFrame.setVendorKey(vendorKey);
lineBeaconFrame.setLotKey(lotKey);
// The second parameter is the channel number
 mtConnectionHandler.writeSlotFrame(lineBeaconFrame, 1, new MTCOperationCallback() {
     @Override
     public void onOperation(boolean success, MTException mtException) {
         if(success){
             Log.v("beaconplus","Success!");
         }else{
             Log.v("beaconplus",mtException.getMessage);
         }
     }
 });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

Becareful, the slot parameter has ranges:

  • Advertisement interval: 100 - 5000 (ms)
  • RSSI@0m: -127 - 0 (dbm)

The most important: Radio txpower is not a range, it's grade, such as: -8, -4, 0, 4. you can get all supported Radio Txpowers from the feature property of the "MTConnectionHandler"

/*
   mtConnectionFeature: a property of MTConnectionHandler.
*/

// get all supported Radio txpower of current device.
byte[] supportedTxpowers = mtConnectionFeature.getSupportedTxpowers();
1
2
3
4
5
6

OK, you can modify any slot in the way above, if you want to close a slot, only need to create a MinewFrame instance and make its frameType property to FrameNone, then write this frame to device. oh don't forget to set a slotnumber for the slot which you want to close.

# special frame data

  1. Modify the Bluetooth broadcast method: Bluetooth 5.0 125Kbps

       /**
         * set broadcast rate
         * @param mCurrentSlot: The currently selected channel value
         * @param templateBroadcastRate: 1mbps is set to 0, 125kbps is set to 1
         * @return broadCastRate
         */
    int broadCastRate = mtConnectionHandler.calculateBroadCastRate(int mCurrentSlot, int templateBroadcastRate);
    mtConnectionHandler.setBroadcastRate(int broadCastRate, MTCOperationCallback mtcOperationCallback);// set broadcast rate
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. Set Acc Frame Parameters:int odr, int wakeupThreshold, int wakeupDuration

    //Get Acc source parameters::int odr, int wakeupThreshold, int wakeupDuration
    List<AccParamsModel> list = mtConnectionHandler.getSetAccParamsValue();
    for(AccParamsModel paramsModel : list) {
        Pair<String, Integer> ordvalues = paramsModel.getOrdsVelues();
    	Integer odr= ordvalues.second;
    	int[] wakeupTimeValues = paramsModel.getWakeupTimeValues();//Indicates the wake-up threshold range
        int wakeupTime_min = wakeupTimeValuesp[0];//Wake Threshold Minimum
        int wakeupTime_max = wakeupTimeValuesp[1];//Wake Threshold Max
    	int[] wakeupDurationValues = paramsModel.getWakeupDurationValues();//wakeup duration range
        int wakeupDuration_min = wakeupTimeValuesp[0];//Wakeup Duration Minimum
        int wakeupDuration_max = wakeupTimeValuesp[1];//Wakeup Duration Max
    }
    
    /*
    Set parameters for Acc:wakeupThreshold Take values in the range of int[]wakeupTimeValues,wakeupDuration takes values in the range of int[] wakeupDurationValues
    */
    mtConnectionHandler.setAccValue(MTCOperationCallback mtcOperationCallback, int odr, int wakeupThreshold, int wakeupDuration); 
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# Change global parameter

Global parameter of device is a device level feature, such as connectable, password require, reset to default and etc.

Please refer to the code example below.

/*
   mtConnectionHandler: a MTConnectionHandler instance
*/
// reset the device
mtConnectionHandler.resetFactorySetting(new MTCOperationCallback() {
    @Override
    public void onOperation(boolean success, MTException mtException) {
        if (success) {
            Log.v("beaconplus","Success!");
        } else {
            Log.v("beaconplus","Failed!");
        }
    }
});

// update connectable settings
// CONNECTABLE_YES: app can connect to the device, CONNECTABLE_NO app can't connect to the device.
// if you set CONNECTABLE_NO, you can connect to device by press the button on device maybe next time.
// DANGER!!!:if there is no button on device and set CONNECTABLE_NO, the app can't connect to device even forever.
mtConnectionHandler.updateConnectable(CONNECTABLE_NO, new MTCOperationCallback() {
    @Override
    public void onOperation(final boolean success, final MTException mtException) {
        if (success) {
            Log.v("beaconplus","Success!");
        } else {
            Log.v("beaconplus","Failed!");
        }
    }
});
// Modify/Add password
// !!!: the password string length must be 8, digits or letters;
// it will add password if device has no password, update password if device has a password
mtConnectionHandler.modifyPassword("minew123", new MTCOperationCallback() {
    @Override
    public void onOperation(final boolean success, MTException mtException) {
        if (success) {
            Log.v("beaconplus","Success!");
        } else {
            Log.v("beaconplus","Failed!");
        }
    }
});
// remove password
mtConnectionHandler.removePassword(new MTCOperationCallback() {
    @Override
    public void onOperation(final boolean success, MTException mtException) {
        if (success) {
            Log.v("beaconplus","Success!");
        } else {
            Log.v("beaconplus","Failed!");
        }
    }
});

/**
 * remote shut down the device.
 * DANGER!!!: please make sure there a button on device, otherwise it may not boot again.
 *
 * (New) Note: If the model name is Beacon Plus-BL and the device does not support shutdown, an UnsupportedOperationException will be thrown directly. 
 * Need to pre-determine the value of model name
 *
 * Get the value of model name via MTConnectionHandler.systeminfos.get(Constants.modlenumber)
 */
mtConnectionHandler.powerOff(new MTCOperationCallback() {
    @Override
    public void onOperation(boolean success, MTException mtException) {
        if (success) {
            Log.v("beaconplus","Success!");
        } else {
            Log.v("beaconplus","Failed!");
        }
    }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

# Sensor data

BeaconPlus devices integrated a variety of sensors: HT(Temperature and Humidity ), Light, Acceleration and etc, currently we provide APi for read HT History data and API for PRI.

Please note: On January 9, 2019, we provided a new interface to obtain temperature and humidity data. If you are using a new interface provided after this date, Please refer to the code example below.

  1. read Sensor data

    Please make sure the device has HT Sensor, otherwise you can't get data correctly. Please refer to the code example below.

    /*
     *  mtConnectionHandler: MTConnectionHandler实例
     *  
     * long longtime = System.currentTimeMillis();
     * int time = (int) (longtime / 1000);
     * byte[] curTime = Tools.intToBytes(time)
     */
    
    // Read temperature and humidity data from the device
    mMTConnectionHandler.getMtSensorHandler().readSensorHistory(curTime, new MTSensorOperateCallback<SensorHTModel>() {
        @Override
        public void onResult(SensorHTModel sensorHTModel) {
            int date = sensorModel.getDate();// The Unix timestamp of this data;
            float temperature = sensorModel.getTemperature();// Temperature data;
            float humidity = sensorModel.getHumidity();// Humidity data;
        }
    
        @Override
        public void onNoHistoricalData() {
            Log.v("beaconplus","");
        }
    });
    // Read temperature data from the device
    mMTConnectionHandler.getMtSensorHandler().readTempSensorHistory(curTime, new  MTSensorOperateCallback<SensorTempModel>() {
        @Override
        public void onResult(SensorTempModel sensorTempModel) {
            int date = sensorTempModel.getDate();// Unix timestamp of this data;
            float temperature = sensorModel.getTemperature();// Temperature data
        }
    });
    // Six-axis sensor data from the device
    mMTConnectionHandler.getMtSensorHandler().readLSMHistory(Tools.intToBytes(time),
                    new MTSensorOperateCallback<SensorSixAxisModel>() {
                        @Override
                        public void onResult(SensorSixAxisModel sensorSixAxisModel) {
                            int date = sensorSixAxisModel.getDate();// Unix timestamp of this data;
                            double A_XAxis = sensorSixAxisModel.getA_XAxis();
                            double A_YAxis = sensorSixAxisModel.getA_YAxis();
                            double A_ZAxis = sensorSixAxisModel.getA_ZAxis();
                            double D_XAxis = sensorSixAxisModel.getD_XAxis();
                            double D_YAxis = sensorSixAxisModel.getD_YAxis();
                            double D_ZAxis = sensorSixAxisModel.getD_ZAxis();
                        }
                    });
    // Sensor data from the magnetometer on the device
    mMTConnectionHandler.getMtSensorHandler().readLISHistory(Tools.intToBytes(time),
                    new MTSensorOperateCallback<SensorMAGModel>() {
                        @Override
                        public void onResult(SensorMAGModel sensorMAGModel) {
                            int date = sensorMAGModel.getDate();// Unix timestamp of this data;
                        	double A_XAxis = sensorMAGModel.getA_XAxis();
                            double A_YAxis = sensorMAGModel.getA_YAxis();
                            double A_ZAxis = sensorMAGModel.getA_ZAxis();
                        }
                    });
    // From the atmospheric pressure sensor data on the device
    mMTConnectionHandler.getMtSensorHandler().readDPSHistory(Tools.intToBytes(time),
                    new MTSensorOperateCallback<SensorAPModel>() {
                        @Override
                        public void onResult(SensorAPModel sensorAPModel) {
                            	int date = sensorAPModel.getDate();// Unix timestamp of this data;
                        	    double pressure = sensorAPModel.getPressure();
                        }
                    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64

    you can vividly display these data with the curve, table or other.

  2. PIR sensor data

    Please make sure the device has PIR Sensor, otherwise you can't get data correctly. Please refer to the code example below.

    //mMtConnectionHandler: a MTConnectionHandler instance
    MTSensorHandler mtSensorHandler = mMtConnectionHandler.getMtSensorHandler();
    Map<String, MTSensorModel> sensorMap = mtSensorHandler.getSensorMap();
    SensorPIRModel sensorPIRModel = (SensorPIRModel) sensorMap.get(FrameType.FramePIRSensor.name());
    //trigger time, the infrared sensor can be triggered again after the trigger time
    int time = sensorPIRModel.getTime();
    //a bool,whether to allow repeated triggers
    boolean repeatTrigger = sensorPIRModel.isRepeatTrigger();
    
    1
    2
    3
    4
    5
    6
    7
    8

# Set triggers

BeaconPlus device has triggers, you can set triggers for every slot, the device will advertise the corresponding slot only when the trigger condition is met.

Please refer to the code example below.

/*
   mtConnectionHandler: a MTConnectionHandler instance
*/

Trigger trigger = new Trigger();
trigger.setCurSlot(2);// Target slot:2
trigger.setTriggerType(TEMPERATURE_ABOVE_ALARM);// Triggering condition:temperature above
trigger.setCondition(10);// value:10
mMTConnectionHandler.setTriggerCondition(trigger,new SetTriggerListener() {
    @Override
    public void onSetTrigger(boolean success, MTException mtException) {
        if (success) {
            Log.v("beaconplus","Success!");
        } else {
            Log.v("beaconplus","Failed!");
        }
    }
});

//Set up the channel
private void SetPassage(int slot){
    Trigger trigger = new Trigger();//Instantiate a trigger
    //There are usually 6 channels
	 swith(slot) {
        case 0://Channel 1
        trigger.setCurSlot(0);
        break;
        case 1://Channel 2
        trigger.setCurSlot(1);
        break;
        case 2://Channel 3
        trigger.setCurSlot(2);
        break;
        case 3://Channel 4
        trigger.setCurSlot(3);
        break;
        case 4://Channel 5
        trigger.setCurSlot(4);
        break;
        case 5://Channel 6
        trigger.setCurSlot(5);
        break;
        defaultbreak
    }
    switch(TriggerType triggerType) {//The argument here is an enum
        case TEMPERATURE_ABOVE_ALARM://Temperature is higher than
            //Trigger condition: temperature is higher than
            trigger.setTriggerType(TEMPERATURE_ABOVE_ALARM);
            break;
        case TEMPERATURE_BELOW_ALARM://Temperature below
            trigger.setTriggerType(TEMPERATURE_BELOW_ALARM);
            break;
        case HUMIDITY_ABOVE_ALRM://Humidity is higher than
            trigger.setTriggerType(HUMIDITY_ABOVE_ALRM);
            break;
        case HUMIDITY_BELOW_ALRM://Humidity is below
            trigger.setTriggerType(HUMIDITY_BELOW_ALRM);
            break;
        case BTN_DTAP_EVT://Double click
            trigger.setTriggerType(BTN_DTAP_EVT);
            break;
        case BTN_TTAP_EVT://Triple hit
            trigger.setTriggerType(BTN_TTAP_EVT);
            break;
    }
    
int mTemCondition= 10;//After reaching the trigger condition, broadcast duration: 10s
switch (triggerType) {
                        case TEMPERATURE_ABOVE_ALARM://Temperature is higher than
                        case TEMPERATURE_BELOW_ALARM://Temperature below
                        case HUMIDITY_ABOVE_ALRM://Humidity is higher than
                        case HUMIDITY_BELOW_ALRM://Humidity is below
                        case LIGHT_ABOVE_ALRM://Light perception is higher than
                        case LIGHT_BELOW_ALARM://Light sensitivity is lower than
                        case FORCE_ABOVE_ALRM://The pressure sensitivity is greater than
                        case FORCE_BELOW_ALRM://The pressure sensitivity is lower than
                        case TVOC_ABOVE_ALARM://TVOC is greater than
                        case TVOC_BELOW_ALARM://TVOC is lower than
                            trigger.setCondition(mTemCondition);
                            break;
                        default:
                            trigger.setCondition(mTemCondition * 1000);
                    }

trigger.setAlwaysAdvertising(boolean alwaysAdvertising)//Set whether to enable basic parameter broadcast. true: on, false: off.
trigger.setTriggerType(TriggerType.TRIGGER_SRC_NONE)//Set whether to enable trigger broadcast. It is turned off when the value is TRIGGER_SRC_NONE and turned on when it is other values.

//Broadcast conditions: time interval, the maximum is 5000ms
trigger.setAdvInterval(int advInterval)
//Broadcast conditions: broadcast signal strength, the maximum is 4, generally this is a negative value
trigger.setRadioTxpower(int radioTxpower)
mMTConnectionHandler.setTriggerCondition(trigger,new SetTriggerListener() {
    @Override
    public void onSetTrigger(boolean success, MTException mtException) {
        if (success) {
            Log.v("beaconplus","Success!");
        } else {
            Log.v("beaconplus","Failed!");
        }
    }
});
}

public enum TriggerType{
  MOTION_DETECT(0),//Acceleration
     TEMPERATURE_ABOVE_ALARM(1),//The temperature is higher than
     TEMPERATURE_BELOW_ALARM(2),//The temperature is lower than
     HUMIDITY_ABOVE_ALRM(3),//Humidity is higher than
     HUMIDITY_BELOW_ALRM(4),//The humidity is lower than
     LIGHT_ABOVE_ALRM(5),//Light perception is higher than
     BTN_PUSH_EVT(6),//Press the button
     BTN_RELEASE_EVT(7),//Button pop-up
     BTN_STAP_EVT(8),//Click the button
     BTN_DTAP_EVT(9),//Double-click the button
     BTN_TTAP_EVT(10),//Three clicks on the button
     LIGHT_BELOW_ALARM(11),//Light sensitivity is lower than
     FORCE_ABOVE_ALRM(12),//The pressure sensitivity is greater than
     FORCE_BELOW_ALRM(13),//The pressure sensitivity is lower than
     PIR_DETECT(14),//infrared
     TVOC_ABOVE_ALARM(15),//TVOC is greater than
     TVOC_BELOW_ALARM(16),//TVOC is lower than
     VIBRATION_DETECT(17),//Vibration trigger
     LEAKAGE_ALARM(18),//Leak alarm

     TRIGGER_SRC_NONE(-1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
 /**
     * Set the sensitivity of the vibration sensor
     * Super high:0x00, high:0x01,middle:0x02,low:0x03,Ultra low:0x04
     */
byte[] bytes = new byte[]{0x00};
mMTConnectionHandler.setVibrationSensitivity(bytes, new MTCOperationCallback() {
            @Override
            public void onOperation(boolean success, MTException mtException) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                    	
                    }
                });
            }
        });

/**
* Set alarm time zone
* String startTime = "19:00" 
* String endTime = "21:00"
* int switch_status = 0 // 0: off, 1: on
*/
mMTConnectionHandler.setAlarmTime(startTime1,endTime1,switch_status1,
startTime2,endTime2,switch_status2,
startTime3,endTime3,switch_status3,
mtcOperationCallback);

	//Callback method for setting alarm time zone
    MTCOperationCallback mtcOperationCallback = new MTCOperationCallback() {
        @Override
        public void onOperation(boolean success, MTException mtException) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {

                    }
                }
            });
        }
    };

/**
* Set the vibration function switch
* 0x01:open,0x00: off
* byte[] statusByte = new byte[]{0x00}; off
* byte[] statusByte = new byte[]{0x01};	open
*/
statusByte = new byte[]{0x00};
mMTConnectionHandler.setVibrationsensorStatus(statusByte, new MTCOperationCallback() {
          @Override
          public void onOperation(boolean success, MTException mtException) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                
                            }
                        });
                    }
                });

//Get vibration sensitivity
mMTConnectionHandler.inquireVibrationSensitivity(mtSensorOperateCallback);
//query vibration status
mMTConnectionHandler.inquireVibrationsensorStatus(mtSensorOperateCallback);
//query the callback method of alarm time zone
mMTConnectionHandler.inquireAlarmTime(mtSensorOperateCallback);

 /**
  * Get vibration sensitivity, query vibration status, query the callback method of alarm 	*  time zone
  */
MTSensorOperateCallback mtSensorOperateCallback = new MTSensorOperateCallback() {
   @Override
   public void onResult(MTSensorModel mtSensorModel) {
   	if (mtSensorModel instanceof VibrationSensorModel) {
       VibrationSensorModel vibrationSensorModel = (VibrationSensorModel) mtSensorModel;

     } else if (mtSensorModel instanceof VibrationStatusModel) {
       VibrationStatusModel vibrationStatusModel = (VibrationStatusModel) mtSensorModel;
               
     } else if (mtSensorModel instanceof SensorTimeModel) {
          SensorTimeModel sensorTimeModel = (SensorTimeModel) mtSensorModel;
                        switch (sensorTimeModel.getTimeInterval()) {
                            case 1:
                                startTime1 = sensorTimeModel.getStartTime();
                                endTime1 = sensorTimeModel.getEndTime();
                                timeSwitch1 = sensorTimeModel.getTimeSwitch() ? 1 : 0;
                                break;
                            case 2:
                                startTime2 = sensorTimeModel.getStartTime();
                                endTime2 = sensorTimeModel.getEndTime();
                                timeSwitch2 = sensorTimeModel.getTimeSwitch() ? 1 : 0;
                                break;
                            case 3:
                                startTime3 = sensorTimeModel.getStartTime();
                                endTime3 = sensorTimeModel.getEndTime();
                                timeSwitch3 = sensorTimeModel.getTimeSwitch() ? 1 : 0;
                                break;
                            default:
                                break;
                        }
              
              
       }
     }
    };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

At the end of this part, you can refer all code above to develop. Of course we made a note for every APi in the SDK, if there is something new, we will update this document.

# Notes

  1. In Android-6.0 or later, Bluetooth scanning requires dynamic application for location permissions, as follows:

        String[] requestPermissions;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                requestPermissions = new String[]{
                        Manifest.permission.BLUETOOTH_SCAN,
                        Manifest.permission.BLUETOOTH_CONNECT,
                        Manifest.permission.ACCESS_COARSE_LOCATION,
                        Manifest.permission.ACCESS_FINE_LOCATION
                };
    
            } else {
                requestPermissions = new String[]{
                        Manifest.permission.ACCESS_COARSE_LOCATION,
                        Manifest.permission.ACCESS_FINE_LOCATION
                };
    
            }
            ActivityCompat.requestPermissions(this,
                    requestPermissions, PERMISSION_CODE);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  2. Because the connection process of the device is in the service, it is recommended to call the following method after the app starts to open the service, otherwise the connection function will not be used. details as follows:

    /*
     * mtCentralManager:a MTConnectionHandler instance
     */
    MTCentralManager mtCentralManager = MTCentralManager.getInstance(this);
    mtCentralManager.startService();
    
    //stop ConnectService
    mtCentralManager.stopService();
    
    1
    2
    3
    4
    5
    6
    7
    8

# Change log

  • 2020.10.27 v1.11 Added lastUpdate property for each broadcast frame

  • 2020.3.25 v1.10 Add information display and data setting supporting linebeacon frame

  • 2019.12.04 v1.3 Modified the problem that some phones in Android 9.0 can't scan when the screen is closed, and added filtering conditions;

  • 2019.7.11 v1.2 change the powerOff;

  • 2019.1.9 v1.1 second version;

Stashed changes

  • 2017.10.13 v1.0 first version;
  • 2019.1.9 v1.1 second version;
  • 2019.7.11 v1.2 change the powerOff;
  • 2019.12.04 v1.3 Modified the problem that some phones in Android 9.0 can't scan when the screen is closed, and added filtering conditions;
  • 2020.3.25 v1.10 Add information display and data setting supporting linebeacon frame
  • 2020.10.27 v1.10.2 Added lastUpdate property for every broadcast frame
  • 2021.4.21 v1.11.0 added six-axis/magnetometer/atmospheric pressure sensor
  • 2021.4.28 v1.11.1 added vibration sensor
  • 2022.2.23 V1.15.1 Added TamperProofFrame ,fix the problem that the optimized SDK cannot connect to the device;
  • 2022.5.9 Added InfoFrame;
  • 2022.5.10 Special frame data: support Bluetooth 5.0 125kbps rate, support parameter modification of ACC frame;
  • 2022.5.23 Added parsing and parameter modification of GInfo broadcast frames;
  • 2024.1.2 Added comments for setting basic parameter broadcast and trigger broadcast switches, and added TriggerType type, add instructions on how to obtain the latest update time of the device and frame;
  • 2024.1.16 Add instructions for clearing cache methods, and correct the use and instructions of some methods;
Last Updated:: 4/17/2024, 10:29:41 AM