├── .gitignore
├── .npmignore
├── README.md
├── android
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── gm
│ └── RCTGMBluetooth
│ ├── RCTGMBluetoothModule.java
│ ├── RCTGMBluetoothPackage.java
│ └── RCTGMBluetoothService.java
├── doc
├── GprinterSDKForAndroid_V2.2.5.zip
├── PrinterSDKforiOS.zip
├── 佳博标签打印机编程手册 v1.0.3.pdf
└── 佳博热敏票据打印机编程手册 v1.0.1.pdf
├── index.js
├── ios
├── RCTBluetoothSerial.xcodeproj
│ └── project.pbxproj
└── RCTBluetoothSerial
│ ├── BLE.h
│ ├── BLE.m
│ ├── BLEDefines.h
│ ├── CBPeripheral+BTSExtensions.h
│ ├── CBPeripheral+BTSExtensions.m
│ ├── RCTBluetoothSerial.h
│ └── RCTBluetoothSerial.m
├── js
├── esc.js
├── tsc.js
└── util.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IJ
26 | #
27 | .idea
28 | .gradle
29 | local.properties
30 | *.iml
31 |
32 | # node.js
33 | #
34 | node_modules/
35 | npm-debug.log
36 |
37 | .vscode
38 | jsconfig.json
39 | *.sublime-workspace
40 | .idea
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /doc
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## React Native GM Bluetooth
2 |
3 | fork from https://github.com/rusel1989/react-native-bluetooth-serial
4 |
5 | 请前往看其文档
6 |
7 | 增加了几个功能便于想打印机输入二进制
8 |
9 | `writeHexToDevice` 输入指令 GMBluetooth.writeHexToDevice('1B 40');
10 |
11 | `writeTextToDevice` 输入文本 GMBluetooth.writeTextToDevice('ReactNative and Bluetooth and Print');
12 |
13 | doc 目录是佳博的demo和ESC TSC文档
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.1.0'
8 | }
9 | }
10 |
11 | apply plugin: 'com.android.library'
12 |
13 | android {
14 | compileSdkVersion 23
15 | buildToolsVersion "23.0.1"
16 |
17 | defaultConfig {
18 | minSdkVersion 16
19 | targetSdkVersion 22
20 | versionCode 1
21 | versionName "1.0"
22 | ndk {
23 | abiFilters "armeabi-v7a", "x86"
24 | }
25 | }
26 | }
27 |
28 | repositories {
29 | mavenCentral()
30 | maven {
31 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
32 | url "$rootDir/../node_modules/react-native/android"
33 | }
34 | }
35 |
36 | dependencies {
37 | compile 'com.facebook.react:react-native:+'
38 | }
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | ## Project-wide Gradle settings.
2 | #
3 | # For more details on how to configure your build environment visit
4 | # http://www.gradle.org/docs/current/userguide/build_environment.html
5 | #
6 | # Specifies the JVM arguments used for the daemon process.
7 | # The setting is particularly useful for tweaking memory settings.
8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
10 | #
11 | # When configured, Gradle will run in incubating parallel mode.
12 | # This option should only be used with decoupled projects. More details, visit
13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
14 | # org.gradle.parallel=true
15 | #Thu Nov 24 08:03:52 CET 2016
16 | android.useDeprecatedNdk=true
17 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmfe/react-native-gm-bluetooth/580aff2111b0f033e339ee38cded793091ae4460/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/java/com/gm/RCTGMBluetooth/RCTGMBluetoothModule.java:
--------------------------------------------------------------------------------
1 | package com.gm.RCTGMBluetooth;
2 |
3 | import java.lang.reflect.Method;
4 | import java.util.Set;
5 | import javax.annotation.Nullable;
6 |
7 | import android.app.Activity;
8 | import android.bluetooth.BluetoothAdapter;
9 | import android.bluetooth.BluetoothDevice;
10 | import android.content.BroadcastReceiver;
11 | import android.content.Context;
12 | import android.content.Intent;
13 | import android.content.IntentFilter;
14 | import android.os.Build;
15 | import android.util.Log;
16 | import android.util.Base64;
17 |
18 | import com.facebook.react.bridge.ActivityEventListener;
19 | import com.facebook.react.bridge.LifecycleEventListener;
20 | import com.facebook.react.bridge.ReactApplicationContext;
21 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
22 | import com.facebook.react.bridge.ReactMethod;
23 | import com.facebook.react.bridge.Arguments;
24 | import com.facebook.react.bridge.WritableMap;
25 | import com.facebook.react.bridge.WritableArray;
26 | import com.facebook.react.bridge.Promise;
27 | import com.facebook.react.modules.core.DeviceEventManagerModule;
28 |
29 | import static com.gm.RCTGMBluetooth.RCTGMBluetoothPackage.TAG;
30 |
31 | @SuppressWarnings("unused")
32 | public class RCTGMBluetoothModule extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
33 |
34 | // Debugging
35 | private static final boolean D = true;
36 |
37 | // Event names
38 | private static final String BT_ENABLED = "bluetoothEnabled";
39 | private static final String BT_DISABLED = "bluetoothDisabled";
40 | private static final String CONN_SUCCESS = "connectionSuccess";
41 | private static final String CONN_FAILED = "connectionFailed";
42 | private static final String CONN_LOST = "connectionLost";
43 | private static final String DEVICE_READ = "read";
44 | private static final String ERROR = "error";
45 |
46 | // Other stuff
47 | private static final int REQUEST_ENABLE_BLUETOOTH = 1;
48 | private static final int REQUEST_PAIR_DEVICE = 2;
49 | // Members
50 | private BluetoothAdapter mBluetoothAdapter;
51 | private RCTGMBluetoothService mBluetoothService;
52 | private ReactApplicationContext mReactContext;
53 |
54 | private StringBuffer mBuffer = new StringBuffer();
55 |
56 | // Promises
57 | private Promise mEnabledPromise;
58 | private Promise mConnectedPromise;
59 | private Promise mDeviceDiscoveryPromise;
60 | private Promise mPairDevicePromise;
61 | private String delimiter = "";
62 |
63 | public RCTGMBluetoothModule(ReactApplicationContext reactContext) {
64 | super(reactContext);
65 |
66 | if (D) Log.d(TAG, "Bluetooth module started");
67 |
68 | mReactContext = reactContext;
69 |
70 | if (mBluetoothAdapter == null) {
71 | mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
72 | }
73 |
74 | if (mBluetoothService == null) {
75 | mBluetoothService = new RCTGMBluetoothService(this);
76 | }
77 |
78 | if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
79 | sendEvent(BT_ENABLED, null);
80 | } else {
81 | sendEvent(BT_DISABLED, null);
82 | }
83 |
84 | mReactContext.addActivityEventListener(this);
85 | mReactContext.addLifecycleEventListener(this);
86 | registerBluetoothStateReceiver();
87 | }
88 |
89 | @Override
90 | public String getName() {
91 | return "RCTGMBluetooth";
92 | }
93 |
94 | @Override
95 | public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
96 | if (D) Log.d(TAG, "On activity result request: " + requestCode + ", result: " + resultCode);
97 | if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
98 | if (resultCode == Activity.RESULT_OK) {
99 | if (D) Log.d(TAG, "User enabled Bluetooth");
100 | if (mEnabledPromise != null) {
101 | mEnabledPromise.resolve(true);
102 | }
103 | } else {
104 | if (D) Log.d(TAG, "User did *NOT* enable Bluetooth");
105 | if (mEnabledPromise != null) {
106 | mEnabledPromise.reject(new Exception("User did not enable Bluetooth"));
107 | }
108 | }
109 | mEnabledPromise = null;
110 | }
111 |
112 | if (requestCode == REQUEST_PAIR_DEVICE) {
113 | if (resultCode == Activity.RESULT_OK) {
114 | if (D) Log.d(TAG, "Pairing ok");
115 | } else {
116 | if (D) Log.d(TAG, "Pairing failed");
117 | }
118 | }
119 | }
120 |
121 | @Override
122 | public void onNewIntent(Intent intent) {
123 | if (D) Log.d(TAG, "On new intent");
124 | }
125 |
126 |
127 | @Override
128 | public void onHostResume() {
129 | if (D) Log.d(TAG, "Host resume");
130 | }
131 |
132 | @Override
133 | public void onHostPause() {
134 | if (D) Log.d(TAG, "Host pause");
135 | }
136 |
137 | @Override
138 | public void onHostDestroy() {
139 | if (D) Log.d(TAG, "Host destroy");
140 | mBluetoothService.stop();
141 | }
142 |
143 | @Override
144 | public void onCatalystInstanceDestroy() {
145 | if (D) Log.d(TAG, "Catalyst instance destroyed");
146 | super.onCatalystInstanceDestroy();
147 | mBluetoothService.stop();
148 | }
149 |
150 | /*******************************/
151 | /** Methods Available from JS **/
152 | /*******************************/
153 |
154 | /*************************************/
155 | /** Bluetooth state related methods **/
156 |
157 | @ReactMethod
158 | /**
159 | * Request user to enable bluetooth
160 | */
161 | public void requestEnable(Promise promise) {
162 | // If bluetooth is already enabled resolve promise immediately
163 | if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
164 | promise.resolve(true);
165 | // Start new intent if bluetooth is note enabled
166 | } else {
167 | Activity activity = getCurrentActivity();
168 | mEnabledPromise = promise;
169 | Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
170 | if (activity != null) {
171 | activity.startActivityForResult(intent, REQUEST_ENABLE_BLUETOOTH);
172 | } else {
173 | Exception e = new Exception("Cannot start activity");
174 | Log.e(TAG, "Cannot start activity", e);
175 | mEnabledPromise.reject(e);
176 | mEnabledPromise = null;
177 | onError(e);
178 | }
179 | }
180 | }
181 |
182 | @ReactMethod
183 | /**
184 | * Enable bluetooth
185 | */
186 | public void enable(Promise promise) {
187 | if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
188 | mBluetoothAdapter.enable();
189 | }
190 | promise.resolve(true);
191 | }
192 |
193 | @ReactMethod
194 | /**
195 | * Disable bluetooth
196 | */
197 | public void disable(Promise promise) {
198 | if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
199 | mBluetoothAdapter.disable();
200 | }
201 | promise.resolve(true);
202 | }
203 |
204 | @ReactMethod
205 | /**
206 | * Check if bluetooth is enabled
207 | */
208 | public void isEnabled(Promise promise) {
209 | if (mBluetoothAdapter != null) {
210 | promise.resolve(mBluetoothAdapter.isEnabled());
211 | } else {
212 | promise.resolve(false);
213 | }
214 | }
215 |
216 | @ReactMethod
217 | public void withDelimiter(String delimiter, Promise promise) {
218 | this.delimiter = delimiter;
219 | promise.resolve(true);
220 | }
221 |
222 | /**************************************/
223 | /** Bluetooth device related methods **/
224 |
225 | @ReactMethod
226 | /**
227 | * List paired bluetooth devices
228 | */
229 | public void list(Promise promise) {
230 | WritableArray deviceList = Arguments.createArray();
231 | if (mBluetoothAdapter != null) {
232 | Set bondedDevices = mBluetoothAdapter.getBondedDevices();
233 |
234 | for (BluetoothDevice rawDevice : bondedDevices) {
235 | WritableMap device = deviceToWritableMap(rawDevice);
236 | deviceList.pushMap(device);
237 | }
238 | }
239 | promise.resolve(deviceList);
240 | }
241 |
242 | @ReactMethod
243 | /**
244 | * Discover unpaired bluetooth devices
245 | */
246 | public void discoverUnpairedDevices(final Promise promise) {
247 | if (D) Log.d(TAG, "Discover unpaired called");
248 |
249 | mDeviceDiscoveryPromise = promise;
250 | registerBluetoothDeviceDiscoveryReceiver();
251 |
252 | if (mBluetoothAdapter != null) {
253 | mBluetoothAdapter.startDiscovery();
254 | } else {
255 | promise.resolve(Arguments.createArray());
256 | }
257 | }
258 |
259 | @ReactMethod
260 | /**
261 | * Cancel discovery
262 | */
263 | public void cancelDiscovery(final Promise promise) {
264 | if (D) Log.d(TAG, "Cancel discovery called");
265 |
266 | if (mBluetoothAdapter != null) {
267 | if (mBluetoothAdapter.isDiscovering()) {
268 | mBluetoothAdapter.cancelDiscovery();
269 | }
270 | }
271 | promise.resolve(true);
272 | }
273 |
274 |
275 | @ReactMethod
276 | /**
277 | * Pair device
278 | */
279 | public void pairDevice(String id, Promise promise) {
280 | if (D) Log.d(TAG, "Pair device: " + id);
281 |
282 | if (mBluetoothAdapter != null) {
283 | mPairDevicePromise = promise;
284 | BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(id);
285 | if (device != null) {
286 | pairDevice(device);
287 | } else {
288 | mPairDevicePromise.reject(new Exception("Could not pair device " + id));
289 | mPairDevicePromise = null;
290 | }
291 | } else {
292 | promise.resolve(false);
293 | }
294 | }
295 |
296 | @ReactMethod
297 | /**
298 | * Unpair device
299 | */
300 | public void unpairDevice(String id, Promise promise) {
301 | if (D) Log.d(TAG, "Unpair device: " + id);
302 |
303 | if (mBluetoothAdapter != null) {
304 | mPairDevicePromise = promise;
305 | BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(id);
306 | if (device != null) {
307 | unpairDevice(device);
308 | } else {
309 | mPairDevicePromise.reject(new Exception("Could not unpair device " + id));
310 | mPairDevicePromise = null;
311 | }
312 | } else {
313 | promise.resolve(false);
314 | }
315 | }
316 |
317 | /********************************/
318 | /** Connection related methods **/
319 |
320 | @ReactMethod
321 | /**
322 | * Connect to device by id
323 | */
324 | public void connect(String id, Promise promise) {
325 | mConnectedPromise = promise;
326 | if (mBluetoothAdapter != null) {
327 | BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(id);
328 | if (device != null) {
329 | mBluetoothService.connect(device);
330 | } else {
331 | promise.reject(new Exception("Could not connect to " + id));
332 | }
333 | } else {
334 | promise.resolve(true);
335 | }
336 | }
337 |
338 | @ReactMethod
339 | /**
340 | * Disconnect from device
341 | */
342 | public void disconnect(Promise promise) {
343 | mBluetoothService.stop();
344 | promise.resolve(true);
345 | }
346 |
347 | @ReactMethod
348 | /**
349 | * Check if device is connected
350 | */
351 | public void isConnected(Promise promise) {
352 | promise.resolve(mBluetoothService.isConnected());
353 | }
354 |
355 | /*********************/
356 | /** Write to device **/
357 |
358 | @ReactMethod
359 | /**
360 | * Write to device over serial port
361 | */
362 | public void writeToDevice(String message, Promise promise) {
363 | if (D) Log.d(TAG, "Write " + message);
364 | byte[] data = Base64.decode(message, Base64.DEFAULT);
365 | mBluetoothService.write(data);
366 | promise.resolve(true);
367 | }
368 |
369 | @ReactMethod
370 | public void writeHexToDevice(String hexString, Promise promise){
371 | mBluetoothService.write(hexStringToBytes(hexString));
372 | promise.resolve(true);
373 | }
374 |
375 | @ReactMethod
376 | public void writeTextToDevice(String Text, Promise promise){
377 | byte[] send;
378 | try {
379 | send = Text.getBytes("GB2312");
380 | } catch (Exception e) {
381 | send = Text.getBytes();
382 | }
383 | mBluetoothService.write(send);
384 | promise.resolve(true);
385 | }
386 |
387 | /**********************/
388 | /** Read from device **/
389 |
390 | @ReactMethod
391 | /**
392 | * Read from device over serial port
393 | */
394 | public void readFromDevice(Promise promise) {
395 | if (D) Log.d(TAG, "Read");
396 | int length = mBuffer.length();
397 | String data = mBuffer.substring(0, length);
398 | mBuffer.delete(0, length);
399 | promise.resolve(data);
400 | }
401 |
402 | @ReactMethod
403 | public void readUntilDelimiter(String delimiter, Promise promise) {
404 | promise.resolve(readUntil(delimiter));
405 | }
406 |
407 |
408 | /***********/
409 | /** Other **/
410 |
411 | @ReactMethod
412 | /**
413 | * Clear data in buffer
414 | */
415 | public void clear(Promise promise) {
416 | mBuffer.setLength(0);
417 | promise.resolve(true);
418 | }
419 |
420 | @ReactMethod
421 | /**
422 | * Get length of data available to read
423 | */
424 | public void available(Promise promise) {
425 | promise.resolve(mBuffer.length());
426 | }
427 |
428 |
429 | @ReactMethod
430 | /**
431 | * Set bluetooth adapter name
432 | */
433 | public void setAdapterName(String newName, Promise promise) {
434 | if (mBluetoothAdapter != null) {
435 | mBluetoothAdapter.setName(newName);
436 | }
437 | promise.resolve(true);
438 | }
439 |
440 | /****************************************/
441 | /** Methods available to whole package **/
442 | /****************************************/
443 |
444 | /**
445 | * Handle connection success
446 | * @param msg Additional message
447 | */
448 | void onConnectionSuccess(String msg) {
449 | WritableMap params = Arguments.createMap();
450 | params.putString("message", msg);
451 | sendEvent(CONN_SUCCESS, null);
452 | if (mConnectedPromise != null) {
453 | mConnectedPromise.resolve(params);
454 | }
455 | mConnectedPromise = null;
456 | }
457 |
458 | /**
459 | * handle connection failure
460 | * @param msg Additional message
461 | */
462 | void onConnectionFailed(String msg) {
463 | WritableMap params = Arguments.createMap();
464 | params.putString("message", msg);
465 | sendEvent(CONN_FAILED, null);
466 | if (mConnectedPromise != null) {
467 | mConnectedPromise.reject(new Exception(msg));
468 | }
469 | mConnectedPromise = null;
470 | }
471 |
472 | /**
473 | * Handle lost connection
474 | * @param msg Message
475 | */
476 | void onConnectionLost (String msg) {
477 | WritableMap params = Arguments.createMap();
478 | params.putString("message", msg);
479 | sendEvent(CONN_LOST, params);
480 | }
481 |
482 | /**
483 | * Handle error
484 | * @param e Exception
485 | */
486 | void onError (Exception e) {
487 | WritableMap params = Arguments.createMap();
488 | params.putString("message", e.getMessage());
489 | sendEvent(ERROR, params);
490 | }
491 |
492 | /**
493 | * Handle read
494 | * @param data Message
495 | */
496 | void onData (String data) {
497 | mBuffer.append(data);
498 | String completeData = readUntil(this.delimiter);
499 | if (completeData != null && completeData.length() > 0) {
500 | WritableMap params = Arguments.createMap();
501 | params.putString("data", completeData);
502 | sendEvent(DEVICE_READ, params);
503 | }
504 | }
505 |
506 | private String readUntil(String delimiter) {
507 | String data = "";
508 | int index = mBuffer.indexOf(delimiter, 0);
509 | if (index > -1) {
510 | data = mBuffer.substring(0, index + delimiter.length());
511 | mBuffer.delete(0, index + delimiter.length());
512 | }
513 | return data;
514 | }
515 |
516 | /*********************/
517 | /** Private methods **/
518 | /*********************/
519 |
520 | /**
521 | * Check if is api level 19 or above
522 | * @return is above api level 19
523 | */
524 | private boolean isKitKatOrAbove () {
525 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
526 | }
527 |
528 | /**
529 | * Send event to javascript
530 | * @param eventName Name of the event
531 | * @param params Additional params
532 | */
533 | private void sendEvent(String eventName, @Nullable WritableMap params) {
534 | if (mReactContext.hasActiveCatalystInstance()) {
535 | if (D) Log.d(TAG, "Sending event: " + eventName);
536 | mReactContext
537 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
538 | .emit(eventName, params);
539 | }
540 | }
541 |
542 | /**
543 | * Convert BluetoothDevice into WritableMap
544 | * @param device Bluetooth device
545 | */
546 | private WritableMap deviceToWritableMap(BluetoothDevice device) {
547 | if (D) Log.d(TAG, "device" + device.toString());
548 |
549 | WritableMap params = Arguments.createMap();
550 |
551 | params.putString("name", device.getName());
552 | params.putString("address", device.getAddress());
553 | params.putString("id", device.getAddress());
554 |
555 | if (device.getBluetoothClass() != null) {
556 | params.putInt("class", device.getBluetoothClass().getDeviceClass());
557 | }
558 |
559 | return params;
560 | }
561 |
562 | /**
563 | * Pair device before kitkat
564 | * @param device Device
565 | */
566 | private void pairDevice(BluetoothDevice device) {
567 | try {
568 | if (D) Log.d(TAG, "Start Pairing...");
569 | Method m = device.getClass().getMethod("createBond", (Class[]) null);
570 | m.invoke(device, (Object[]) null);
571 | registerDevicePairingReceiver(device.getAddress(), BluetoothDevice.BOND_BONDED);
572 | if (D) Log.d(TAG, "Pairing finished.");
573 | } catch (Exception e) {
574 | Log.e(TAG, "Cannot pair device", e);
575 | if (mPairDevicePromise != null) {
576 | mPairDevicePromise.reject(e);
577 | mPairDevicePromise = null;
578 | }
579 | onError(e);
580 | }
581 | }
582 |
583 | /**
584 | * Unpair device
585 | * @param device Device
586 | */
587 | private void unpairDevice(BluetoothDevice device) {
588 | try {
589 | if (D) Log.d(TAG, "Start Unpairing...");
590 | Method m = device.getClass().getMethod("removeBond", (Class[]) null);
591 | m.invoke(device, (Object[]) null);
592 | registerDevicePairingReceiver(device.getAddress(), BluetoothDevice.BOND_NONE);
593 | } catch (Exception e) {
594 | Log.e(TAG, "Cannot unpair device", e);
595 | if (mPairDevicePromise != null) {
596 | mPairDevicePromise.reject(e);
597 | mPairDevicePromise = null;
598 | }
599 | onError(e);
600 | }
601 | }
602 |
603 | /**
604 | * Register receiver for device pairing
605 | * @param deviceId Id of device
606 | * @param requiredState State that we require
607 | */
608 | private void registerDevicePairingReceiver(final String deviceId, final int requiredState) {
609 | IntentFilter intentFilter = new IntentFilter();
610 |
611 | intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
612 |
613 | final BroadcastReceiver devicePairingReceiver = new BroadcastReceiver() {
614 | public void onReceive(Context context, Intent intent) {
615 | String action = intent.getAction();
616 |
617 | if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
618 | final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
619 | final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
620 |
621 | if (state == BluetoothDevice.BOND_BONDED && prevState == BluetoothDevice.BOND_BONDING) {
622 | if (D) Log.d(TAG, "Device paired");
623 | if (mPairDevicePromise != null) {
624 | mPairDevicePromise.resolve(true);
625 | mPairDevicePromise = null;
626 | }
627 | try {
628 | mReactContext.unregisterReceiver(this);
629 | } catch (Exception e) {
630 | Log.e(TAG, "Cannot unregister receiver", e);
631 | onError(e);
632 | }
633 | } else if (state == BluetoothDevice.BOND_NONE && prevState == BluetoothDevice.BOND_BONDED){
634 | if (D) Log.d(TAG, "Device unpaired");
635 | if (mPairDevicePromise != null) {
636 | mPairDevicePromise.resolve(true);
637 | mPairDevicePromise = null;
638 | }
639 | try {
640 | mReactContext.unregisterReceiver(this);
641 | } catch (Exception e) {
642 | Log.e(TAG, "Cannot unregister receiver", e);
643 | onError(e);
644 | }
645 | }
646 |
647 | }
648 | }
649 | };
650 |
651 | mReactContext.registerReceiver(devicePairingReceiver, intentFilter);
652 | }
653 |
654 | /**
655 | * Register receiver for bluetooth device discovery
656 | */
657 | private void registerBluetoothDeviceDiscoveryReceiver() {
658 | IntentFilter intentFilter = new IntentFilter();
659 |
660 | intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
661 | intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
662 |
663 | final BroadcastReceiver deviceDiscoveryReceiver = new BroadcastReceiver() {
664 | private WritableArray unpairedDevices = Arguments.createArray();
665 | public void onReceive(Context context, Intent intent) {
666 | String action = intent.getAction();
667 | if (D) Log.d(TAG, "onReceive called");
668 |
669 | if (BluetoothDevice.ACTION_FOUND.equals(action)) {
670 | BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
671 | WritableMap d = deviceToWritableMap(device);
672 | unpairedDevices.pushMap(d);
673 | } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
674 | if (D) Log.d(TAG, "Discovery finished");
675 | if (mDeviceDiscoveryPromise != null) {
676 | mDeviceDiscoveryPromise.resolve(unpairedDevices);
677 | mDeviceDiscoveryPromise = null;
678 | }
679 |
680 | try {
681 | mReactContext.unregisterReceiver(this);
682 | } catch (Exception e) {
683 | Log.e(TAG, "Unable to unregister receiver", e);
684 | onError(e);
685 | }
686 | }
687 | }
688 | };
689 |
690 | mReactContext.registerReceiver(deviceDiscoveryReceiver, intentFilter);
691 | }
692 |
693 | /**
694 | * Register receiver for bluetooth state change
695 | */
696 | private void registerBluetoothStateReceiver() {
697 | IntentFilter intentFilter = new IntentFilter();
698 |
699 | intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
700 |
701 | final BroadcastReceiver bluetoothStateReceiver = new BroadcastReceiver() {
702 | @Override
703 | public void onReceive(Context context, Intent intent) {
704 | final String action = intent.getAction();
705 |
706 | if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
707 | final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
708 | switch (state) {
709 | case BluetoothAdapter.STATE_OFF:
710 | if (D) Log.d(TAG, "Bluetooth was disabled");
711 | sendEvent(BT_DISABLED, null);
712 | break;
713 | case BluetoothAdapter.STATE_ON:
714 | if (D) Log.d(TAG, "Bluetooth was enabled");
715 | sendEvent(BT_ENABLED, null);
716 | break;
717 | }
718 | }
719 | }
720 | };
721 |
722 | mReactContext.registerReceiver(bluetoothStateReceiver, intentFilter);
723 | }
724 |
725 | public static byte[] hexStringToBytes(String hexString) {
726 | hexString = hexString.toLowerCase();
727 | String[] hexStrings = hexString.split(" ");
728 | byte[] bytes = new byte[hexStrings.length];
729 | for (int i = 0; i < hexStrings.length; i++) {
730 | char[] hexChars = hexStrings[i].toCharArray();
731 | bytes[i] = (byte) (charToByte(hexChars[0]) << 4 | charToByte(hexChars[1]));
732 | }
733 | return bytes;
734 | }
735 | private static byte charToByte(char c) {
736 | return (byte) "0123456789abcdef".indexOf(c);
737 | }
738 | }
739 |
--------------------------------------------------------------------------------
/android/src/main/java/com/gm/RCTGMBluetooth/RCTGMBluetoothPackage.java:
--------------------------------------------------------------------------------
1 | package com.gm.RCTGMBluetooth;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | import com.facebook.react.ReactPackage;
8 | import com.facebook.react.bridge.NativeModule;
9 | import com.facebook.react.bridge.ReactApplicationContext;
10 | import com.facebook.react.uimanager.ViewManager;
11 | import com.facebook.react.bridge.JavaScriptModule;
12 |
13 | public class RCTGMBluetoothPackage implements ReactPackage {
14 | static final String TAG = "GMBluetooth";
15 |
16 | @Override
17 | public List createNativeModules(ReactApplicationContext reactContext) {
18 | List modules = new ArrayList<>();
19 | modules.add(new RCTGMBluetoothModule(reactContext));
20 | return modules;
21 | }
22 |
23 | @Override
24 | public List> createJSModules() {
25 | return Collections.emptyList();
26 | }
27 |
28 | @Override
29 | public List createViewManagers(ReactApplicationContext reactContext) {
30 | return Collections.emptyList();
31 | }
32 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/gm/RCTGMBluetooth/RCTGMBluetoothService.java:
--------------------------------------------------------------------------------
1 | package com.gm.RCTGMBluetooth;
2 |
3 | import java.io.InputStream;
4 | import java.io.OutputStream;
5 | import java.util.UUID;
6 |
7 | import android.bluetooth.BluetoothAdapter;
8 | import android.bluetooth.BluetoothDevice;
9 | import android.bluetooth.BluetoothSocket;
10 | import android.util.Log;
11 |
12 | import static com.gm.RCTGMBluetooth.RCTGMBluetoothPackage.TAG;
13 |
14 | /**
15 | * This class does all the work for setting up and managing Bluetooth
16 | * connections with other devices. It has a thread that listens for
17 | * incoming connections, a thread for connecting with a device, and a
18 | * thread for performing data transmissions when connected.
19 | *
20 | * This code was based on the Android SDK BluetoothChat Sample
21 | * $ANDROID_SDK/samples/android-17/BluetoothChat
22 | */
23 | class RCTGMBluetoothService {
24 | // Debugging
25 | private static final boolean D = true;
26 |
27 | // UUIDs
28 | private static final UUID UUID_SPP = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
29 |
30 | // Member fields
31 | private BluetoothAdapter mAdapter;
32 | private ConnectThread mConnectThread;
33 | private ConnectedThread mConnectedThread;
34 | private RCTGMBluetoothModule mModule;
35 | private String mState;
36 |
37 | // Constants that indicate the current connection state
38 | private static final String STATE_NONE = "none"; // we're doing nothing
39 | private static final String STATE_CONNECTING = "connecting"; // now initiating an outgoing connection
40 | private static final String STATE_CONNECTED = "connected"; // now connected to a remote device
41 |
42 | /**
43 | * Constructor. Prepares a new RCTGMBluetoothModule session.
44 | * @param module Module which handles service events
45 | */
46 | RCTGMBluetoothService(RCTGMBluetoothModule module) {
47 | mAdapter = BluetoothAdapter.getDefaultAdapter();
48 | mState = STATE_NONE;
49 | mModule = module;
50 | }
51 |
52 | /********************************************/
53 | /** Methods available within whole package **/
54 | /********************************************/
55 |
56 | /**
57 | * Start the ConnectThread to initiate a connection to a remote device.
58 | * @param device The BluetoothDevice to connect
59 | */
60 | synchronized void connect(BluetoothDevice device) {
61 | if (D) Log.d(TAG, "connect to: " + device);
62 |
63 | if (mState.equals(STATE_CONNECTING)) {
64 | cancelConnectThread(); // Cancel any thread attempting to make a connection
65 | }
66 |
67 | cancelConnectedThread(); // Cancel any thread currently running a connection
68 |
69 | // Start the thread to connect with the given device
70 | mConnectThread = new ConnectThread(device);
71 | mConnectThread.start();
72 | setState(STATE_CONNECTING);
73 | }
74 |
75 | /**
76 | * Check whether service is connected to device
77 | * @return Is connected to device
78 | */
79 | boolean isConnected () {
80 | return getState().equals(STATE_CONNECTED);
81 | }
82 |
83 | /**
84 | * Write to the ConnectedThread in an unsynchronized manner
85 | * @param out The bytes to write
86 | * @see ConnectedThread#write(byte[])
87 | */
88 | void write(byte[] out) {
89 | if (D) Log.d(TAG, "Write in service, state is " + STATE_CONNECTED);
90 | ConnectedThread r; // Create temporary object
91 |
92 | // Synchronize a copy of the ConnectedThread
93 | synchronized (this) {
94 | if (!isConnected()) return;
95 | r = mConnectedThread;
96 | }
97 |
98 | r.write(out); // Perform the write unsynchronized
99 | }
100 |
101 | /**
102 | * Stop all threads
103 | */
104 | synchronized void stop() {
105 | if (D) Log.d(TAG, "stop");
106 |
107 | cancelConnectThread();
108 | cancelConnectedThread();
109 |
110 | setState(STATE_NONE);
111 | }
112 |
113 | /*********************/
114 | /** Private methods **/
115 | /*********************/
116 |
117 | /**
118 | * Return the current connection state.
119 | */
120 | private synchronized String getState() {
121 | return mState;
122 | }
123 |
124 | /**
125 | * Set the current state of connection
126 | * @param state An integer defining the current connection state
127 | */
128 | private synchronized void setState(String state) {
129 | if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
130 | mState = state;
131 | }
132 |
133 | /**
134 | * Start the ConnectedThread to begin managing a Bluetooth connection
135 | * @param socket The BluetoothSocket on which the connection was made
136 | * @param device The BluetoothDevice that has been connected
137 | */
138 | private synchronized void connectionSuccess(BluetoothSocket socket, BluetoothDevice device) {
139 | if (D) Log.d(TAG, "connected");
140 |
141 | cancelConnectThread(); // Cancel any thread attempting to make a connection
142 | cancelConnectedThread(); // Cancel any thread currently running a connection
143 |
144 | // Start the thread to manage the connection and perform transmissions
145 | mConnectedThread = new ConnectedThread(socket);
146 | mConnectedThread.start();
147 |
148 | mModule.onConnectionSuccess("Connected to " + device.getName());
149 | setState(STATE_CONNECTED);
150 | }
151 |
152 |
153 | /**
154 | * Indicate that the connection attempt failed and notify the UI Activity.
155 | */
156 | private void connectionFailed() {
157 | mModule.onConnectionFailed("Unable to connect to device"); // Send a failure message
158 | RCTGMBluetoothService.this.stop(); // Start the service over to restart listening mode
159 | }
160 |
161 | /**
162 | * Indicate that the connection was lost and notify the UI Activity.
163 | */
164 | private void connectionLost() {
165 | mModule.onConnectionLost("Device connection was lost"); // Send a failure message
166 | RCTGMBluetoothService.this.stop(); // Start the service over to restart listening mode
167 | }
168 |
169 | /**
170 | * Cancel connect thread
171 | */
172 | private void cancelConnectThread () {
173 | if (mConnectThread != null) {
174 | mConnectThread.cancel();
175 | mConnectThread = null;
176 | }
177 | }
178 |
179 | /**
180 | * Cancel connected thread
181 | */
182 | private void cancelConnectedThread () {
183 | if (mConnectedThread != null) {
184 | mConnectedThread.cancel();
185 | mConnectedThread = null;
186 | }
187 | }
188 |
189 | /**
190 | * This thread runs while attempting to make an outgoing connection
191 | * with a device. It runs straight through; the connection either
192 | * succeeds or fails.
193 | */
194 | private class ConnectThread extends Thread {
195 | private BluetoothSocket mmSocket;
196 | private final BluetoothDevice mmDevice;
197 |
198 | ConnectThread(BluetoothDevice device) {
199 | mmDevice = device;
200 | BluetoothSocket tmp = null;
201 |
202 | // Get a BluetoothSocket for a connection with the given BluetoothDevice
203 | try {
204 | tmp = device.createRfcommSocketToServiceRecord(UUID_SPP);
205 | } catch (Exception e) {
206 | mModule.onError(e);
207 | Log.e(TAG, "Socket create() failed", e);
208 | }
209 | mmSocket = tmp;
210 | }
211 |
212 | public void run() {
213 | if (D) Log.d(TAG, "BEGIN mConnectThread");
214 | setName("ConnectThread");
215 |
216 | // Always cancel discovery because it will slow down a connection
217 | mAdapter.cancelDiscovery();
218 |
219 | // Make a connection to the BluetoothSocket
220 | try {
221 | // This is a blocking call and will only return on a successful connection or an exception
222 | if (D) Log.d(TAG,"Connecting to socket...");
223 | mmSocket.connect();
224 | if (D) Log.d(TAG,"Connected");
225 | } catch (Exception e) {
226 | Log.e(TAG, e.toString());
227 | mModule.onError(e);
228 |
229 | // Some 4.1 devices have problems, try an alternative way to connect
230 | try {
231 | Log.i(TAG,"Trying fallback...");
232 | mmSocket = (BluetoothSocket) mmDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(mmDevice,1);
233 | mmSocket.connect();
234 | Log.i(TAG,"Connected");
235 | } catch (Exception e2) {
236 | Log.e(TAG, "Couldn't establish a Bluetooth connection.");
237 | mModule.onError(e2);
238 | try {
239 | mmSocket.close();
240 | } catch (Exception e3) {
241 | Log.e(TAG, "unable to close() socket during connection failure", e3);
242 | mModule.onError(e3);
243 | }
244 | connectionFailed();
245 | return;
246 | }
247 | }
248 |
249 | // Reset the ConnectThread because we're done
250 | synchronized (RCTGMBluetoothService.this) {
251 | mConnectThread = null;
252 | }
253 |
254 | connectionSuccess(mmSocket, mmDevice); // Start the connected thread
255 |
256 | }
257 |
258 | void cancel() {
259 | try {
260 | mmSocket.close();
261 | } catch (Exception e) {
262 | Log.e(TAG, "close() of connect socket failed", e);
263 | mModule.onError(e);
264 | }
265 | }
266 | }
267 |
268 | /**
269 | * This thread runs during a connection with a remote device.
270 | * It handles all incoming and outgoing transmissions.
271 | */
272 | private class ConnectedThread extends Thread {
273 | private final BluetoothSocket mmSocket;
274 | private final InputStream mmInStream;
275 | private final OutputStream mmOutStream;
276 |
277 | ConnectedThread(BluetoothSocket socket) {
278 | if (D) Log.d(TAG, "create ConnectedThread");
279 | mmSocket = socket;
280 | InputStream tmpIn = null;
281 | OutputStream tmpOut = null;
282 |
283 | // Get the BluetoothSocket input and output streams
284 | try {
285 | tmpIn = socket.getInputStream();
286 | tmpOut = socket.getOutputStream();
287 | } catch (Exception e) {
288 | Log.e(TAG, "temp sockets not created", e);
289 | mModule.onError(e);
290 | }
291 |
292 | mmInStream = tmpIn;
293 | mmOutStream = tmpOut;
294 | }
295 |
296 | public void run() {
297 | Log.i(TAG, "BEGIN mConnectedThread");
298 | byte[] buffer = new byte[1024];
299 | int bytes;
300 |
301 | // Keep listening to the InputStream while connected
302 | while (true) {
303 | try {
304 | bytes = mmInStream.read(buffer); // Read from the InputStream
305 | String data = new String(buffer, 0, bytes, "ISO-8859-1");
306 |
307 | mModule.onData(data); // Send the new data String to the UI Activity
308 | } catch (Exception e) {
309 | Log.e(TAG, "disconnected", e);
310 | mModule.onError(e);
311 | connectionLost();
312 | RCTGMBluetoothService.this.stop(); // Start the service over to restart listening mode
313 | break;
314 | }
315 | }
316 | }
317 |
318 | /**
319 | * Write to the connected OutStream.
320 | * @param buffer The bytes to write
321 | */
322 | void write(byte[] buffer) {
323 | try {
324 | String str = new String(buffer, "UTF-8");
325 | if (D) Log.d(TAG, "Write in thread " + str);
326 | mmOutStream.write(buffer);
327 | } catch (Exception e) {
328 | Log.e(TAG, "Exception during write", e);
329 | mModule.onError(e);
330 | }
331 | }
332 |
333 | void cancel() {
334 | try {
335 | mmSocket.close();
336 | } catch (Exception e) {
337 | Log.e(TAG, "close() of connect socket failed", e);
338 | }
339 | }
340 | }
341 | }
342 |
--------------------------------------------------------------------------------
/doc/GprinterSDKForAndroid_V2.2.5.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmfe/react-native-gm-bluetooth/580aff2111b0f033e339ee38cded793091ae4460/doc/GprinterSDKForAndroid_V2.2.5.zip
--------------------------------------------------------------------------------
/doc/PrinterSDKforiOS.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmfe/react-native-gm-bluetooth/580aff2111b0f033e339ee38cded793091ae4460/doc/PrinterSDKforiOS.zip
--------------------------------------------------------------------------------
/doc/佳博标签打印机编程手册 v1.0.3.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmfe/react-native-gm-bluetooth/580aff2111b0f033e339ee38cded793091ae4460/doc/佳博标签打印机编程手册 v1.0.3.pdf
--------------------------------------------------------------------------------
/doc/佳博热敏票据打印机编程手册 v1.0.1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmfe/react-native-gm-bluetooth/580aff2111b0f033e339ee38cded793091ae4460/doc/佳博热敏票据打印机编程手册 v1.0.1.pdf
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const ReactNative = require('react-native');
2 | const {Buffer} = require('buffer');
3 | const {NativeModules, DeviceEventEmitter, Platform} = ReactNative;
4 | const TSC = require('./js/tsc');
5 | const ESC = require('./js/esc');
6 | const Util = require('./js/util');
7 |
8 | let GMBluetooth = NativeModules.GMBluetooth;
9 | if(Platform.OS === 'ios'){
10 | GMBluetooth = NativeModules.BluetoothSerial;
11 | }
12 |
13 | /**
14 | * Listen for available events
15 | * @param {String} eventName Name of event one of connectionSuccess, connectionLost, data, rawData
16 | * @param {Function} handler Event handler
17 | */
18 | GMBluetooth.on = (eventName, handler) => {
19 | DeviceEventEmitter.addListener(eventName, handler)
20 | };
21 |
22 | /**
23 | * Stop listening for event
24 | * @param {String} eventName Name of event one of connectionSuccess, connectionLost, data, rawData
25 | * @param {Function} handler Event handler
26 | */
27 | GMBluetooth.removeListener = (eventName, handler) => {
28 | DeviceEventEmitter.removeListener(eventName, handler)
29 | };
30 |
31 | /**
32 | * Write data to device, you can pass string or buffer,
33 | * We must convert to base64 in RN there is no way to pass buffer directly
34 | * @param {Buffer|String} data
35 | * @return {Promise}
36 | */
37 | GMBluetooth.write = (data) => {
38 | if (typeof data === 'string') {
39 | data = new Buffer(data)
40 | }
41 | return GMBluetooth.writeToDevice(data.toString('base64'))
42 | };
43 |
44 | GMBluetooth.TSC = TSC;
45 | GMBluetooth.ESC = ESC;
46 | GMBluetooth.Util = Util;
47 |
48 | ESC._setBT(GMBluetooth);
49 | TSC._setBT(GMBluetooth);
50 |
51 | module.exports = GMBluetooth;
52 |
--------------------------------------------------------------------------------
/ios/RCTBluetoothSerial.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 69CA06071CC43C2900AF6479 /* RCTBluetoothSerial.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 69CA06061CC43C2900AF6479 /* RCTBluetoothSerial.h */; };
11 | 69CA06091CC43C2900AF6479 /* RCTBluetoothSerial.m in Sources */ = {isa = PBXBuildFile; fileRef = 69CA06081CC43C2900AF6479 /* RCTBluetoothSerial.m */; };
12 | 69CA06161CC49D5E00AF6479 /* BLE.m in Sources */ = {isa = PBXBuildFile; fileRef = 69CA06101CC49D5E00AF6479 /* BLE.m */; };
13 | 69CA06171CC49D5E00AF6479 /* CBPeripheral+BTSExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 69CA06131CC49D5E00AF6479 /* CBPeripheral+BTSExtensions.m */; };
14 | /* End PBXBuildFile section */
15 |
16 | /* Begin PBXCopyFilesBuildPhase section */
17 | 69CA06011CC43C2900AF6479 /* CopyFiles */ = {
18 | isa = PBXCopyFilesBuildPhase;
19 | buildActionMask = 2147483647;
20 | dstPath = "include/$(PRODUCT_NAME)";
21 | dstSubfolderSpec = 16;
22 | files = (
23 | 69CA06071CC43C2900AF6479 /* RCTBluetoothSerial.h in CopyFiles */,
24 | );
25 | runOnlyForDeploymentPostprocessing = 0;
26 | };
27 | /* End PBXCopyFilesBuildPhase section */
28 |
29 | /* Begin PBXFileReference section */
30 | 69CA06031CC43C2900AF6479 /* libRCTBluetoothSerial.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTBluetoothSerial.a; sourceTree = BUILT_PRODUCTS_DIR; };
31 | 69CA06061CC43C2900AF6479 /* RCTBluetoothSerial.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBluetoothSerial.h; sourceTree = ""; };
32 | 69CA06081CC43C2900AF6479 /* RCTBluetoothSerial.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTBluetoothSerial.m; sourceTree = ""; };
33 | 69CA060F1CC49D5E00AF6479 /* BLE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLE.h; sourceTree = ""; };
34 | 69CA06101CC49D5E00AF6479 /* BLE.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BLE.m; sourceTree = ""; };
35 | 69CA06111CC49D5E00AF6479 /* BLEDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLEDefines.h; sourceTree = ""; };
36 | 69CA06121CC49D5E00AF6479 /* CBPeripheral+BTSExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CBPeripheral+BTSExtensions.h"; sourceTree = ""; };
37 | 69CA06131CC49D5E00AF6479 /* CBPeripheral+BTSExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CBPeripheral+BTSExtensions.m"; sourceTree = ""; };
38 | /* End PBXFileReference section */
39 |
40 | /* Begin PBXFrameworksBuildPhase section */
41 | 69CA06001CC43C2900AF6479 /* Frameworks */ = {
42 | isa = PBXFrameworksBuildPhase;
43 | buildActionMask = 2147483647;
44 | files = (
45 | );
46 | runOnlyForDeploymentPostprocessing = 0;
47 | };
48 | /* End PBXFrameworksBuildPhase section */
49 |
50 | /* Begin PBXGroup section */
51 | 69CA05FA1CC43C2900AF6479 = {
52 | isa = PBXGroup;
53 | children = (
54 | 69CA06051CC43C2900AF6479 /* RCTBluetoothSerial */,
55 | 69CA06041CC43C2900AF6479 /* Products */,
56 | );
57 | sourceTree = "";
58 | };
59 | 69CA06041CC43C2900AF6479 /* Products */ = {
60 | isa = PBXGroup;
61 | children = (
62 | 69CA06031CC43C2900AF6479 /* libRCTBluetoothSerial.a */,
63 | );
64 | name = Products;
65 | sourceTree = "";
66 | };
67 | 69CA06051CC43C2900AF6479 /* RCTBluetoothSerial */ = {
68 | isa = PBXGroup;
69 | children = (
70 | 69CA060F1CC49D5E00AF6479 /* BLE.h */,
71 | 69CA06101CC49D5E00AF6479 /* BLE.m */,
72 | 69CA06111CC49D5E00AF6479 /* BLEDefines.h */,
73 | 69CA06121CC49D5E00AF6479 /* CBPeripheral+BTSExtensions.h */,
74 | 69CA06131CC49D5E00AF6479 /* CBPeripheral+BTSExtensions.m */,
75 | 69CA06061CC43C2900AF6479 /* RCTBluetoothSerial.h */,
76 | 69CA06081CC43C2900AF6479 /* RCTBluetoothSerial.m */,
77 | );
78 | path = RCTBluetoothSerial;
79 | sourceTree = "";
80 | };
81 | /* End PBXGroup section */
82 |
83 | /* Begin PBXNativeTarget section */
84 | 69CA06021CC43C2900AF6479 /* RCTBluetoothSerial */ = {
85 | isa = PBXNativeTarget;
86 | buildConfigurationList = 69CA060C1CC43C2900AF6479 /* Build configuration list for PBXNativeTarget "RCTBluetoothSerial" */;
87 | buildPhases = (
88 | 69CA05FF1CC43C2900AF6479 /* Sources */,
89 | 69CA06001CC43C2900AF6479 /* Frameworks */,
90 | 69CA06011CC43C2900AF6479 /* CopyFiles */,
91 | );
92 | buildRules = (
93 | );
94 | dependencies = (
95 | );
96 | name = RCTBluetoothSerial;
97 | productName = RCTBluetoothSerial;
98 | productReference = 69CA06031CC43C2900AF6479 /* libRCTBluetoothSerial.a */;
99 | productType = "com.apple.product-type.library.static";
100 | };
101 | /* End PBXNativeTarget section */
102 |
103 | /* Begin PBXProject section */
104 | 69CA05FB1CC43C2900AF6479 /* Project object */ = {
105 | isa = PBXProject;
106 | attributes = {
107 | LastUpgradeCheck = 0730;
108 | ORGANIZATIONNAME = "Jakub Martyčák";
109 | TargetAttributes = {
110 | 69CA06021CC43C2900AF6479 = {
111 | CreatedOnToolsVersion = 7.3;
112 | };
113 | };
114 | };
115 | buildConfigurationList = 69CA05FE1CC43C2900AF6479 /* Build configuration list for PBXProject "RCTBluetoothSerial" */;
116 | compatibilityVersion = "Xcode 3.2";
117 | developmentRegion = English;
118 | hasScannedForEncodings = 0;
119 | knownRegions = (
120 | en,
121 | );
122 | mainGroup = 69CA05FA1CC43C2900AF6479;
123 | productRefGroup = 69CA06041CC43C2900AF6479 /* Products */;
124 | projectDirPath = "";
125 | projectRoot = "";
126 | targets = (
127 | 69CA06021CC43C2900AF6479 /* RCTBluetoothSerial */,
128 | );
129 | };
130 | /* End PBXProject section */
131 |
132 | /* Begin PBXSourcesBuildPhase section */
133 | 69CA05FF1CC43C2900AF6479 /* Sources */ = {
134 | isa = PBXSourcesBuildPhase;
135 | buildActionMask = 2147483647;
136 | files = (
137 | 69CA06091CC43C2900AF6479 /* RCTBluetoothSerial.m in Sources */,
138 | 69CA06161CC49D5E00AF6479 /* BLE.m in Sources */,
139 | 69CA06171CC49D5E00AF6479 /* CBPeripheral+BTSExtensions.m in Sources */,
140 | );
141 | runOnlyForDeploymentPostprocessing = 0;
142 | };
143 | /* End PBXSourcesBuildPhase section */
144 |
145 | /* Begin XCBuildConfiguration section */
146 | 69CA060A1CC43C2900AF6479 /* Debug */ = {
147 | isa = XCBuildConfiguration;
148 | buildSettings = {
149 | ALWAYS_SEARCH_USER_PATHS = YES;
150 | CLANG_ANALYZER_NONNULL = YES;
151 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
152 | CLANG_CXX_LIBRARY = "libc++";
153 | CLANG_ENABLE_MODULES = YES;
154 | CLANG_ENABLE_OBJC_ARC = YES;
155 | CLANG_WARN_BOOL_CONVERSION = YES;
156 | CLANG_WARN_CONSTANT_CONVERSION = YES;
157 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
158 | CLANG_WARN_EMPTY_BODY = YES;
159 | CLANG_WARN_ENUM_CONVERSION = YES;
160 | CLANG_WARN_INT_CONVERSION = YES;
161 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
162 | CLANG_WARN_UNREACHABLE_CODE = YES;
163 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
164 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
165 | COPY_PHASE_STRIP = NO;
166 | DEBUG_INFORMATION_FORMAT = dwarf;
167 | ENABLE_STRICT_OBJC_MSGSEND = YES;
168 | ENABLE_TESTABILITY = YES;
169 | GCC_C_LANGUAGE_STANDARD = gnu99;
170 | GCC_DYNAMIC_NO_PIC = NO;
171 | GCC_NO_COMMON_BLOCKS = YES;
172 | GCC_OPTIMIZATION_LEVEL = 0;
173 | GCC_PREPROCESSOR_DEFINITIONS = (
174 | "DEBUG=1",
175 | "$(inherited)",
176 | );
177 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
178 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
179 | GCC_WARN_UNDECLARED_SELECTOR = YES;
180 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
181 | GCC_WARN_UNUSED_FUNCTION = YES;
182 | GCC_WARN_UNUSED_VARIABLE = YES;
183 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
184 | MTL_ENABLE_DEBUG_INFO = YES;
185 | ONLY_ACTIVE_ARCH = YES;
186 | SDKROOT = iphoneos;
187 | };
188 | name = Debug;
189 | };
190 | 69CA060B1CC43C2900AF6479 /* Release */ = {
191 | isa = XCBuildConfiguration;
192 | buildSettings = {
193 | ALWAYS_SEARCH_USER_PATHS = YES;
194 | CLANG_ANALYZER_NONNULL = YES;
195 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
196 | CLANG_CXX_LIBRARY = "libc++";
197 | CLANG_ENABLE_MODULES = YES;
198 | CLANG_ENABLE_OBJC_ARC = YES;
199 | CLANG_WARN_BOOL_CONVERSION = YES;
200 | CLANG_WARN_CONSTANT_CONVERSION = YES;
201 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
202 | CLANG_WARN_EMPTY_BODY = YES;
203 | CLANG_WARN_ENUM_CONVERSION = YES;
204 | CLANG_WARN_INT_CONVERSION = YES;
205 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
206 | CLANG_WARN_UNREACHABLE_CODE = YES;
207 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
208 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
209 | COPY_PHASE_STRIP = NO;
210 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
211 | ENABLE_NS_ASSERTIONS = NO;
212 | ENABLE_STRICT_OBJC_MSGSEND = YES;
213 | GCC_C_LANGUAGE_STANDARD = gnu99;
214 | GCC_NO_COMMON_BLOCKS = YES;
215 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
216 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
217 | GCC_WARN_UNDECLARED_SELECTOR = YES;
218 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
219 | GCC_WARN_UNUSED_FUNCTION = YES;
220 | GCC_WARN_UNUSED_VARIABLE = YES;
221 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
222 | MTL_ENABLE_DEBUG_INFO = NO;
223 | SDKROOT = iphoneos;
224 | VALIDATE_PRODUCT = YES;
225 | };
226 | name = Release;
227 | };
228 | 69CA060D1CC43C2900AF6479 /* Debug */ = {
229 | isa = XCBuildConfiguration;
230 | buildSettings = {
231 | HEADER_SEARCH_PATHS = "$(SRCROOT)/../../react-native/React/**";
232 | OTHER_LDFLAGS = "-ObjC";
233 | PRODUCT_NAME = "$(TARGET_NAME)";
234 | SKIP_INSTALL = YES;
235 | };
236 | name = Debug;
237 | };
238 | 69CA060E1CC43C2900AF6479 /* Release */ = {
239 | isa = XCBuildConfiguration;
240 | buildSettings = {
241 | HEADER_SEARCH_PATHS = "$(SRCROOT)/../../react-native/React/**";
242 | OTHER_LDFLAGS = "-ObjC";
243 | PRODUCT_NAME = "$(TARGET_NAME)";
244 | SKIP_INSTALL = YES;
245 | };
246 | name = Release;
247 | };
248 | /* End XCBuildConfiguration section */
249 |
250 | /* Begin XCConfigurationList section */
251 | 69CA05FE1CC43C2900AF6479 /* Build configuration list for PBXProject "RCTBluetoothSerial" */ = {
252 | isa = XCConfigurationList;
253 | buildConfigurations = (
254 | 69CA060A1CC43C2900AF6479 /* Debug */,
255 | 69CA060B1CC43C2900AF6479 /* Release */,
256 | );
257 | defaultConfigurationIsVisible = 0;
258 | defaultConfigurationName = Release;
259 | };
260 | 69CA060C1CC43C2900AF6479 /* Build configuration list for PBXNativeTarget "RCTBluetoothSerial" */ = {
261 | isa = XCConfigurationList;
262 | buildConfigurations = (
263 | 69CA060D1CC43C2900AF6479 /* Debug */,
264 | 69CA060E1CC43C2900AF6479 /* Release */,
265 | );
266 | defaultConfigurationIsVisible = 0;
267 | defaultConfigurationName = Release;
268 | };
269 | /* End XCConfigurationList section */
270 | };
271 | rootObject = 69CA05FB1CC43C2900AF6479 /* Project object */;
272 | }
273 |
--------------------------------------------------------------------------------
/ios/RCTBluetoothSerial/BLE.h:
--------------------------------------------------------------------------------
1 |
2 | /*
3 |
4 | Copyright (c) 2013 RedBearLab
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 |
12 | */
13 |
14 | #import
15 | #if TARGET_OS_IPHONE
16 | #import
17 | #else
18 | #import
19 | #endif
20 |
21 | #import "CBPeripheral+BTSExtensions.h"
22 |
23 | @protocol BLEDelegate
24 | @optional
25 | -(void) bleDidConnect;
26 | -(void) bleDidDisconnect;
27 | -(void) bleDidUpdateRSSI:(NSNumber *) rssi;
28 | -(void) bleDidReceiveData:(unsigned char *) data length:(int) length;
29 | -(void) bleDidChangedState:(bool) isEnabled;
30 |
31 | @required
32 | @end
33 |
34 | @interface BLE : NSObject {
35 |
36 | }
37 |
38 | @property (nonatomic,assign) id delegate;
39 | @property (strong, nonatomic) NSMutableArray *peripherals;
40 | @property (strong, nonatomic) CBCentralManager *CM;
41 | @property (strong, nonatomic) CBPeripheral *activePeripheral;
42 |
43 | -(void) enableReadNotification:(CBPeripheral *)p;
44 | -(void) read;
45 | -(void) writeValue:(CBUUID *)serviceUUID characteristicUUID:(CBUUID *)characteristicUUID p:(CBPeripheral *)p data:(NSData *)data;
46 |
47 | -(BOOL) isConnected;
48 | -(void) write:(NSData *)d;
49 | -(void) readRSSI;
50 |
51 | -(void) controlSetup;
52 | -(int) findBLEPeripherals:(int) timeout;
53 | -(void) connectPeripheral:(CBPeripheral *)peripheral;
54 |
55 | -(UInt16) swap:(UInt16) s;
56 | -(const char *) centralManagerStateToString:(int)state;
57 | -(void) scanTimer:(NSTimer *)timer;
58 | -(void) printKnownPeripherals;
59 | -(void) printPeripheralInfo:(CBPeripheral*)peripheral;
60 |
61 | -(void) getAllServicesFromPeripheral:(CBPeripheral *)p;
62 | -(void) getAllCharacteristicsFromPeripheral:(CBPeripheral *)p;
63 | -(CBService *) findServiceFromUUID:(CBUUID *)UUID p:(CBPeripheral *)p;
64 | -(CBCharacteristic *) findCharacteristicFromUUID:(CBUUID *)UUID service:(CBService*)service;
65 |
66 | //-(NSString *) NSUUIDToString:(NSUUID *) UUID;
67 | -(NSString *) CBUUIDToString:(CBUUID *) UUID;
68 |
69 | -(int) compareCBUUID:(CBUUID *) UUID1 UUID2:(CBUUID *)UUID2;
70 | -(int) compareCBUUIDToInt:(CBUUID *) UUID1 UUID2:(UInt16)UUID2;
71 | -(UInt16) CBUUIDToInt:(CBUUID *) UUID;
72 | -(BOOL) UUIDSAreEqual:(NSUUID *)UUID1 UUID2:(NSUUID *)UUID2;
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/ios/RCTBluetoothSerial/BLE.m:
--------------------------------------------------------------------------------
1 |
2 | /*
3 |
4 | Copyright (c) 2013 RedBearLab
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 |
12 | */
13 |
14 | #import "BLE.h"
15 | #import "BLEDefines.h"
16 |
17 | @implementation BLE
18 |
19 | @synthesize delegate;
20 | @synthesize CM;
21 | @synthesize peripherals;
22 | @synthesize activePeripheral;
23 |
24 | static bool isConnected = false;
25 | static int rssi = 0;
26 | static const int MAX_BUF_LENGTH = 100;
27 |
28 | // TODO should have a configurable list of services
29 | CBUUID *redBearLabsServiceUUID;
30 | CBUUID *adafruitServiceUUID;
31 | CBUUID *lairdServiceUUID;
32 | CBUUID *blueGigaServiceUUID;
33 | CBUUID *rongtaSerivceUUID;
34 | CBUUID *posnetSerivceUUID;
35 | CBUUID *serialServiceUUID;
36 | CBUUID *readCharacteristicUUID;
37 | CBUUID *writeCharacteristicUUID;
38 |
39 | -(void) readRSSI
40 | {
41 | [activePeripheral readRSSI];
42 | }
43 |
44 | -(BOOL) isConnected
45 | {
46 | return isConnected;
47 | }
48 |
49 | -(void) read
50 | {
51 | // CBUUID *uuid_service = [CBUUID UUIDWithString:@RBL_SERVICE_UUID];
52 | // CBUUID *uuid_char = [CBUUID UUIDWithString:@RBL_CHAR_TX_UUID];
53 |
54 | // [self readValue:uuid_service characteristicUUID:uuid_char p:activePeripheral];
55 | [self readValue:serialServiceUUID characteristicUUID:readCharacteristicUUID p:activePeripheral];
56 |
57 | }
58 |
59 | -(void) write:(NSData *)d
60 | {
61 | NSLog(@"%@", @"write in ble.m");
62 | NSInteger data_len = d.length;
63 | NSData *buffer;
64 | int i = 0;
65 |
66 | for(; i < data_len; i+=MAX_BUF_LENGTH)
67 | {
68 | NSInteger remainLength = data_len-i;
69 | NSInteger bufLen = ((remainLength)>MAX_BUF_LENGTH) ? MAX_BUF_LENGTH:remainLength;
70 | buffer = [d subdataWithRange:NSMakeRange(i, bufLen)];
71 |
72 | NSLog(@"Buffer data %i %i %@", remainLength, i, [[NSString alloc] initWithData:buffer encoding:NSUTF8StringEncoding]);
73 |
74 | [self writeValue:serialServiceUUID characteristicUUID:writeCharacteristicUUID p:activePeripheral data:buffer];
75 | }
76 | }
77 |
78 | -(void) enableReadNotification:(CBPeripheral *)p
79 | {
80 | // CBUUID *uuid_service = [CBUUID UUIDWithString:@RBL_SERVICE_UUID];
81 | // CBUUID *uuid_char = [CBUUID UUIDWithString:@RBL_CHAR_TX_UUID];
82 | //
83 | // [self notification:uuid_service characteristicUUID:uuid_char p:p on:YES];
84 | [self notification:serialServiceUUID characteristicUUID:readCharacteristicUUID p:p on:YES];
85 |
86 | }
87 |
88 | -(void) notification:(CBUUID *)serviceUUID characteristicUUID:(CBUUID *)characteristicUUID p:(CBPeripheral *)p on:(BOOL)on
89 | {
90 | CBService *service = [self findServiceFromUUID:serviceUUID p:p];
91 |
92 | if (!service)
93 | {
94 | NSLog(@"Could not find service with UUID %@ on peripheral with UUID %@",
95 | [self CBUUIDToString:serviceUUID],
96 | p.identifier.UUIDString);
97 |
98 | return;
99 | }
100 |
101 | CBCharacteristic *characteristic = [self findCharacteristicFromUUID:characteristicUUID service:service];
102 |
103 | if (!characteristic)
104 | {
105 | NSLog(@"Could not find characteristic with UUID %@ on service with UUID %@ on peripheral with UUID %@",
106 | [self CBUUIDToString:characteristicUUID],
107 | [self CBUUIDToString:serviceUUID],
108 | p.identifier.UUIDString);
109 |
110 | return;
111 | }
112 |
113 | [p setNotifyValue:on forCharacteristic:characteristic];
114 | }
115 |
116 | -(UInt16) frameworkVersion
117 | {
118 | return RBL_BLE_FRAMEWORK_VER;
119 | }
120 |
121 | -(NSString *) CBUUIDToString:(CBUUID *) cbuuid;
122 | {
123 | NSData *data = cbuuid.data;
124 |
125 | if ([data length] == 2)
126 | {
127 | const unsigned char *tokenBytes = [data bytes];
128 | return [NSString stringWithFormat:@"%02x%02x", tokenBytes[0], tokenBytes[1]];
129 | }
130 | else if ([data length] == 16)
131 | {
132 | NSUUID* nsuuid = [[NSUUID alloc] initWithUUIDBytes:[data bytes]];
133 | return [nsuuid UUIDString];
134 | }
135 |
136 | return [cbuuid description];
137 | }
138 |
139 | -(void) readValue: (CBUUID *)serviceUUID characteristicUUID:(CBUUID *)characteristicUUID p:(CBPeripheral *)p
140 | {
141 | CBService *service = [self findServiceFromUUID:serviceUUID p:p];
142 |
143 | if (!service)
144 | {
145 | NSLog(@"Could not find service with UUID %@ on peripheral with UUID %@",
146 | [self CBUUIDToString:serviceUUID],
147 | p.identifier.UUIDString);
148 |
149 | return;
150 | }
151 |
152 | CBCharacteristic *characteristic = [self findCharacteristicFromUUID:characteristicUUID service:service];
153 |
154 | if (!characteristic)
155 | {
156 | NSLog(@"Could not find characteristic with UUID %@ on service with UUID %@ on peripheral with UUID %@",
157 | [self CBUUIDToString:characteristicUUID],
158 | [self CBUUIDToString:serviceUUID],
159 | p.identifier.UUIDString);
160 |
161 | return;
162 | }
163 |
164 | [p readValueForCharacteristic:characteristic];
165 | }
166 |
167 | -(void) writeValue:(CBUUID *)serviceUUID characteristicUUID:(CBUUID *)characteristicUUID p:(CBPeripheral *)p data:(NSData *)data
168 | {
169 | CBService *service = [self findServiceFromUUID:serviceUUID p:p];
170 |
171 | if (!service)
172 | {
173 | NSLog(@"Could not find service with UUID %@ on peripheral with UUID %@",
174 | [self CBUUIDToString:serviceUUID],
175 | p.identifier.UUIDString);
176 |
177 | return;
178 | }
179 |
180 | CBCharacteristic *characteristic = [self findCharacteristicFromUUID:characteristicUUID service:service];
181 |
182 | if (!characteristic)
183 | {
184 | NSLog(@"Could not find characteristic with UUID %@ on service with UUID %@ on peripheral with UUID %@",
185 | [self CBUUIDToString:characteristicUUID],
186 | [self CBUUIDToString:serviceUUID],
187 | p.identifier.UUIDString);
188 |
189 | return;
190 | }
191 |
192 | NSLog(@"%@", @"writeValue in ble.m");
193 |
194 | NSLog(@"Buffer data %i", data.length);
195 | if ((characteristic.properties & CBCharacteristicPropertyWrite) == CBCharacteristicPropertyWrite) {
196 | [p writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
197 | }
198 | else if ((characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) == CBCharacteristicPropertyWriteWithoutResponse) {
199 | [p writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
200 | }
201 | }
202 |
203 | -(UInt16) swap:(UInt16)s
204 | {
205 | UInt16 temp = s << 8;
206 | temp |= (s >> 8);
207 | return temp;
208 | }
209 |
210 | - (void) controlSetup
211 | {
212 | self.CM = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
213 | }
214 |
215 | - (int) findBLEPeripherals:(int) timeout
216 | {
217 | if (self.CM.state != CBCentralManagerStatePoweredOn)
218 | {
219 | NSLog(@"CoreBluetooth not correctly initialized !");
220 | NSLog(@"State = %ld (%s)\r\n", (long)self.CM.state, [self centralManagerStateToString:self.CM.state]);
221 | return -1;
222 | }
223 |
224 | [NSTimer scheduledTimerWithTimeInterval:(float)timeout target:self selector:@selector(scanTimer:) userInfo:nil repeats:NO];
225 |
226 | #if TARGET_OS_IPHONE
227 | redBearLabsServiceUUID = [CBUUID UUIDWithString:@RBL_SERVICE_UUID];
228 | adafruitServiceUUID = [CBUUID UUIDWithString:@ADAFRUIT_SERVICE_UUID];
229 | lairdServiceUUID = [CBUUID UUIDWithString:@LAIRD_SERVICE_UUID];
230 | blueGigaServiceUUID = [CBUUID UUIDWithString:@BLUEGIGA_SERVICE_UUID];
231 | rongtaSerivceUUID = [CBUUID UUIDWithString:@RONGTA_SERVICE_UUID];
232 | posnetSerivceUUID = [CBUUID UUIDWithString:@POSNET_SERVICE_UUID];
233 |
234 | NSArray *services = @[redBearLabsServiceUUID, adafruitServiceUUID, lairdServiceUUID, blueGigaServiceUUID, rongtaSerivceUUID, posnetSerivceUUID];
235 | [self.CM scanForPeripheralsWithServices:services options: nil];
236 | #else
237 | [self.CM scanForPeripheralsWithServices:nil options:nil]; // Start scanning
238 | #endif
239 |
240 | NSLog(@"scanForPeripheralsWithServices");
241 |
242 | return 0; // Started scanning OK !
243 | }
244 |
245 | - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
246 | {
247 | done = false;
248 |
249 | [[self delegate] bleDidDisconnect];
250 |
251 | isConnected = false;
252 | }
253 |
254 | - (void) connectPeripheral:(CBPeripheral *)peripheral
255 | {
256 | NSLog(@"Connecting to peripheral with UUID : %@", peripheral.identifier.UUIDString);
257 |
258 | self.activePeripheral = peripheral;
259 | self.activePeripheral.delegate = self;
260 | [self.CM connectPeripheral:self.activePeripheral
261 | options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];
262 | }
263 |
264 | - (const char *) centralManagerStateToString: (int)state
265 | {
266 | switch(state)
267 | {
268 | case CBCentralManagerStateUnknown:
269 | return "State unknown (CBCentralManagerStateUnknown)";
270 | case CBCentralManagerStateResetting:
271 | return "State resetting (CBCentralManagerStateUnknown)";
272 | case CBCentralManagerStateUnsupported:
273 | return "State BLE unsupported (CBCentralManagerStateResetting)";
274 | case CBCentralManagerStateUnauthorized:
275 | return "State unauthorized (CBCentralManagerStateUnauthorized)";
276 | case CBCentralManagerStatePoweredOff:
277 | return "State BLE powered off (CBCentralManagerStatePoweredOff)";
278 | case CBCentralManagerStatePoweredOn:
279 | return "State powered up and ready (CBCentralManagerStatePoweredOn)";
280 | default:
281 | return "State unknown";
282 | }
283 |
284 | return "Unknown state";
285 | }
286 |
287 | - (void) scanTimer:(NSTimer *)timer
288 | {
289 | [self.CM stopScan];
290 | NSLog(@"Stopped Scanning");
291 | NSLog(@"Known peripherals : %lu", (unsigned long)[self.peripherals count]);
292 | [self printKnownPeripherals];
293 | }
294 |
295 | - (void) printKnownPeripherals
296 | {
297 | NSLog(@"List of currently known peripherals :");
298 |
299 | for (int i = 0; i < self.peripherals.count; i++)
300 | {
301 | CBPeripheral *p = [self.peripherals objectAtIndex:i];
302 |
303 | if (p.identifier != NULL)
304 | NSLog(@"%d | %@", i, p.identifier.UUIDString);
305 | else
306 | NSLog(@"%d | NULL", i);
307 |
308 | [self printPeripheralInfo:p];
309 | }
310 | }
311 |
312 | - (void) printPeripheralInfo:(CBPeripheral*)peripheral
313 | {
314 | NSLog(@"------------------------------------");
315 | NSLog(@"Peripheral Info :");
316 |
317 | if (peripheral.identifier != NULL)
318 | NSLog(@"UUID : %@", peripheral.identifier.UUIDString);
319 | else
320 | NSLog(@"UUID : NULL");
321 |
322 | NSLog(@"Name : %@", peripheral.name);
323 | NSLog(@"-------------------------------------");
324 | }
325 |
326 | - (BOOL) UUIDSAreEqual:(NSUUID *)UUID1 UUID2:(NSUUID *)UUID2
327 | {
328 | if ([UUID1.UUIDString isEqualToString:UUID2.UUIDString])
329 | return TRUE;
330 | else
331 | return FALSE;
332 | }
333 |
334 | -(void) getAllServicesFromPeripheral:(CBPeripheral *)p
335 | {
336 | [p discoverServices:nil]; // Discover all services without filter
337 | }
338 |
339 | -(void) getAllCharacteristicsFromPeripheral:(CBPeripheral *)p
340 | {
341 | for (int i=0; i < p.services.count; i++)
342 | {
343 | CBService *s = [p.services objectAtIndex:i];
344 | // printf("Fetching characteristics for service with UUID : %s\r\n",[self CBUUIDToString:s.UUID]);
345 | [p discoverCharacteristics:nil forService:s];
346 | }
347 | }
348 |
349 | -(int) compareCBUUID:(CBUUID *) UUID1 UUID2:(CBUUID *)UUID2
350 | {
351 | char b1[16];
352 | char b2[16];
353 | [UUID1.data getBytes:b1];
354 | [UUID2.data getBytes:b2];
355 |
356 | if (memcmp(b1, b2, UUID1.data.length) == 0)
357 | return 1;
358 | else
359 | return 0;
360 | }
361 |
362 | -(int) compareCBUUIDToInt:(CBUUID *)UUID1 UUID2:(UInt16)UUID2
363 | {
364 | char b1[16];
365 |
366 | [UUID1.data getBytes:b1];
367 | UInt16 b2 = [self swap:UUID2];
368 |
369 | if (memcmp(b1, (char *)&b2, 2) == 0)
370 | return 1;
371 | else
372 | return 0;
373 | }
374 |
375 | -(UInt16) CBUUIDToInt:(CBUUID *) UUID
376 | {
377 | char b1[16];
378 | [UUID.data getBytes:b1];
379 | return ((b1[0] << 8) | b1[1]);
380 | }
381 |
382 | -(CBUUID *) IntToCBUUID:(UInt16)UUID
383 | {
384 | char t[16];
385 | t[0] = ((UUID >> 8) & 0xff); t[1] = (UUID & 0xff);
386 | NSData *data = [[NSData alloc] initWithBytes:t length:16];
387 | return [CBUUID UUIDWithData:data];
388 | }
389 |
390 | -(CBService *) findServiceFromUUID:(CBUUID *)UUID p:(CBPeripheral *)p
391 | {
392 | for(int i = 0; i < p.services.count; i++)
393 | {
394 | CBService *s = [p.services objectAtIndex:i];
395 | if ([self compareCBUUID:s.UUID UUID2:UUID])
396 | return s;
397 | }
398 |
399 | return nil; //Service not found on this peripheral
400 | }
401 |
402 | -(CBCharacteristic *) findCharacteristicFromUUID:(CBUUID *)UUID service:(CBService*)service
403 | {
404 | for(int i=0; i < service.characteristics.count; i++)
405 | {
406 | CBCharacteristic *c = [service.characteristics objectAtIndex:i];
407 | if ([self compareCBUUID:c.UUID UUID2:UUID]) return c;
408 | }
409 |
410 | return nil; //Characteristic not found on this service
411 | }
412 |
413 | #if TARGET_OS_IPHONE
414 | //-- no need for iOS
415 | #else
416 | - (BOOL) isLECapableHardware
417 | {
418 | NSString * state = nil;
419 |
420 | switch ([CM state])
421 | {
422 | case CBCentralManagerStateUnsupported:
423 | state = @"The platform/hardware doesn't support Bluetooth Low Energy.";
424 | break;
425 |
426 | case CBCentralManagerStateUnauthorized:
427 | state = @"The app is not authorized to use Bluetooth Low Energy.";
428 | break;
429 |
430 | case CBCentralManagerStatePoweredOff:
431 | state = @"Bluetooth is currently powered off.";
432 | break;
433 |
434 | case CBCentralManagerStatePoweredOn:
435 | return TRUE;
436 |
437 | case CBCentralManagerStateUnknown:
438 | default:
439 | return FALSE;
440 |
441 | }
442 |
443 | NSLog(@"Central manager state: %@", state);
444 |
445 | NSAlert *alert = [[NSAlert alloc] init];
446 | [alert setMessageText:state];
447 | [alert addButtonWithTitle:@"OK"];
448 | [alert setIcon:[[NSImage alloc] initWithContentsOfFile:@"AppIcon"]];
449 | [alert beginSheetModalForWindow:nil modalDelegate:self didEndSelector:nil contextInfo:nil];
450 |
451 | return FALSE;
452 | }
453 | #endif
454 |
455 | - (void)centralManagerDidUpdateState:(CBCentralManager *)central
456 | {
457 | #if TARGET_OS_IPHONE
458 | char *stateString = [self centralManagerStateToString:central.state];
459 |
460 | NSLog(@"Status of CoreBluetooth central manager changed %ld (%s)", (long)central.state, stateString);
461 |
462 | bool isBluetoothEnabled = false;
463 | if (central.state == CBCentralManagerStatePoweredOn) {
464 | isBluetoothEnabled = true;
465 | }
466 |
467 | if (!isBluetoothEnabled && isConnected) {
468 | [[self delegate] bleDidDisconnect];
469 | done = false;
470 | isConnected = false;
471 | }
472 |
473 | [[self delegate] bleDidChangedState:isBluetoothEnabled];
474 | #else
475 | [self isLECapableHardware];
476 | #endif
477 | }
478 |
479 | - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
480 | {
481 | if (!self.peripherals)
482 | self.peripherals = [[NSMutableArray alloc] initWithObjects:peripheral,nil];
483 | else
484 | {
485 | for(int i = 0; i < self.peripherals.count; i++)
486 | {
487 | CBPeripheral *p = [self.peripherals objectAtIndex:i];
488 | [p bts_setAdvertisementData:advertisementData RSSI:RSSI];
489 |
490 | if ((p.identifier == NULL) || (peripheral.identifier == NULL))
491 | continue;
492 |
493 | if ([self UUIDSAreEqual:p.identifier UUID2:peripheral.identifier])
494 | {
495 | [self.peripherals replaceObjectAtIndex:i withObject:peripheral];
496 | NSLog(@"Duplicate UUID found updating...");
497 | return;
498 | }
499 | }
500 |
501 | [self.peripherals addObject:peripheral];
502 |
503 | NSLog(@"New UUID, adding");
504 | }
505 |
506 | NSLog(@"didDiscoverPeripheral");
507 | }
508 |
509 | - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
510 | {
511 | if (peripheral.identifier != NULL)
512 | NSLog(@"Connected to %@ successful", peripheral.identifier.UUIDString);
513 | else
514 | NSLog(@"Connected to NULL successful");
515 |
516 | self.activePeripheral = peripheral;
517 | [self.activePeripheral discoverServices:nil];
518 | [self getAllServicesFromPeripheral:peripheral];
519 | }
520 |
521 | static bool done = false;
522 |
523 | - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
524 | {
525 | if (!error)
526 | {
527 | NSLog(@"Characteristics of service with UUID : %@ found\n",[self CBUUIDToString:service.UUID]);
528 |
529 | for (int i=0; i < service.characteristics.count; i++)
530 | {
531 | CBCharacteristic *c = [service.characteristics objectAtIndex:i];
532 | NSLog(@"Found characteristic %@\n",[ self CBUUIDToString:c.UUID]);
533 | CBService *s = [peripheral.services objectAtIndex:(peripheral.services.count - 1)];
534 |
535 | if ([service.UUID isEqual:s.UUID])
536 | {
537 | if (!done)
538 | {
539 | [self enableReadNotification:activePeripheral];
540 | [[self delegate] bleDidConnect];
541 | isConnected = true;
542 | done = true;
543 | }
544 |
545 | break;
546 | }
547 | }
548 | }
549 | else
550 | {
551 | NSLog(@"Characteristic discorvery unsuccessful!");
552 | }
553 | }
554 |
555 | - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
556 | {
557 | if (!error)
558 | {
559 | // Determine if we're connected to Red Bear Labs, Adafruit or Laird hardware
560 | for (CBService *service in peripheral.services) {
561 |
562 | if ([service.UUID isEqual:redBearLabsServiceUUID]) {
563 | NSLog(@"RedBearLabs Bluetooth");
564 | serialServiceUUID = redBearLabsServiceUUID;
565 | readCharacteristicUUID = [CBUUID UUIDWithString:@RBL_CHAR_TX_UUID];
566 | writeCharacteristicUUID = [CBUUID UUIDWithString:@RBL_CHAR_RX_UUID];
567 | break;
568 | } else if ([service.UUID isEqual:adafruitServiceUUID]) {
569 | NSLog(@"Adafruit Bluefruit LE");
570 | serialServiceUUID = adafruitServiceUUID;
571 | readCharacteristicUUID = [CBUUID UUIDWithString:@ADAFRUIT_CHAR_TX_UUID];
572 | writeCharacteristicUUID = [CBUUID UUIDWithString:@ADAFRUIT_CHAR_RX_UUID];
573 | break;
574 | } else if ([service.UUID isEqual:lairdServiceUUID]) {
575 | NSLog(@"Laird BL600");
576 | serialServiceUUID = lairdServiceUUID;
577 | readCharacteristicUUID = [CBUUID UUIDWithString:@LAIRD_CHAR_TX_UUID];
578 | writeCharacteristicUUID = [CBUUID UUIDWithString:@LAIRD_CHAR_RX_UUID];
579 | break;
580 | } else if ([service.UUID isEqual:blueGigaServiceUUID]) {
581 | NSLog(@"BlueGiga Bluetooth");
582 | serialServiceUUID = blueGigaServiceUUID;
583 | readCharacteristicUUID = [CBUUID UUIDWithString:@BLUEGIGA_CHAR_TX_UUID];
584 | writeCharacteristicUUID = [CBUUID UUIDWithString:@BLUEGIGA_CHAR_RX_UUID];
585 | break;
586 | } else if ([service.UUID isEqual:rongtaSerivceUUID]) {
587 | serialServiceUUID = rongtaSerivceUUID;
588 | readCharacteristicUUID = [CBUUID UUIDWithString:@RONGTA_CHAR_TX_UUID];
589 | writeCharacteristicUUID = [CBUUID UUIDWithString:@RONGTA_CHAR_RX_UUID];
590 | } else if ([service.UUID isEqual:posnetSerivceUUID]) {
591 | NSLog(@"Posnet");
592 | serialServiceUUID = posnetSerivceUUID;
593 | readCharacteristicUUID = [CBUUID UUIDWithString:@POSNET_CHAR_TX_UUID];
594 | writeCharacteristicUUID = [CBUUID UUIDWithString:@POSNET_CHAR_RX_UUID];
595 | } else {
596 | // ignore unknown services
597 | }
598 | }
599 |
600 | // TODO - future versions should just get characteristics we care about
601 | // [peripheral discoverCharacteristics:characteristics forService:service];
602 | [self getAllCharacteristicsFromPeripheral:peripheral];
603 | }
604 | else
605 | {
606 | NSLog(@"Service discovery was unsuccessful!");
607 | }
608 | }
609 |
610 | - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
611 | {
612 | if (!error)
613 | {
614 | // printf("Updated notification state for characteristic with UUID %s on service with UUID %s on peripheral with UUID %s\r\n",[self CBUUIDToString:characteristic.UUID],[self CBUUIDToString:characteristic.service.UUID],[self UUIDToString:peripheral.UUID]);
615 | }
616 | else
617 | {
618 | NSLog(@"Error in setting notification state for characteristic with UUID %@ on service with UUID %@ on peripheral with UUID %@",
619 | [self CBUUIDToString:characteristic.UUID],
620 | [self CBUUIDToString:characteristic.service.UUID],
621 | peripheral.identifier.UUIDString);
622 |
623 | NSLog(@"Error code was %s", [[error description] cStringUsingEncoding:NSStringEncodingConversionAllowLossy]);
624 | }
625 | }
626 |
627 | - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
628 | {
629 | static unsigned char buf[512];
630 | NSInteger data_len;
631 |
632 | if (!error)
633 | {
634 | if ([characteristic.UUID isEqual:readCharacteristicUUID])
635 | {
636 | data_len = characteristic.value.length;
637 | [characteristic.value getBytes:buf length:data_len];
638 |
639 | [[self delegate] bleDidReceiveData:buf length:data_len];
640 | }
641 | }
642 | else
643 | {
644 | NSLog(@"updateValueForCharacteristic failed!");
645 | }
646 | }
647 |
648 | - (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(NSError *)error
649 | {
650 | if (!isConnected)
651 | return;
652 |
653 | if (rssi != peripheral.RSSI.intValue)
654 | {
655 | rssi = peripheral.RSSI.intValue;
656 | [[self delegate] bleDidUpdateRSSI:activePeripheral.RSSI];
657 | }
658 | }
659 |
660 | @end
661 |
--------------------------------------------------------------------------------
/ios/RCTBluetoothSerial/BLEDefines.h:
--------------------------------------------------------------------------------
1 |
2 | /*
3 |
4 | Copyright (c) 2013 RedBearLab
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 |
12 | */
13 |
14 | // BlueGiga Service
15 | #define BLUEGIGA_SERVICE_UUID "1D5688DE-866D-3AA4-EC46-A1BDDB37ECF6"
16 | #define BLUEGIGA_CHAR_TX_UUID "AF20fBAC-2518-4998-9AF7-AF42540731B3"
17 | #define BLUEGIGA_CHAR_RX_UUID "AF20fBAC-2518-4998-9AF7-AF42540731B3"
18 |
19 | // RBL Service
20 | #define RBL_SERVICE_UUID "713D0000-503E-4C75-BA94-3148F18D941E"
21 | #define RBL_CHAR_TX_UUID "713D0002-503E-4C75-BA94-3148F18D941E"
22 | #define RBL_CHAR_RX_UUID "713D0003-503E-4C75-BA94-3148F18D941E"
23 |
24 | // Adafruit BLE
25 | // http://learn.adafruit.com/getting-started-with-the-nrf8001-bluefruit-le-breakout/adding-app-support
26 | // Adafruit | Nordic's TX and RX are the opposite of RBL. This code uses RBL perspective for naming.
27 | #define ADAFRUIT_SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
28 | #define ADAFRUIT_CHAR_TX_UUID "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
29 | #define ADAFRUIT_CHAR_RX_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
30 |
31 | // Laird Virtual Serial Port (vSP) service for BL600 http://www.lairdtech.com/DownloadAsset.aspx?id=2147489885
32 | #define LAIRD_SERVICE_UUID "569a1101-b87f-490c-92cb-11ba5ea5167c"
33 | #define LAIRD_CHAR_TX_UUID "569a2000-b87f-490c-92cb-11ba5ea5167c"
34 | #define LAIRD_CHAR_RX_UUID "569a2001-b87f-490c-92cb-11ba5ea5167c"
35 |
36 | #define RONGTA_SERVICE_UUID "E7810A71-73AE-499D-8C15-FAA9AEF0C3F2"
37 | #define RONGTA_CHAR_TX_UUID "BEF8D6C9-9C21-4C9E-B632-BD58C1009F9F"
38 | #define RONGTA_CHAR_RX_UUID "BEF8D6C9-9C21-4C9E-B632-BD58C1009F9F"
39 |
40 | #define POSNET_SERVICE_UUID "53544D54-4552-494F-5345-525631303030"
41 | #define POSNET_CHAR_TX_UUID "53544F55-4152-5449-4E20-205630303031"
42 | #define POSNET_CHAR_RX_UUID "53544F55-4152-5449-4E20-205630303031"
43 |
44 | #define RBL_BLE_FRAMEWORK_VER 0x0200
45 |
--------------------------------------------------------------------------------
/ios/RCTBluetoothSerial/CBPeripheral+BTSExtensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // CBPeripheral+BTSExtensions.h
3 | // BluetoothSerial Cordova Plugin
4 | //
5 | // (c) 2103-2015 Don Coleman
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | #import
20 | #import
21 | #import
22 |
23 | @interface CBPeripheral(com_megster_bluetoothserial_extension)
24 |
25 | @property (nonatomic, retain) NSString *btsAdvertising;
26 | @property (nonatomic, retain) NSNumber *btsAdvertisementRSSI;
27 |
28 | -(void)bts_setAdvertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber*)rssi;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/ios/RCTBluetoothSerial/CBPeripheral+BTSExtensions.m:
--------------------------------------------------------------------------------
1 | //
2 | // CBPeripheral+BTSExtensions.m
3 | // BluetoothSerial Cordova Plugin
4 | //
5 | // (c) 2103-2015 Don Coleman
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | #import "CBPeripheral+BTSExtensions.h"
20 |
21 | static char BTS_ADVERTISING_IDENTIFER;
22 | static char BTS_ADVERTISEMENT_RSSI_IDENTIFER;
23 |
24 | @implementation CBPeripheral(com_megster_bluetoothserial_extension)
25 |
26 | // AdvertisementData and RSSI are from didDiscoverPeripheral.
27 | // Save the manufacturerData so we can pass to Cordova in the peripheral
28 | -(void)bts_setAdvertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)rssi{
29 |
30 | if (advertisementData) {
31 | id manufacturerData = [advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey];
32 | if (manufacturerData) {
33 | const uint8_t *bytes = [manufacturerData bytes];
34 | long len = [manufacturerData length];
35 | // skip manufacturer uuid
36 | NSData *data = [NSData dataWithBytes:bytes+2 length:len-2];
37 | [self setBtsAdvertising: [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
38 | }
39 | }
40 |
41 | [self setBtsAdvertisementRSSI: rssi];
42 | }
43 |
44 | -(void)setBtsAdvertising:(NSString *)newAdvertisingValue{
45 | objc_setAssociatedObject(self, &BTS_ADVERTISING_IDENTIFER, newAdvertisingValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
46 | }
47 |
48 | -(NSString*)btsAdvertising{
49 | return objc_getAssociatedObject(self, &BTS_ADVERTISING_IDENTIFER);
50 | }
51 |
52 |
53 | -(void)setBtsAdvertisementRSSI:(NSNumber *)newAdvertisementRSSIValue {
54 | objc_setAssociatedObject(self, &BTS_ADVERTISEMENT_RSSI_IDENTIFER, newAdvertisementRSSIValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
55 | }
56 |
57 | -(NSString*)btsAdvertisementRSSI{
58 | return objc_getAssociatedObject(self, &BTS_ADVERTISEMENT_RSSI_IDENTIFER);
59 | }
60 |
61 | @end
62 |
--------------------------------------------------------------------------------
/ios/RCTBluetoothSerial/RCTBluetoothSerial.h:
--------------------------------------------------------------------------------
1 | //
2 | // RCTBluetoothSerial.h
3 | // RCTBluetoothSerial
4 | //
5 | // Created by Jakub Martyčák on 17.04.16.
6 | // Copyright © 2016 Jakub Martyčák. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | //#import "RCTEventEmitter.h" Wasnt working properly yet, events were fired but listeneres not called
13 | #import "BLE.h"
14 |
15 | @interface RCTBluetoothSerial : NSObject {
16 | BLE *_bleShield;
17 | BOOL _subscribed;
18 | RCTPromiseResolveBlock _connectionResolver;
19 | RCTPromiseRejectBlock _connectionRejector;
20 | NSString* _subscribeCallbackId;
21 | NSString* _subscribeBytesCallbackId;
22 | NSString* _rssiCallbackId;
23 | NSMutableString *_buffer;
24 | NSString *_delimiter;
25 | }
26 | @end
27 |
--------------------------------------------------------------------------------
/ios/RCTBluetoothSerial/RCTBluetoothSerial.m:
--------------------------------------------------------------------------------
1 | //
2 | // RCTBluetoothSerial.m
3 | // RCTBluetoothSerial
4 | //
5 | // Created by Jakub Martyčák on 17.04.16.
6 | // Copyright © 2016 Jakub Martyčák. All rights reserved.
7 | //
8 |
9 | #import "RCTBluetoothSerial.h"
10 |
11 | @interface RCTBluetoothSerial()
12 | - (NSString *)readUntilDelimiter:(NSString *)delimiter;
13 | - (NSMutableArray *)getPeripheralList;
14 | - (void)sendDataToSubscriber;
15 | - (CBPeripheral *)findPeripheralByUUID:(NSString *)uuid;
16 | - (void)connectToUUID:(NSString *)uuid;
17 | - (void)listPeripheralsTimer:(NSTimer *)timer;
18 | - (void)connectFirstDeviceTimer:(NSTimer *)timer;
19 | - (void)connectUuidTimer:(NSTimer *)timer;
20 | @end
21 |
22 | @implementation RCTBluetoothSerial
23 |
24 | RCT_EXPORT_MODULE();
25 |
26 | @synthesize bridge = _bridge;
27 |
28 | - (instancetype)init {
29 | _bleShield = [[BLE alloc] init];
30 | [_bleShield controlSetup];
31 | [_bleShield setDelegate:self];
32 |
33 | _buffer = [[NSMutableString alloc] init];
34 | return self;
35 | }
36 |
37 | - (dispatch_queue_t)methodQueue
38 | {
39 | // run all module methods in main thread
40 | // if we don't no timer callbacks got called
41 | return dispatch_get_main_queue();
42 | }
43 |
44 | - (NSArray *)supportedEvents
45 | {
46 | return @[@"connectionSuccess",@"connectionLost",@"bluetoothEnabled",@"bluetoothDisabled",@"data"];
47 | }
48 |
49 |
50 | #pragma mark - Methods available form Javascript
51 |
52 | RCT_EXPORT_METHOD(connect:(NSString *)uuid
53 | resolver:(RCTPromiseResolveBlock)resolve
54 | rejector:(RCTPromiseRejectBlock)reject)
55 | {
56 |
57 | NSLog(@"connect");
58 |
59 | // if the uuid is null or blank, scan and
60 | // connect to the first available device
61 |
62 | if (uuid == (NSString *)[NSNull null]) {
63 | [self connectToFirstDevice];
64 | } else if ([uuid isEqualToString:@""]) {
65 | [self connectToFirstDevice];
66 | } else {
67 | [self connectToUUID:uuid];
68 | }
69 |
70 | _connectionResolver = resolve;
71 | _connectionRejector = reject;
72 | }
73 |
74 | RCT_EXPORT_METHOD(disconnect:(RCTPromiseResolveBlock)resolve
75 | rejector:(RCTPromiseRejectBlock)reject)
76 | {
77 |
78 | NSLog(@"disconnect");
79 |
80 | _connectionResolver = nil;
81 | _connectionRejector = nil;
82 |
83 | if (_bleShield.activePeripheral) {
84 | if(_bleShield.activePeripheral.state == CBPeripheralStateConnected)
85 | {
86 | [[_bleShield CM] cancelPeripheralConnection:[_bleShield activePeripheral]];
87 | }
88 | }
89 |
90 | resolve((id)kCFBooleanTrue);
91 | }
92 |
93 | RCT_EXPORT_METHOD(subscribe:(NSString *)delimiter
94 | resolver:(RCTPromiseResolveBlock)resolve
95 | rejector:(RCTPromiseRejectBlock)reject)
96 | {
97 | NSLog(@"subscribe");
98 |
99 | if (delimiter != nil) {
100 | _delimiter = [delimiter copy];
101 | _subscribed = TRUE;
102 | resolve((id)kCFBooleanTrue);
103 | } else {
104 | NSError *err = nil;
105 | reject(@"no_delimiter", @"Delimiter was null", err);
106 | }
107 | }
108 |
109 | RCT_EXPORT_METHOD(unsubscribe:(NSString *)delimiter
110 | resolver:(RCTPromiseResolveBlock)resolve)
111 | {
112 | NSLog(@"unsubscribe");
113 |
114 | _delimiter = nil;
115 | _subscribed = FALSE;
116 |
117 | resolve((id)kCFBooleanTrue);
118 | }
119 |
120 | RCT_EXPORT_METHOD(writeToDevice:(NSString *)message
121 | resolver:(RCTPromiseResolveBlock)resolve
122 | rejector:(RCTPromiseRejectBlock)reject)
123 | {
124 | NSLog(@"write");
125 | if (message != nil) {
126 | NSData *data = [[NSData alloc] initWithBase64EncodedString:message options:NSDataBase64DecodingIgnoreUnknownCharacters];
127 | [_bleShield write:data];
128 | resolve((id)kCFBooleanTrue);
129 | } else {
130 | NSError *err = nil;
131 | reject(@"no_data", @"Data was null", err);
132 | }
133 | }
134 |
135 | RCT_EXPORT_METHOD(writeTextToDevice:(NSString *)text
136 | resolver:(RCTPromiseResolveBlock)resolve
137 | rejector:(RCTPromiseRejectBlock)reject)
138 | {
139 | NSLog(@"write");
140 | if (text != nil) {
141 | NSData *data = nil;
142 | NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
143 | data = [text dataUsingEncoding:enc];
144 | [_bleShield write:data];
145 | resolve((id)kCFBooleanTrue);
146 | } else {
147 | NSError *err = nil;
148 | reject(@"no_data", @"Data was null", err);
149 | }
150 | }
151 |
152 | RCT_EXPORT_METHOD(writeHexToDevice:(NSString *)hexString
153 | resolver:(RCTPromiseResolveBlock)resolve
154 | rejector:(RCTPromiseRejectBlock)reject)
155 | {
156 | NSLog(@"write");
157 | NSData *data = [self hexToBytes:hexString];
158 | [_bleShield write:data];
159 | if (hexString != nil) {
160 | resolve((id)kCFBooleanTrue);
161 | } else {
162 | NSError *err = nil;
163 | reject(@"no_data", @"Data was null", err);
164 | }
165 | }
166 |
167 | -(NSData*) hexToBytes:(NSString *)hexString {
168 | hexString = [hexString stringByReplacingOccurrencesOfString:@" " withString: @""];
169 | NSMutableData* data = [NSMutableData data];
170 | int idx;
171 | for (idx = 0; idx+2 <= hexString.length; idx+=2) {
172 | NSRange range = NSMakeRange(idx, 2);
173 | NSString* hexStr = [hexString substringWithRange:range];
174 | NSScanner* scanner = [NSScanner scannerWithString:hexStr];
175 | unsigned int intValue;
176 | [scanner scanHexInt:&intValue];
177 | [data appendBytes:&intValue length:1];
178 | }
179 | return data;
180 | }
181 |
182 | RCT_EXPORT_METHOD(list:(RCTPromiseResolveBlock)resolve
183 | rejector:(RCTPromiseRejectBlock)reject)
184 | {
185 | [self scanForBLEPeripherals:3];
186 | [NSTimer scheduledTimerWithTimeInterval:(float)3.0
187 | target:self
188 | selector:@selector(listPeripheralsTimer:)
189 | userInfo:resolve
190 | repeats:NO];
191 | }
192 |
193 | RCT_EXPORT_METHOD(isEnabled:(RCTPromiseResolveBlock)resolve
194 | rejector:(RCTPromiseRejectBlock)reject)
195 | {
196 | // short delay so CBCentralManger can spin up bluetooth
197 | [NSTimer scheduledTimerWithTimeInterval:(float)0.2
198 | target:self
199 | selector:@selector(bluetoothStateTimer:)
200 | userInfo:resolve
201 | repeats:NO];
202 |
203 | }
204 |
205 | RCT_EXPORT_METHOD(isConnected:(RCTPromiseResolveBlock)resolve
206 | rejector:(RCTPromiseRejectBlock)reject)
207 | {
208 | if (_bleShield.isConnected) {
209 | resolve((id)kCFBooleanTrue);
210 | } else {
211 | resolve((id)kCFBooleanFalse);
212 | }
213 | }
214 |
215 | RCT_EXPORT_METHOD(available:(RCTPromiseResolveBlock)resolve
216 | rejector:(RCTPromiseRejectBlock)reject)
217 | {
218 | // future versions could use messageAsNSInteger, but realistically, int is fine for buffer length
219 | NSNumber *buffLen = [NSNumber numberWithInteger:[_buffer length]];
220 | resolve(buffLen);
221 | }
222 |
223 | RCT_EXPORT_METHOD(read:(RCTPromiseResolveBlock)resolve
224 | rejector:(RCTPromiseRejectBlock)reject)
225 | {
226 | NSString *message = @"";
227 |
228 | if ([_buffer length] > 0) {
229 | long end = [_buffer length] - 1;
230 | message = [_buffer substringToIndex:end];
231 | NSRange entireString = NSMakeRange(0, end);
232 | [_buffer deleteCharactersInRange:entireString];
233 | }
234 |
235 | resolve(message);
236 | }
237 |
238 | RCT_EXPORT_METHOD(readUntil:(NSString *)delimiter
239 | resolver:(RCTPromiseResolveBlock)resolve
240 | rejector:(RCTPromiseRejectBlock)reject)
241 | {
242 | NSString *message = [self readUntilDelimiter:delimiter];
243 | resolve(message);
244 | }
245 |
246 | RCT_EXPORT_METHOD(clear:(RCTPromiseResolveBlock)resolve)
247 | {
248 | long end = [_buffer length] - 1;
249 | NSRange truncate = NSMakeRange(0, end);
250 | [_buffer deleteCharactersInRange:truncate];
251 | resolve((id)kCFBooleanTrue);
252 | }
253 |
254 | #pragma mark - BLEDelegate
255 |
256 | - (void)bleDidReceiveData:(unsigned char *)data length:(int)length
257 | {
258 | NSLog(@"bleDidReceiveData");
259 |
260 | // Append to the buffer
261 | NSData *d = [NSData dataWithBytes:data length:length];
262 | NSString *s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding];
263 | NSLog(@"Received %@", s);
264 |
265 | if (s) {
266 | [_buffer appendString:s];
267 |
268 | if (_subscribed) {
269 | [self sendDataToSubscriber]; // only sends if a delimiter is hit
270 | }
271 |
272 | } else {
273 | NSLog(@"Error converting received data into a String.");
274 | }
275 |
276 | // Always send raw data if someone is listening
277 | //if (_subscribeBytesCallbackId) {
278 | // NSData* nsData = [NSData dataWithBytes:(const void *)data length:length];
279 | //}
280 |
281 | }
282 |
283 | - (void)bleDidChangedState:(bool)isEnabled
284 | {
285 | NSLog(@"bleDidChangedState");
286 | NSString *eventName;
287 | if (isEnabled) {
288 | eventName = @"bluetoothEnabled";
289 | } else {
290 | eventName = @"bluetoothDisabled";
291 | }
292 | [self.bridge.eventDispatcher sendDeviceEventWithName:eventName body:nil];
293 | }
294 |
295 | - (void)bleDidConnect
296 | {
297 | NSLog(@"bleDidConnect");
298 | [self.bridge.eventDispatcher sendDeviceEventWithName:@"connectionSuccess" body:nil];
299 | //[self sendEventWithName:@"connectionSuccess" body:nil];
300 |
301 | if (_connectionResolver) {
302 | _connectionResolver((id)kCFBooleanTrue);
303 | }
304 | }
305 |
306 | - (void)bleDidDisconnect
307 | {
308 | // TODO is there anyway to figure out why we disconnected?
309 | NSLog(@"bleDidDisconnect");
310 | [self.bridge.eventDispatcher sendDeviceEventWithName:@"connectionLost" body:nil];
311 | //[self sendEventWithName:@"connectionLost" body:nil];
312 |
313 | _connectionResolver = nil;
314 | }
315 |
316 | #pragma mark - timers
317 |
318 | -(void)listPeripheralsTimer:(NSTimer *)timer
319 | {
320 | RCTPromiseResolveBlock resolve = [timer userInfo];
321 | NSMutableArray *peripherals = [self getPeripheralList];
322 | resolve(peripherals);
323 | }
324 |
325 | -(void)connectFirstDeviceTimer:(NSTimer *)timer
326 | {
327 | if(_bleShield.peripherals.count > 0) {
328 | NSLog(@"Connecting");
329 | [_bleShield connectPeripheral:[_bleShield.peripherals objectAtIndex:0]];
330 | } else {
331 | NSString *message = @"Did not find any BLE peripherals";
332 | NSError *err = nil;
333 | NSLog(@"%@", message);
334 | _connectionRejector(@"no_peripherals", message, err);
335 |
336 | }
337 | }
338 |
339 | -(void)connectUuidTimer:(NSTimer *)timer
340 | {
341 | NSString *uuid = [timer userInfo];
342 | CBPeripheral *peripheral = [self findPeripheralByUUID:uuid];
343 |
344 | if (peripheral) {
345 | [_bleShield connectPeripheral:peripheral];
346 | } else {
347 | NSString *message = [NSString stringWithFormat:@"Could not find peripheral %@.", uuid];
348 | NSError *err = nil;
349 | NSLog(@"%@", message);
350 | _connectionRejector(@"wrong_uuid", message, err);
351 |
352 | }
353 | }
354 |
355 | - (void)bluetoothStateTimer:(NSTimer *)timer
356 | {
357 | RCTPromiseResolveBlock resolve = [timer userInfo];
358 | int bluetoothState = [[_bleShield CM] state];
359 | BOOL enabled = bluetoothState == CBCentralManagerStatePoweredOn;
360 |
361 | if (enabled) {
362 | resolve((id)kCFBooleanTrue);
363 | } else {
364 | resolve((id)kCFBooleanFalse);
365 | }
366 | }
367 |
368 | #pragma mark - internal implemetation
369 |
370 | - (NSString*)readUntilDelimiter: (NSString*) delimiter
371 | {
372 |
373 | NSRange range = [_buffer rangeOfString: delimiter];
374 | NSString *message = @"";
375 |
376 | if (range.location != NSNotFound) {
377 |
378 | long end = range.location + range.length;
379 | message = [_buffer substringToIndex:end];
380 |
381 | NSRange truncate = NSMakeRange(0, end);
382 | [_buffer deleteCharactersInRange:truncate];
383 | }
384 | return message;
385 | }
386 |
387 | - (NSMutableArray*) getPeripheralList
388 | {
389 |
390 | NSMutableArray *peripherals = [NSMutableArray array];
391 |
392 | for (int i = 0; i < _bleShield.peripherals.count; i++) {
393 | NSMutableDictionary *peripheral = [NSMutableDictionary dictionary];
394 | CBPeripheral *p = [_bleShield.peripherals objectAtIndex:i];
395 |
396 | NSString *uuid = p.identifier.UUIDString;
397 | [peripheral setObject: uuid forKey: @"uuid"];
398 | [peripheral setObject: uuid forKey: @"id"];
399 |
400 | NSString *name = [p name];
401 | if (!name) {
402 | name = [peripheral objectForKey:@"uuid"];
403 | }
404 | [peripheral setObject: name forKey: @"name"];
405 |
406 | NSNumber *rssi = [p btsAdvertisementRSSI];
407 | if (rssi) { // BLEShield doesn't provide advertised RSSI
408 | [peripheral setObject: rssi forKey:@"rssi"];
409 | }
410 |
411 | [peripherals addObject:peripheral];
412 | }
413 |
414 | return peripherals;
415 | }
416 |
417 | // calls the JavaScript subscriber with data if we hit the _delimiter
418 | - (void) sendDataToSubscriber {
419 |
420 | NSString *message = [self readUntilDelimiter:_delimiter];
421 |
422 | if ([message length] > 0) {
423 | [self.bridge.eventDispatcher sendDeviceEventWithName:@"data" body:@{@"data": message}];
424 | }
425 |
426 | }
427 |
428 | // Ideally we'd get a callback when found, maybe _bleShield can be modified
429 | // to callback on centralManager:didRetrievePeripherals. For now, use a timer.
430 | - (void)scanForBLEPeripherals:(int)timeout
431 | {
432 |
433 | NSLog(@"Scanning for BLE Peripherals");
434 |
435 | // disconnect
436 | if (_bleShield.activePeripheral) {
437 | if(_bleShield.activePeripheral.state == CBPeripheralStateConnected)
438 | {
439 | [[_bleShield CM] cancelPeripheralConnection:[_bleShield activePeripheral]];
440 | return;
441 | }
442 | }
443 |
444 | // remove existing peripherals
445 | if (_bleShield.peripherals) {
446 | _bleShield.peripherals = nil;
447 | }
448 |
449 | [_bleShield findBLEPeripherals:timeout];
450 | }
451 |
452 | - (void)connectToFirstDevice
453 | {
454 |
455 | [self scanForBLEPeripherals:3];
456 |
457 | [NSTimer scheduledTimerWithTimeInterval:(float)3.0
458 | target:self
459 | selector:@selector(connectFirstDeviceTimer:)
460 | userInfo:nil
461 | repeats:NO];
462 | }
463 |
464 | - (void)connectToUUID:(NSString *)uuid
465 | {
466 |
467 | int interval = 0;
468 |
469 | if (_bleShield.peripherals.count < 1) {
470 | interval = 3;
471 | [self scanForBLEPeripherals:interval];
472 | }
473 |
474 | [NSTimer scheduledTimerWithTimeInterval:interval
475 | target:self
476 | selector:@selector(connectUuidTimer:)
477 | userInfo:uuid
478 | repeats:NO];
479 | }
480 |
481 | - (CBPeripheral*)findPeripheralByUUID:(NSString*)uuid
482 | {
483 |
484 | NSMutableArray *peripherals = [_bleShield peripherals];
485 | CBPeripheral *peripheral = nil;
486 |
487 | for (CBPeripheral *p in peripherals) {
488 |
489 | NSString *other = p.identifier.UUIDString;
490 |
491 | if ([uuid isEqualToString:other]) {
492 | peripheral = p;
493 | break;
494 | }
495 | }
496 | return peripheral;
497 | }
498 |
499 | @end
500 |
--------------------------------------------------------------------------------
/js/esc.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const Util = require('./util');
3 |
4 | const Common = {
5 | INIT: "1B 40",//初始化
6 |
7 | ALIGN_LEFT: "1B 61 00",//左对齐
8 | ALIGN_RIGHT: "1B 61 02",//居右对齐
9 | ALIGN_CENTER: "1B 61 01",//居中对齐
10 |
11 | UNDER_LINE: "1C 2D 01",//下划线
12 |
13 | PRINT_AND_NEW_LINE: "0A",//打印并换行
14 |
15 | FONT_SMALL: "1B 4D 01",//小号字体 9x17
16 | FONT_NORMAL: "1B 4D 00",//正常 12x24
17 | FONT_BOLD: "1B 45 01",//粗体
18 |
19 | FONT_HEIGHT_TIMES: '1B 21 10',
20 | FONT_WIDTH_TIMES: '1B 21 20',
21 | FONT_HEIGHT_WIDTH_TIMES: '1B 21 30',
22 |
23 | SOUND: "1B 42 02 02" // 蜂鸣 2次/100ms
24 | };
25 |
26 | const Config = {
27 | wordNumber: 48 // 可打印的字数,对应80mm纸张
28 | };
29 |
30 | let writeTextToDevice, writeHexToDevice;
31 |
32 | function _setBT(bt) {
33 | writeTextToDevice = bt.writeTextToDevice;
34 | writeHexToDevice = bt.writeHexToDevice;
35 | }
36 |
37 | function setConfig(config) {
38 | Object.assign(Config, config);
39 | }
40 |
41 | function leftRight(left, right, wordNumber = Config.wordNumber) {
42 | return left + Util.getSpace(wordNumber - Util.getWordsLength(left) - Util.getWordsLength(right)) + right;
43 | }
44 |
45 | function keyValue(name, value, wordNumber = Config.wordNumber) {
46 | const nameLen = Util.getWordsLength(name);
47 | let vArr = [], temp = '';
48 | _.each(value, (v, i) => {
49 | const tvLen = Util.getWordsLength(temp + v);
50 | const diff = tvLen - (wordNumber - nameLen);
51 | if (diff <= 0) {
52 | temp += v;
53 | if (i === value.length - 1) {
54 | vArr.push(temp);
55 | }
56 | } else {
57 | if (Util.isChinese(v) && diff === 1) {
58 | temp += ' ';
59 | }
60 | vArr.push(temp);
61 | temp = v;
62 | }
63 | });
64 | return _.map(vArr, (v, i) => {
65 | if (i === 0) {
66 | return name + v;
67 | } else {
68 | return Util.getSpace(name.length) + v;
69 | }
70 | }).join('');
71 | }
72 |
73 | const ESC = {
74 | Common,
75 | Util: {
76 | leftRight,
77 | keyValue,
78 | },
79 | _setBT,
80 | setConfig,
81 |
82 | init(){
83 | writeHexToDevice(Common.INIT);
84 | },
85 | printAndNewLine(){
86 | writeHexToDevice(Common.PRINT_AND_NEW_LINE);
87 | },
88 | alignLeft(){
89 | writeHexToDevice(Common.ALIGN_LEFT);
90 | },
91 | alignCenter(){
92 | writeHexToDevice(Common.ALIGN_CENTER);
93 | },
94 | alignRight(){
95 | writeHexToDevice(Common.ALIGN_RIGHT);
96 | },
97 |
98 | underline(){
99 | writeHexToDevice(Common.UNDER_LINE);
100 | },
101 |
102 | fontSmall(){
103 | writeHexToDevice(Common.FONT_SMALL);
104 | },
105 | fontNormal(){
106 | writeHexToDevice(Common.FONT_NORMAL);
107 | },
108 | fontBold(){
109 | writeHexToDevice(Common.FONT_BOLD);
110 | },
111 |
112 | fontHeightTimes(){
113 | writeHexToDevice(Common.FONT_HEIGHT_TIMES);
114 | },
115 | fontHeightTimes(){
116 | writeHexToDevice(Common.FONT_WIDTH_TIMES);
117 | },
118 | fontHeightTimes(){
119 | writeHexToDevice(Common.FONT_HEIGHT_WIDTH_TIMES);
120 | },
121 |
122 | text(str){
123 | writeTextToDevice(str)
124 | },
125 |
126 | sound(){
127 | writeHexToDevice(Common.SOUND);
128 | }
129 | };
130 |
131 | module.exports = ESC;
--------------------------------------------------------------------------------
/js/tsc.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const Util = require('./util');
3 |
4 | let writeTextToDevice;
5 |
6 | function _setBT(bt) {
7 | writeTextToDevice = bt.writeTextToDevice;
8 | }
9 |
10 | let Config = {
11 | width: 60,
12 | height: 40,
13 | gap: 2,
14 | direction: 0,
15 | referenceX: 30,
16 | referenceY: 30
17 | };
18 |
19 | const TSC = {
20 | _setBT,
21 | config(con){
22 | Object.assign(Config, con);
23 | },
24 | size(width, height){
25 | writeTextToDevice(`SIZE ${width} mm, ${height} mm\r\n`);
26 | },
27 | gap(g){
28 | writeTextToDevice(`GAP ${g} mm\r\n`);
29 | },
30 | direction(dir = 0){
31 | writeTextToDevice(`DIRECTION ${dir}\r\n`);
32 | },
33 | reference(x = 0, y = 0){
34 | writeTextToDevice(`REFERENCE ${x},${y}\r\n`);
35 | },
36 | cls(){
37 | writeTextToDevice(`CLS \r\n`);
38 | },
39 |
40 | init(){
41 | TSC.size(Config.width, Config.height);
42 | TSC.gap(Config.gap);
43 | TSC.direction(Config.direction);
44 | TSC.reference(Config.referenceX, Config.referenceY);
45 | TSC.cls();
46 | },
47 |
48 | text(x, y, text, x_times = 1, y_times = 1){
49 | writeTextToDevice(`TEXT ${x},${y},"TSS24.BF2", 0,${x_times},${y_times},"${text}"`)
50 | },
51 | print(times = 1){
52 | writeTextToDevice(`PRINT ${times}\r\n`);
53 | },
54 | sound(times = 2, time = 100){
55 | writeTextToDevice(`SOUND ${times},${time}\r\n`);
56 | },
57 | drawBox(x_start,y_start,x_end,y_end,line_thickness){
58 | writeTextToDevice(`BOX ${x_start},${y_start},${x_end},${y_end},${line_thickness}\r\n`);
59 | },
60 | drawBar(x,y,width,height){
61 | writeTextToDevice(`BAR ${x},${y},${width},${height}\r\n`);
62 | },
63 | drawQrcode(x,y,ecc_level,cell_width,mode,rotation,qrcode_content){
64 | writeTextToDevice(`QRCODE ${x},${y},${ecc_level},${cell_width},${mode},${rotation},"${qrcode_content}"\r\n`)
65 | }
66 | }
67 |
68 | module.exports = TSC;
--------------------------------------------------------------------------------
/js/util.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 |
3 | function isChinese(word) {
4 | const charCode = word.charCodeAt(0);
5 | return !(charCode >= 0 && charCode <= 128)
6 | }
7 |
8 | function getWordLength(word) {
9 | return isChinese(word) ? 2 : 1;
10 | }
11 |
12 | function getWordsLength(words) {
13 | return _.reduce(words, (m, v) => m + getWordLength(v), 0);
14 | }
15 |
16 | function getSpace(len) {
17 | return _.times(len, () => ' ').join('');
18 | }
19 |
20 | module.exports = {
21 | isChinese,
22 | getWordsLength,
23 | getWordLength,
24 | getSpace
25 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-gm-bluetooth",
3 | "version": "0.39.7",
4 | "description": "react-native-gm-bluetooth",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/gmfe/react-native-gm-bluetooth.git"
12 | },
13 | "keywords": [
14 | "bluetooth",
15 | "react-native",
16 | "weight",
17 | "print"
18 | ],
19 | "author": "",
20 | "license": "ISC",
21 | "bugs": {
22 | "url": "https://github.com/gmfe/react-native-gm-bluetooth/issues"
23 | },
24 | "homepage": "https://github.com/gmfe/react-native-gm-bluetooth#readme",
25 | "dependencies": {
26 | "buffer": "^4.6.0"
27 | },
28 | "peerDependencies": {
29 | "react-native": ">=0.40",
30 | "lodash": "^4.17.4"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------