diff --git a/.gitignore b/.gitignore
index 650f395..43f53aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
# Built application files
*.apk
*.ap_
+*.jar
# Files for the ART/Dalvik VM
*.dex
@@ -12,7 +13,7 @@
bin/
gen/
out/
-
+libs/
# Gradle files
.gradle/
build/
diff --git a/app/build.gradle b/app/build.gradle
index 44a1642..a69b422 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -19,11 +19,22 @@ android {
}
}
+repositories {
+ maven {
+ url "https://repo.eclipse.org/content/repositories/paho-releases/"
+ }
+}
+
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:23.4.0'
+ compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
+ compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.0'
testCompile 'junit:junit:4.12'
+ compile 'com.github.nkzawa:socket.io-client:0.3.0'
+// compile files('libs/yunba-sdk-release1.8.0.jar')
+
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index db4d0c0..382a1a6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -33,14 +33,17 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/net/chaoc/blescanner/MainActivity.java b/app/src/main/java/net/chaoc/blescanner/MainActivity.java
index 9fa4bf2..2561ed0 100644
--- a/app/src/main/java/net/chaoc/blescanner/MainActivity.java
+++ b/app/src/main/java/net/chaoc/blescanner/MainActivity.java
@@ -1,33 +1,32 @@
package net.chaoc.blescanner;
-import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
+import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.os.AsyncTask;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.util.SparseArrayCompat;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.text.Editable;
+import android.text.Html;
+import android.text.TextWatcher;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.EditText;
import android.widget.TextView;
-
-import org.eclipse.paho.client.mqttv3.IMqttActionListener;
-import org.eclipse.paho.client.mqttv3.IMqttToken;
-
-import java.io.ByteArrayInputStream;
-
-import io.yunba.android.manager.YunBaManager;
+import android.widget.Toast;
+import android.widget.ToggleButton;
import static android.content.ContentValues.TAG;
@@ -40,7 +39,15 @@ public class MainActivity extends AppCompatActivity implements android.view.View
Button stopScanningButton;
TextView peripheralTextView;
private final static int REQUEST_ENABLE_BT = 1;
- private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
+ public String advDataStr;
+ AdvertiseData advData;
+ BluetoothLeAdvertiser advertiser;
+ AdvertiseSettings advSettings;
+
+ AdvertiseCallback advCallback;
+
+ long matchCount = 0;
+ long otherCount = 0;
@Override
@@ -66,56 +73,82 @@ public void onClick(View v) {
}
});
stopScanningButton.setVisibility(View.INVISIBLE);
+ }
- YunBaManager.start(getApplicationContext());
- YunBaManager.subscribe(getApplicationContext(), new String[]{"t1"}, new IMqttActionListener() {
-
- @Override
- public void onSuccess(IMqttToken arg0) {
- Log.d("main", "Subscribe topic succeed");
-
- }
-
- @Override
- public void onFailure(IMqttToken arg0, Throwable arg1) {
- Log.d("main", "Subscribe topic failed" );
- }
- });
-
- registerMessageReceiver();
-
+ @Override
+ public void onClick(View v) {
}
+
public void init(){
btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
btAdapter = btManager.getAdapter();
btScanner = btAdapter.getBluetoothLeScanner();
+ advertiser = btAdapter.getBluetoothLeAdvertiser();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
}
+ // ble advertisers
+ advSettings = new AdvertiseSettings.Builder()
+ .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
+ .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
+ .setConnectable( false )
+ .build();
+ advDataStr = "121383000000";
+ ToggleButton toggle = (ToggleButton) findViewById(R.id.toggleButton2);
+ final EditText eText = (EditText) findViewById(R.id.editText4);
+ eText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ String text;
+ text = s.toString();
+ if (text.matches("-?[0-9a-fA-F]+") == true) {
+ advDataStr = s.toString();
+ } else {
+ s.clear();
+ s.append("121383000000");
+ }
+ }
+ });
+
+ toggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ //enabled
+ //update text value;
+ //validated it; if false then disabled this
+ //start advertising;
+ advertise(toByteArray(advDataStr));
+
+
+ } else {
+ //disabled
+ //stop advertising;
+ stopAdvertise();
+ }
+ }
+ });
- // Make sure we have access coarse location enabled, if not, prompt the user to enable it
-// if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
-// final AlertDialog.Builder builder = new AlertDialog.Builder(this);
-// builder.setTitle("This app needs location access");
-// builder.setMessage("Please grant location access so this app can detect peripherals.");
-// builder.setPositiveButton(android.R.string.ok, null);
-// builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
-// @Override
-// public void onDismiss(DialogInterface dialog) {
-// requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
-// }
-// });
-// builder.show();
-// }
}
public void startScanning() {
System.out.println("start scanning");
peripheralTextView.setText("");
+
startScanningButton.setVisibility(View.INVISIBLE);
stopScanningButton.setVisibility(View.VISIBLE);
AsyncTask.execute(new Runnable() {
@@ -144,13 +177,50 @@ public void run() {
private ScanCallback leScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
- peripheralTextView.append("Name: " + result.getDevice().getName() + " rssi: " + result.getRssi() + " : " + result.getScanRecord().getManufacturerSpecificData().keyAt(0) + "\n" + toHexString(result.getScanRecord().getManufacturerSpecificData().valueAt(0))+ "\n");
-
- // auto scroll for text view
- final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight();
- // if there is no need to scroll, scrollAmount will be <=0
- if (scrollAmount > 0)
- peripheralTextView.scrollTo(0, scrollAmount);
+ //if (result.getScanRecord().getManufacturerSpecificData().keyAt(0) == 65280) {
+ if (false) {
+ matchCount += 1;
+
+ peripheralTextView.append("$" + matchCount + ":N:" + result.getDevice().getName() + " rssi: " + result.getRssi() + " : " + result.getScanRecord().getManufacturerSpecificData().keyAt(0) + "\n" + toHexString(result.getScanRecord().getBytes()) + "\n");
+
+ // auto scroll for text view
+ final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight();
+ // if there is no need to scroll, scrollAmount will be <=0
+ if (scrollAmount > 0)
+ peripheralTextView.scrollTo(0, scrollAmount);
+ } else {
+ if (result.getDevice().getName() != null ) {
+ //if (result.getDevice().getName().toString().startsWith("Brac")) {
+ try {
+ String data = toHexString(result.getScanRecord().getManufacturerSpecificData().valueAt(0));
+ String payload = toHexString(result.getScanRecord().getBytes());
+ String name = result.getDevice().getName();
+ int key = result.getScanRecord().getManufacturerSpecificData().keyAt(0);
+ int rssi = result.getRssi();
+
+ String str = "$:" + name + " rssi" + rssi + " : " + key + "\n"
+ + data + "\n"
+ + payload + "\n";
+ if (key == 65281 && data.startsWith("12")) {
+ peripheralTextView.append(Html.fromHtml(""+str+""));
+ } else {
+ peripheralTextView.append(str);
+ }
+ peripheralTextView.append("\n");
+
+ otherCount += 1;
+ Log.w(this.getClass().getSimpleName(), toHexString(result.getScanRecord().getBytes()));
+ } catch (Exception e) {
+ Log.e(this.getClass().getSimpleName(), "get manufacture data error");
+ }
+ // auto scroll for text view
+ final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight();
+ // if there is no need to scroll, scrollAmount will be <=0
+ if (scrollAmount > 0)
+ peripheralTextView.scrollTo(0, scrollAmount);
+ //}
+ }
+ }
}
};
@@ -168,5 +238,45 @@ public String toHexString(byte[] bytes){
return hexString.toString();
}
+ public static byte[] toByteArray(String s){
+ int len = s.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ + Character.digit(s.charAt(i+1), 16));
+ }
+ return data;
+ }
+
+ public void advertise(byte[] manuData){
+ advData = new AdvertiseData.Builder()
+ .setIncludeDeviceName(true)
+ .addManufacturerData(65281, manuData)
+ .build();
+ advCallback = new AdvertiseCallback() {
+ @Override
+ public void onStartSuccess(AdvertiseSettings settingsInEffect) {
+ super.onStartSuccess(settingsInEffect);
+ Toast.makeText(getApplicationContext(), "start advertising", Toast.LENGTH_LONG).show();
+ }
+ };
+ advertiser.startAdvertising(advSettings, advData, advCallback);
+ }
+
+ public void stopAdvertise(){
+ advertiser.stopAdvertising(advCallback);
+
+ }
+
+ public class MessageReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Action - " + intent.getAction());
+ Toast.makeText(getApplicationContext(), "msg received", Toast.LENGTH_SHORT).show();
+ }
+ }
}
+
+
diff --git a/app/src/main/java/net/chaoc/blescanner/RemoteReceiver.java b/app/src/main/java/net/chaoc/blescanner/RemoteReceiver.java
deleted file mode 100644
index 806491a..0000000
--- a/app/src/main/java/net/chaoc/blescanner/RemoteReceiver.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package net.chaoc.blescanner;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-import io.yunba.android.manager.YunBaManager;
-
-/**
- * Created by chaocao on 2016/10/29.
- */
-
-public class RemoteReceiver extends BroadcastReceiver {
- private final static String REPORT_MSG_SHOW_NOTIFICARION = "1000";
- private final static String REPORT_MSG_SHOW_NOTIFICARION_FAILED = "1001";
- @Override
- public void onReceive(Context context, Intent intent) {
- if (YunBaManager.MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) {
-
- String topic = intent.getStringExtra(YunBaManager.MQTT_TOPIC);
- String msg = intent.getStringExtra(YunBaManager.MQTT_MSG);
-
-
- StringBuilder showMsg = new StringBuilder();
- showMsg.append("Received message from server: ").append(YunBaManager.MQTT_TOPIC)
- .append(" = ").append(topic).append(" ")
- .append(YunBaManager.MQTT_MSG).append(" = ").append(msg);
- boolean flag = ResUtil.showNotifation(context, topic, msg);
- //上报显示通知栏状态, 以方便后台统计
- if (flag) YunBaManager.report(context, REPORT_MSG_SHOW_NOTIFICARION, topic);
- else YunBaManager.report(context, REPORT_MSG_SHOW_NOTIFICARION_FAILED, topic);
-
- // send msg to app
-
- } else if(YunBaManager.PRESENCE_RECEIVED_ACTION.equals(intent.getAction())) {
- //msg from presence.
- String topic = intent.getStringExtra(YunBaManager.MQTT_TOPIC);
- String payload = intent.getStringExtra(YunBaManager.MQTT_MSG);
- StringBuilder showMsg = new StringBuilder();
- showMsg.append("Received message presence: ").append(YunBaManager.MQTT_TOPIC)
- .append(" = ").append(topic).append(" ")
- .append(YunBaManager.MQTT_MSG).append(" = ").append(payload);
- Log.d("DemoReceiver", showMsg.toString());
-
- }
-
-
-
-
-
- }
- //send msg to MainActivity
- private void processCustomMessage(Context context, Intent intent) {
-
- intent.setAction(MainActivity.MESSAGE_RECEIVED_ACTION);
- intent.addCategory(context.getPackageName());
- context.sendBroadcast(intent);
-
- }
-
-
-}
diff --git a/app/src/main/java/net/chaoc/blescanner/ResUtil.java b/app/src/main/java/net/chaoc/blescanner/ResUtil.java
deleted file mode 100644
index e091d50..0000000
--- a/app/src/main/java/net/chaoc/blescanner/ResUtil.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package net.chaoc.blescanner;
-
-import android.app.ActivityManager;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.media.RingtoneManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.TaskStackBuilder;
-import android.telephony.TelephonyManager;
-
-import java.util.List;
-import java.util.Random;
-
-import io.yunba.android.manager.YunBaManager;
-
-/**
- * Created by chaocao on 2016/10/29.
- */
-
-public class ResUtil {
- public static boolean isEmpty(String s) {
- if (null == s)
- return true;
- if (s.length() == 0)
- return true;
- if (s.trim().length() == 0)
- return true;
- return false;
- }
-
- public static boolean showNotifation(Context context, String topic,
- String msg) {
- try {
- Uri alarmSound = RingtoneManager
- .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
-
- long[] pattern = { 500, 500, 500 };
- NotificationCompat.Builder mBuilder = new
- NotificationCompat.Builder(
- context) //missing icon: .setSmallIcon(R.drawable.ic_launcher)
- .setContentTitle(topic).setContentText(msg)
- .setSound(alarmSound).setVibrate(pattern).setAutoCancel(true);
- // Creates an explicit intent for an Activity in your app
- Intent resultIntent = new Intent(context,
- MainActivity.class);
-
- if (!ResUtil.isEmpty(topic))
- resultIntent.putExtra(YunBaManager.MQTT_TOPIC, topic);
- if (!ResUtil.isEmpty(msg))
- resultIntent.putExtra(YunBaManager.MQTT_MSG, msg);
- // The stack builder object will contain an artificial back stack
- // for the
- // started Activity.
- // This ensures that navigating backward from the Activity leads
-
- // of
- // your application to the Home screen.
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
- // Adds the back stack for the Intent (but not the Intent itself)
- stackBuilder.addParentStack(MainActivity.class);
-
- // Adds the Intent that starts the Activity to the top of the
-
- stackBuilder.addNextIntent(resultIntent);
- PendingIntent resultPendingIntent =
- stackBuilder.getPendingIntent(
- 0, PendingIntent.FLAG_UPDATE_CURRENT);
-
- mBuilder.setContentIntent(resultPendingIntent);
- NotificationManager mNotificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- // mId allows you to update the notification later on.
- Random r = new Random();
- mNotificationManager.notify(r.nextInt(), mBuilder.build());
-
-// NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
-// context).setSmallIcon(R.drawable.ic_launcher) // notification
-// // icon
-// .setContentTitle(topic) // title for notification
-// .setContentText(msg) // message for notification
-// .setAutoCancel(true); // clear notification after click
-// Intent intent = new Intent(context, YunBaTabActivity.class);
-//
-// if (!DemoUtil.isEmpty(topic))
-// intent.putExtra(YunBaManager.MQTT_TOPIC, topic);
-// if (!DemoUtil.isEmpty(msg))
-// intent.putExtra(YunBaManager.MQTT_MSG, msg);
-// PendingIntent pi = PendingIntent.getActivity(context, 0, intent,
-// Intent.FLAG_ACTIVITY_NEW_TASK);
-// mBuilder.setContentIntent(pi);
-// NotificationManager mNotificationManager = (NotificationManager) context
-// .getSystemService(Context.NOTIFICATION_SERVICE);
-// mNotificationManager.notify(0, mBuilder.build());
- } catch (Exception e) {
- return false;
- }
- return true;
- }
-
- public static void showToast(final String toast, final Context context) {
- // if (!isAppOnForeground(context)) return;
- // new Thread(new Runnable() {
- //
- // @Override
- // public void run() {
- // Looper.prepare();
- // Toast.makeText(context, toast, Toast.LENGTH_SHORT).show();
- // Looper.loop();
- // }
- // }).start();
- }
-
- public static boolean isAppOnForeground(Context context) {
- ActivityManager activityManager = (ActivityManager) context
- .getSystemService(Context.ACTIVITY_SERVICE);
- // Returns a list of application processes that are running on the
- // device
- List appProcesses = activityManager
- .getRunningAppProcesses();
- if (appProcesses == null)
- return false;
- for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
- // importance:
- // The relative importance level that the system places
- // on this process.
- // May be one of IMPORTANCE_FOREGROUND, IMPORTANCE_VISIBLE,
- // IMPORTANCE_SERVICE, IMPORTANCE_BACKGROUND, or IMPORTANCE_EMPTY.
- // These constants are numbered so that "more important" values are
- // always smaller than "less important" values.
- // processName:
- // The name of the process that this object is associated with.
- if (appProcess.processName.equals(context.getPackageName())
- && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
- return true;
- }
- }
- return false;
- }
-
- public static String join(T[] array, String cement) {
- StringBuilder builder = new StringBuilder();
-
- if (array == null || array.length == 0) {
- return null;
- }
- for (T t : array) {
- builder.append(t).append(cement);
- }
-
- builder.delete(builder.length() - cement.length(), builder.length());
-
- return builder.toString();
- }
-
- public static boolean isNetworkEnabled(Context context) {
- ConnectivityManager conn = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo info = conn.getActiveNetworkInfo();
- return (info != null && info.isConnected());
- }
-
- public static String getImei(Context context, String imei) {
- TelephonyManager telephonyManager = (TelephonyManager) context
- .getSystemService(Context.TELEPHONY_SERVICE);
- imei = telephonyManager.getDeviceId();
- return imei;
- }
-
- public static String getAppKey(Context context) {
- Bundle metaData = null;
- String appKey = null;
- try {
- ApplicationInfo ai = context.getPackageManager()
- .getApplicationInfo(context.getPackageName(),
- PackageManager.GET_META_DATA);
- if (null != ai) {
- metaData = ai.metaData;
- }
- if (null != metaData) {
- appKey = metaData.getString("YUNBA_APPKEY");
- if ((null == appKey) || appKey.length() != 24) {
- appKey = "Error";
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
-
- }
- return appKey;
- }
-
-}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 43860d1..d4837a4 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,6 +1,7 @@
-
-
diff --git a/app/src/main/res/layout/newmain.xml b/app/src/main/res/layout/newmain.xml
new file mode 100644
index 0000000..177c92a
--- /dev/null
+++ b/app/src/main/res/layout/newmain.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 53f4fad..74b2ab0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.1'
+ classpath 'com.android.tools.build:gradle:2.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/hbm-specs.md b/hbm-specs.md
new file mode 100644
index 0000000..51ff07c
--- /dev/null
+++ b/hbm-specs.md
@@ -0,0 +1,76 @@
+# hbm-specs: bracelet and base station (type 1)
+V0.2
+
+本文提供方案1的手环、基站设计的一些基本需求;
+
+## 目的和概述
+
+方案1(type 1)的设计基于约定以下目的:
+
+- 手环能够满足定位,告警,一次性腕带能满足离体状态的监控需求。
+- 手环的广播需要:定位、呼救、离体,三种特征传递以及表达格式。让手环1,手环2均能满足基站1和基站2的监控。
+- 手环定位的频率、呼救广播包频率和次数、离体状态复位的频率、条件和方式的约定。
+- 手环和基站的上下行命令方式;
+
+## 手环规格(#TODO)
+
+- 传感器,耗电,充电方案;
+- 交互界面,包括按键人机设计和按键功能;
+- 一次性表带安装方式和离体检测设计;
+
+## 手环-基站通信协议
+
+- 其一为广播协议,约定定位、呼救、离体,三种特征,其它信息包括:电量,其他测量结果; 广播协议主要是约定在以下方式:
+
+ 1. manufacture id: 目前为00ff(65280)需进一步协商(#TODO);
+ 2. manufactureSpecificData:目前以12开头; data的二进制位和含义表达如下(-为便于阅读的分隔符): `B1-B2-B3-B4-B5-B6-B7-B8.... `
+ 3. B1:长度2,版本号:目前为0x12
+ 4. B2:长度2,手环状态位:0x13=正常定位包;0x66=告警;0x68=离体;其他,可包含手环规格;
+ 5. B3-B4-B5-B6:手环编号段;0x0000 0000-0xffff ffff
+ 6. B7:长度2,电量,格式待定0x00~0xff,按手环原有设置;
+ 7. B8及以后:其他信息;
+
+- 其二为链接协议。按手环设计来提供基站主动链接手环后进行的行为;该协议可通过基站自动连接,也可以通过配置管理工具来连接。其安全性和其他设置也应予以规定。 待定,遵循手环规格;(#TODO)
+
+## 基站-服务器通信协议
+
+- 基站应可在部署现场设置:本地服务器地址;基站编号;WLAN设置;
+- 基站使用mqtt client与本地服务器实时数据通讯;保留使用其他互联网标准协议进行其他通讯;
+- mqtt client连接至本地服务器,并注册自己基站编号为clientID;
+- mqtt client可使用eclipse的paho项目下的[java](https://eclipse.org/paho/clients/java/)或[android client](https://eclipse.org/paho/clients/android/);
+
+- 上行报文1;扫描广播的结果; 上行报文应有以下规则:
+
+ 1. 扫描到定位+离体包信息时,不超过t1(t1=5-20)秒发送一次,批量发送所有收到定位包;分别至`position`和`common`;
+ 2. 扫描不到手环时,应每t2(t2=30)秒发送一次基站状态包至`common`; 其中`status`为0时为正常。其他值为异常。异常原因按照调试中发现的问题,另外更新。
+ 3. 告警包信息,立即发送至`nursecall`频道;
+
+定位和离体信息格式:topic: `position`
+
+ JSON: {"bsid": $basestation_id, "rssi": $rssi, "data": $data.toString, "bcid": $bracelet_id, "timestamp": $epoch_timestamp}
+ Byte: $basestation_id(6) $rssi(2) $data_from_lescan
+
+告警信息格式:topic: `nursecall`
+
+ JSON: {"bsid": $basestation_id, "rssi": $rssi, "nursecall": True, "data": $data.toString, "bcid": $bracelet_id, "timestamp": $epoch_timestamp}
+ Byte: $basestation_id(6) $rssi(2) $data_from_lescan
+
+基站状态包:topic: `common`
+
+ JSON: {"bsid": $basestation_id, "status": $status, "timestamp": $epoch_timestamp}
+ Byte: $basestation_id(6) $status(2)
+
+
+- 下行报文:服务器下行返回的指令,分为以下几种;
+
+ 1. `nursecall` 的回复指令;
+ 2. 其它指令;(#TODO)
+
+一般格式:`alias` 方式或topic: `command`,需根据`bsid`过滤
+
+ JSON: {"bsid": $basestation_id, "type": $commandtype, "content": $content}
+ (nursecall_reply): {"bsid": $basestation_id, "type": "nursecall_reply", "content": "}
+
+
+- 上行报文2:链接后获取手环数据的上发报文,按手环硬件制造商需求设定(#TODO);
+