From c6a433e59489ff8f578b194855ca40b4125efcec Mon Sep 17 00:00:00 2001 From: Hom-Wang Date: Fri, 15 May 2020 20:43:25 +0800 Subject: [PATCH] initial --- .gitignore | 14 + app/.gitignore | 1 + app/build.gradle | 56 ++ app/proguard-rules.pro | 21 + app/src/main/AndroidManifest.xml | 24 + .../java/com/kitsprout/ks/KBluetooth.java | 250 +++++++ .../main/java/com/kitsprout/ks/KSerial.java | 260 +++++++ .../java/com/kitsprout/main/MainActivity.java | 181 +++++ app/src/main/jni/CMakeLists.txt | 46 ++ app/src/main/jni/kSerial.c | 632 ++++++++++++++++++ app/src/main/jni/kSerial.h | 113 ++++ app/src/main/jni/kSerialJNI.cpp | 286 ++++++++ app/src/main/jni/kStatus.h | 74 ++ .../drawable-v24/ic_launcher_foreground.xml | 30 + .../res/drawable/ic_launcher_background.xml | 170 +++++ app/src/main/res/drawable/shape.xml | 17 + app/src/main/res/layout/activity_main.xml | 98 +++ app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 7032 bytes app/src/main/res/mipmap-ldpi/ic_launcher.png | Bin 0 -> 2792 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 4185 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 9785 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 15485 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 21162 bytes app/src/main/res/values/colors.xml | 6 + app/src/main/res/values/strings.xml | 4 + app/src/main/res/values/styles.xml | 10 + build.gradle | 29 + gradle.properties | 20 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 +++++ gradlew.bat | 84 +++ settings.gradle | 2 + 33 files changed, 2606 insertions(+) create mode 100644 .gitignore create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/kitsprout/ks/KBluetooth.java create mode 100644 app/src/main/java/com/kitsprout/ks/KSerial.java create mode 100644 app/src/main/java/com/kitsprout/main/MainActivity.java create mode 100644 app/src/main/jni/CMakeLists.txt create mode 100644 app/src/main/jni/kSerial.c create mode 100644 app/src/main/jni/kSerial.h create mode 100644 app/src/main/jni/kSerialJNI.cpp create mode 100644 app/src/main/jni/kStatus.h create mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/drawable/shape.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-ldpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..603b140 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..36ae69d --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,56 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.3" + + defaultConfig { + applicationId "com.kitsprout.main" + minSdkVersion 24 + targetSdkVersion 29 + versionCode 2 + versionName "1.1" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + externalNativeBuild { + cmake { + cppFlags "" + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + android.applicationVariants.all { variant -> + variant.outputs.all { + def project = 'kSerialBLE' + def SEP = '-' + def version = variant.versionName + outputFileName = project + SEP + version + ".apk" + } + } + + externalNativeBuild { + cmake { + path "src/main/jni/CMakeLists.txt" + version "3.10.2" + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + implementation 'com.google.android.material:material:1.1.0' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3f627d2 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/kitsprout/ks/KBluetooth.java b/app/src/main/java/com/kitsprout/ks/KBluetooth.java new file mode 100644 index 0000000..cd6b62b --- /dev/null +++ b/app/src/main/java/com/kitsprout/ks/KBluetooth.java @@ -0,0 +1,250 @@ +package com.kitsprout.ks; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSocket; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.util.Log; +import android.view.Menu; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Set; +import java.util.UUID; + +public class KBluetooth { + + private static final String TAG = "KSBLE"; + private static final UUID btUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); + + private static boolean connectStatus; + private static BluetoothAdapter adapter; + private static BluetoothDevice[] deviceList; + private static BluetoothSocket socket; + private static InputStream inputStream; + private static OutputStream outputStream; + + public static BluetoothDevice device; + + public static boolean startup(Context context) { + connectStatus = false; + + Log.d(TAG, "kBluetooth startup ..."); + + // get the bluetooth adapter + adapter = getAdapter(); + if (adapter == null) { + Log.e(TAG, " getAdapter() ... error"); + return false; + } + Log.d(TAG, " getAdapter() ... ok"); + + // get bluetooth paired devices + deviceList = getPairedDevice(adapter); + Log.d(TAG, " getPairedDevice() ... ok"); + // select print device list + for (int i = 0; i < deviceList.length; i++) { + Log.d(TAG, " [" + (i+1) + "] " + + deviceList[i].getName() + + " (" + deviceList[i].getAddress() + ")"); + } + + // listening bluetooth broadcasts + BroadcastReceiver btReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) { + connectStatus = true; + Log.d(TAG, "onReceive() ... connected"); + } else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) { + if (connectStatus) { + connectStatus = false; + disconnect(); + } + Log.d(TAG, "onReceive() ... disconnected"); + } + } + }; + IntentFilter btConnectFilter = new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED); + IntentFilter btDisconnectFilter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED); + context.registerReceiver(btReceiver, btConnectFilter); + context.registerReceiver(btReceiver, btDisconnectFilter); + + return true; + } + + private static BluetoothAdapter getAdapter() { +// Log.d(TAG, "getAdapter()"); + BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); + if (btAdapter == null) { + Log.e(TAG, "device doesn't support bluetooth"); + return null; + } + return btAdapter; + } + + private static BluetoothDevice[] getPairedDevice(BluetoothAdapter btAdapter) { +// Log.d(TAG, "getPairedDevice()"); + BluetoothDevice[] btDeviceList = null; + Set btDevicesList = btAdapter.getBondedDevices(); + if (btDevicesList.size() > 0) { + int cnt = 0; + btDeviceList = new BluetoothDevice[btDevicesList.size()]; + for (BluetoothDevice device : btDevicesList) { + btDeviceList[cnt++] = device; + } + } + return btDeviceList; + } + + private static BluetoothSocket getSocket(BluetoothDevice device) { +// Log.d(TAG, "getSocket()"); + BluetoothSocket btSocket = null; + try { + btSocket = device.createRfcommSocketToServiceRecord(btUUID); + } catch (IOException e) { + Log.e(TAG, "getSocket() method failed", e); + } + return btSocket; + } + + private static BluetoothDevice getDevice(BluetoothAdapter btAdapter, String address) { +// Log.d(TAG, "getDevice()"); + return btAdapter.getRemoteDevice(address); + } + + public static String[] getDeviceName() { +// Log.d(TAG, "getDeviceName()"); + String[] deviceName = new String[deviceList.length]; + for (int i = 0; i < deviceList.length; i++) { + String name = deviceList[i].getName(); + if (deviceList[i].getName().length() > 13) { + name = name.substring(0, 10) + "..."; + } + deviceName[i] = String.format("%s (%s)", name, deviceList[i].getAddress()); + } + return deviceName; + } + + public static void updatePairedDeviceList() { +// Log.d(TAG, "updatePairedDevice()"); + deviceList = getPairedDevice(adapter); + } + + public static boolean connect(String address) { + Log.d(TAG, String.format("connect() ... mac: %s", address)); + socket = getSocket(getDevice(adapter, address)); + if (socket == null) { + return false; + } + adapter.cancelDiscovery(); + try { + // Connect to the remote device through the socket + socket.connect(); + inputStream = socket.getInputStream(); + outputStream = socket.getOutputStream(); + } catch (IOException connectException) { + // Unable to connect; close the socket and return + try { + socket.close(); + Log.d(TAG, "connect() ... failed"); + } catch (IOException e) { + Log.e(TAG, "connect() ... failed ... could not open the client socket", e); + } + return false; + } + return true; + } + + public static boolean isConnected() { + return connectStatus; + } + + public static boolean disconnect() { + Log.d(TAG, String.format("disconnect() ... mac: %s", device.getAddress())); + try { + socket.close(); + } catch (IOException e) { + Log.e(TAG, "disconnect() ... failed ... could not close the client socket", e); + return false; + } + return true; + } + + public static final int DISCONNECT_FAILED = -2; // disconnect + public static final int DISCONNECTED = -1; // disconnect + public static final int UNCHANE = 0; // do nothing + public static final int CONNECTED = 1; // connect + public static final int CONNECT_FAILED = 2; // disconnect + public static int select(int index) { + int state = UNCHANE; + if ((index > 0) && (index <= deviceList.length)) { + // connect + device = deviceList[index - 1]; + if (!connectStatus) { + connectStatus = connect(device.getAddress()); + if (connectStatus) { + state = CONNECTED; + } else { + state = CONNECT_FAILED; + } + } else { + state = UNCHANE; + } + } else { + // disconnect + if (connectStatus) { + connectStatus = false; + if (disconnect()) { + state = DISCONNECTED; + } else { + state = DISCONNECT_FAILED; + } + } + } + return state; + } + + public static int send(byte[] data) { + Log.d(TAG, "send()"); + try { + outputStream.write(data); + } catch (IOException e) { + Log.e(TAG, "OutputStream write() method failed", e); + } + return data.length; + } + + public static byte[] receive() { + try { + int bytesAvailable = inputStream.available(); + if (bytesAvailable > 0) { + byte[] receiveBytes = new byte[bytesAvailable]; + if (inputStream.read(receiveBytes) != -1) { + return receiveBytes; + } + } + } catch (IOException e) { + Log.e(TAG, "InputStream read() method failed", e); + return null; + } + return new byte[0]; + } + + public static Menu updateDeviceMenu(Menu menu) { + updatePairedDeviceList(); + menu.clear(); + menu.add(0, 0, 0, "Disconnect"); + String[] deviceName = getDeviceName(); + for (int i = 1; i <= deviceName.length; i++) { + menu.add(0, i, i, deviceName[i - 1]); + } + return menu; + } + +} diff --git a/app/src/main/java/com/kitsprout/ks/KSerial.java b/app/src/main/java/com/kitsprout/ks/KSerial.java new file mode 100644 index 0000000..2fed698 --- /dev/null +++ b/app/src/main/java/com/kitsprout/ks/KSerial.java @@ -0,0 +1,260 @@ +package com.kitsprout.ks; + +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +public class KSerial { + + // form JNI + static { + System.loadLibrary("libkserial"); + } + private static native byte[] pack(int[] param, int type, int lens, double[] data); + private static native int unpackBuffer(byte[] buf, int lens); + private static native int getPacketCount(); + private static native int getPacketType(int index); + private static native int getPacketBytes(int index); + private static native int[] getPacketParam(int index); + private static native double[] getPacketData(int index); + + // log tag + private static final String TAG = "KSERIAL"; + + // default maximum receive buffer size + private static final int maxPacketBufferSize = 8 * 1024; // 8 kB + + // receive packet buffer and index + private int packetBufferIndex; + private byte[] packetBuffer; + + // maximum record buffer size + private int saveMaxBufferSize; + private ArrayList pklist; + + private double packetTimeUnit; + private long packetTotalCount; + private int parameter16; + + // timestamp and packet frequency + private boolean timestampInited; + private boolean enablePacketTime; + private long[] firstTimestamp; + private long[] lastTimestamp; + private long systemTimestampCount; + private long systemLastTimestamp; + private double frequency; + private double frequencyFiltered; + + // lost rate + private boolean enableLostRate; + private long lostCount; + + public static class KPacket { + public int type; + public int nbyte; + public int[] param; + public double[] data; + } + + public KSerial(int saveMaxBufferSize, double timeUnit) { + this.saveMaxBufferSize = saveMaxBufferSize; + this.pklist = new ArrayList(); + this.packetTotalCount = 0; + this.timestampInited = false; + this.firstTimestamp = new long[2]; + this.lastTimestamp = new long[2]; + this.resetPacketBuffer(); + this.setTimeUnit(timeUnit); + } + + private static final String[] typeNum2str = { + "uint8", "uint16", "uint32", "uint64", + "int8", "int16", "int32", "int64", + "R0", "half", "float", "double", + "R1", "R2", "R3", "R4", + }; + public static String typeConvert(int type) { + if ((type < 0) || (type > 15)) { + return ""; + } + return typeNum2str[type]; + } + public static int typeConvert(String type) { + return 0; + } + private static int typeSize(int type) { + return 0; + } + private static int typeSize(String type) { + return 0; + } + + private void resetPacketBuffer() { + packetBufferIndex = 0; + packetBuffer = new byte[maxPacketBufferSize]; + } + + private void setTimeUnit(double timeUnit) { + if (timeUnit > 0) { + // use packet time + enablePacketTime = true; + packetTimeUnit = timeUnit; + } else { + // use system time + enablePacketTime = false; + packetTimeUnit = 0; + } + } + + private long getPacketTimestamp(KPacket packet) { + return (long)(packet.data[0] * 1000 + packet.data[1]); + } + + public int getPacketParameterU16(KPacket packet) { + parameter16 = (packet.param[1] & 0x0000FFFF) * 256 | packet.param[0]; + return parameter16; + } + + private long getPacketLost(KPacket[] packets) { + long count = 0; + for (KPacket packet : packets) { + int lastCount = parameter16; + int differenceCount = getPacketParameterU16(packet) - lastCount; + if ((differenceCount != 1) && (differenceCount != -65535)) { + count++; + } + } + return count; + } + + private double getPacketFrequency(KPacket lastPacket, int count) { + if (!timestampInited) { + timestampInited = true; + firstTimestamp[0] = System.currentTimeMillis(); + lastTimestamp[0] = firstTimestamp[0]; + if (enablePacketTime) { + firstTimestamp[1] = getPacketTimestamp(lastPacket); + lastTimestamp[1] = firstTimestamp[1]; + } + frequency = 0; + } else { + packetTotalCount = packetTotalCount + count; + long systemTimestamp = System.currentTimeMillis(); + if (enablePacketTime) { + long packetTimestamp = getPacketTimestamp(lastPacket); + double dt = (packetTimestamp - lastTimestamp[1]) * packetTimeUnit; + lastTimestamp[1] = packetTimestamp; + frequency = count / dt; + } else { + double dt = (systemTimestamp - systemLastTimestamp) * 0.001; + systemTimestampCount = systemTimestampCount + count; + if (dt > 1.2) { + frequency = systemTimestampCount / dt; + systemTimestampCount = 0; + systemLastTimestamp = systemTimestamp; + } + } + lastTimestamp[0] = systemTimestamp; + } + return frequency; + } + + public byte[] setPacket(KPacket[] packets) { +// int byteCount = 0; +// List packetList = new ArrayList(); +// for (KPacket packet : packets) { +// byte[] packBytes = pack(packet.param, packet.type, packet.data.length, packet.data); +// packetList.add(packBytes); +// byteCount += packBytes.length; +// } +// return packetList.toArray(); + return new byte[0]; + } + + public byte[] setPacket(KPacket packets) { + return pack(packets.param, packets.type, packets.data.length, packets.data); + } + + public KPacket[] getPacket(byte[] receiveBytes) { + // update packet buffer + System.arraycopy(receiveBytes, 0, packetBuffer, packetBufferIndex, receiveBytes.length); + packetBufferIndex += receiveBytes.length; + // unpack receive buffer + int newPacketBufferIndex = unpackBuffer(packetBuffer, packetBufferIndex); + int packetCount = getPacketCount(); + // receive packet + KPacket[] pk = new KPacket[packetCount]; + if (packetCount > 0) { + for (int i = 0; i < packetCount; i++) { + // get packet info and data form jni + KPacket packet = new KPacket(); + packet.type = getPacketType(i); + packet.nbyte = getPacketBytes(i); + packet.param = getPacketParam(i); + packet.data = getPacketData(i); + // record packet + if (saveMaxBufferSize > 0) { + if (pklist.size() >= saveMaxBufferSize) { + pklist.remove(0); // remove oldest packet + } + pklist.add(packet); + } + pk[i] = packet; + } + // get frequency + frequency = getPacketFrequency(pk[packetCount-1], packetCount); + // get lostCount + if (enableLostRate) { + lostCount += getPacketLost(pk); + } + // remove unpack data from receive buffer + packetBufferIndex -= newPacketBufferIndex; + System.arraycopy(packetBuffer, newPacketBufferIndex, packetBuffer, 0, packetBufferIndex); +// Log.d(TAG, String.format("time=%.2f, freq=%.2f, packetTotalCount=%d, pklist.size()=%d", getTimes(), getFrequency(0), packetTotalCount, pklist.size())); + } + + return pk; + } + + public double getFrequency(double weighting) { + if (weighting > 0) { + frequencyFiltered = (1 - weighting) * frequencyFiltered + weighting * frequency; + } else { + frequencyFiltered = frequency; + } + return frequencyFiltered; + } + + public double getTimes() { + if (enablePacketTime) { + return (lastTimestamp[1] - firstTimestamp[1]) * packetTimeUnit; + } else { + return (lastTimestamp[0] - firstTimestamp[0]) * 0.001; + } + } + + // need to set parameter byte to counter (16-bit) + public void enableLostRateDetection(boolean cmd) { + enableLostRate = cmd; + lostCount -= 1; + } + + public long getLostCount() { + return lostCount; + } + + public long getPacketTotalCount() { + return packetTotalCount; + } + + public long getSavePacketCount() { + return pklist.size(); + } + + public ArrayList getSavePacketBuffer() { + return pklist; + } + +} diff --git a/app/src/main/java/com/kitsprout/main/MainActivity.java b/app/src/main/java/com/kitsprout/main/MainActivity.java new file mode 100644 index 0000000..90ee9e9 --- /dev/null +++ b/app/src/main/java/com/kitsprout/main/MainActivity.java @@ -0,0 +1,181 @@ +package com.kitsprout.main; + +import androidx.appcompat.app.AppCompatActivity; + +import android.graphics.Color; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import com.kitsprout.ks.KBluetooth; +import com.kitsprout.ks.KSerial; +import com.kitsprout.ks.KSerial.KPacket; + +import java.util.Locale; + +public class MainActivity extends AppCompatActivity { + + private Menu bluetoothDeviceMenu = null; + + private TextView bluetoothRecvText; + private TextView bluetoothRecvBufferText; + private EditText bluetoothSendText; + private Button bluetoothSendButton; + private Button bluetoothConnectStatus; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + // layout + bluetoothRecvText = findViewById(R.id.textViewBluetoothRecv); + bluetoothRecvBufferText = findViewById(R.id.textViewBluetoothRecvBuffer); + bluetoothSendText = findViewById(R.id.editTextBluetoothSend); + bluetoothSendButton = findViewById(R.id.buttonBluetoothSend); + bluetoothConnectStatus = findViewById(R.id.buttonBluetoothConnectStatus); + changeConnectStatusColor(false); + + // bluetooth startup + if (!KBluetooth.startup(this)) { + Log.e("KS_DBG", "bluetoothStartup ... error"); + } + bluetoothRecvText.setText(getPacketString(0, 0, 0, 0, 0, new KPacket[0])); + + // keep screen on + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + bluetoothDeviceMenu = menu; + return super.onCreateOptionsMenu(bluetoothDeviceMenu); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + bluetoothDeviceMenu = KBluetooth.updateDeviceMenu(menu); + return super.onPrepareOptionsMenu(bluetoothDeviceMenu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + int state = KBluetooth.select(id); + if (state == KBluetooth.DISCONNECTED) { +// bluetoothRecvBufferText.setText(""); + changeConnectStatusColor(false); + Toast.makeText(this, "DISCONNECT SUCCESS", Toast.LENGTH_SHORT).show(); + } else if (state == KBluetooth.DISCONNECT_FAILED) { + Toast.makeText(this, "DISCONNECT FAILED", Toast.LENGTH_SHORT).show(); + } else if (state == KBluetooth.CONNECT_FAILED) { + Toast.makeText(this, "CONNECT FAILED", Toast.LENGTH_SHORT).show(); + } else if (state == KBluetooth.CONNECTED) { + bluetoothBeginListening(); + Toast.makeText(this, "CONNECT SUCCESS", Toast.LENGTH_SHORT).show(); + } + return super.onOptionsItemSelected(item); + } + + private void changeConnectStatusColor(boolean enable) { + if (enable) { + bluetoothSendButton.setTextColor(Color.BLACK); + bluetoothConnectStatus.setBackgroundColor(Color.RED); + } else { + bluetoothSendButton.setTextColor(Color.GRAY); + bluetoothConnectStatus.setBackgroundColor(Color.parseColor("#DCDCDC")); + } + } + + public void OnClickBluetoothSendData(View view) { + if (KBluetooth.isConnected()) { + int lens = KBluetooth.send(bluetoothSendText.getText().toString().getBytes()); + Log.d("KS_DBG", String.format("OnClickBluetoothSendData() ... lens = %d", lens)); + } else { + Log.d("KS_DBG", "OnClickBluetoothSendData() ... without connect"); + } + } + + KSerial ks; + Thread bluetoothRecvThread; + void bluetoothBeginListening() { + Log.d("KS_DBG", "bluetoothBeginListening()"); + final Handler handler = new Handler(); + ks = new KSerial(32*1024, 0.001); + ks.enableLostRateDetection(true); + bluetoothRecvThread = new Thread(new Runnable() { + public void run() { + changeConnectStatusColor(true); + while (!Thread.currentThread().isInterrupted() && KBluetooth.isConnected()) { + byte[] receiveBytes = KBluetooth.receive(); + if (receiveBytes != null) { + int bytesAvailable = receiveBytes.length; + if (bytesAvailable > 0) { + KPacket[] pk = ks.getPacket(receiveBytes); + for (KSerial.KPacket KPacket : pk) { + Log.d("KSERIAL", String.format("%6d,%.0f,%d,%d", + ks.getPacketParameterU16(KPacket), ks.getFrequency(0), bytesAvailable, pk.length)); + } + if (pk.length > 0) { + // show information + final String recvByteString = getPacketHexString(ks.setPacket(pk[pk.length-1])); + final String recvBufferString = getPacketString(ks.getFrequency(0), ks.getTimes(), bytesAvailable, ks.getLostCount(), ks.getPacketTotalCount(), pk); + handler.post(new Runnable() { + public void run() { + bluetoothRecvText.setText(recvBufferString); + bluetoothRecvBufferText.setText(recvByteString); + } + }); + } + } + } else { + KBluetooth.disconnect(); + break; + } + } + changeConnectStatusColor(false); + } + }); + bluetoothRecvThread.start(); + } + + private String getPacketHexString(byte[] buf) { + StringBuilder logString; + logString = new StringBuilder(""); + for (byte b : buf) { + logString.append(String.format(Locale.ENGLISH, " %02X", b)); + } + return logString.toString(); + } + + private String getPacketString(double freq, double time, int bytesAvailable, long lostCount, long bytesTotal, KPacket[] packet) { + int idx = packet.length - 1; + StringBuilder logString; + logString = new StringBuilder(String.format(Locale.ENGLISH, "Freq: %.2f Hz\n", freq)); + logString.append(String.format(Locale.ENGLISH, "Time: %.3f sec\n", time)); + logString.append(String.format(Locale.ENGLISH, "Recv: %d\n", bytesAvailable)); + logString.append(String.format(Locale.ENGLISH, "Lost: %d\n", lostCount)); + if (idx < 0) { + return logString.toString(); + } + logString.append("\n"); + logString.append(String.format(Locale.ENGLISH, "Total: %d (%d)\n", bytesTotal, packet.length)); + logString.append(String.format(Locale.ENGLISH, "Type: %s\n", KSerial.typeConvert(packet[idx].type))); + logString.append(String.format(Locale.ENGLISH, "Lens: %d (%d bytes)\n", packet[idx].data.length, packet[idx].nbyte)); + logString.append(String.format(Locale.ENGLISH, "Param: %02X, %02X\n", packet[idx].param[0], packet[idx].param[1])); + logString.append("\n"); + for (int i = 0; i < packet[idx].data.length; i++) { + logString.append(String.format(Locale.ENGLISH, "Data[%d]: %.0f\n", i, packet[idx].data[i])); + } + return logString.toString(); + } + +} diff --git a/app/src/main/jni/CMakeLists.txt b/app/src/main/jni/CMakeLists.txt new file mode 100644 index 0000000..8074c46 --- /dev/null +++ b/app/src/main/jni/CMakeLists.txt @@ -0,0 +1,46 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html + +# Sets the minimum version of CMake required to build the native library. + +cmake_minimum_required(VERSION 3.4.1) + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. + +add_library( + # Sets the name of the library. + libkserial + # Sets the library as a shared library. + SHARED + # Provides a relative path to your source file(s). + kSerial.c kSerialJNI.cpp +) + +# Searches for a specified prebuilt library and stores the path as a +# variable. Because CMake includes system libraries in the search path by +# default, you only need to specify the name of the public NDK library +# you want to add. CMake verifies that the library exists before +# completing its build. + +find_library( + # Sets the name of the path variable. + log-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + log +) + +# Specifies libraries CMake should link to your target library. You +# can link multiple libraries, such as libraries you define in this +# build script, prebuilt third-party libraries, or system libraries. + +target_link_libraries( + # Specifies the target library. + libkserial + # Links the target library to the log library + # included in the NDK. + ${log-lib} +) diff --git a/app/src/main/jni/kSerial.c b/app/src/main/jni/kSerial.c new file mode 100644 index 0000000..cadedcd --- /dev/null +++ b/app/src/main/jni/kSerial.c @@ -0,0 +1,632 @@ +/** + * __ ____ + * / /__ _ __ / __/ __ + * / //_/(_)/ /_ / / ___ ____ ___ __ __ / /_ + * / ,< / // __/_\ \ / _ \ / __// _ \/ // // __/ + * /_/|_|/_/ \__//___// .__//_/ \___/\_,_/ \__/ + * /_/ github.com/KitSprout + * + * @file kSerial.c + * @author KitSprout + * @date Mar-2020 + * @brief kSerial packet format : + * byte 1 : header 'K' (75) [HK] + * byte 2 : header 'S' (83) [HS] + * byte 3 : data bytes (12-bit) [L ] + * byte 4 : data type [T ] + * byte 5 : parameter 1 [P1] + * byte 6 : parameter 2 [P2] + * byte 7 : checksum [CK] + * ... + * byte L-1 : data [DN] + * byte L : finish '\r' (13) [ER] + */ + +/* Includes --------------------------------------------------------------------------------*/ +#include "kSerial.h" +#if KSERIAL_SEND_ENABLE || KSERIAL_RECV_ENABLE +#include +#include +#include "serial.h" +#endif + +/* Define ----------------------------------------------------------------------------------*/ +/* Macro -----------------------------------------------------------------------------------*/ + +#if KSERIAL_SEND_ENABLE +#ifndef kSerial_Send +#define kSerial_Send(__DATA, __LENS) Serial_SendData(&s, __DATA, __LENS) +#endif +#endif +#if KSERIAL_RECV_ENABLE +#define kSerial_Recv(__DATA, __LENS) Serial_RecvData(&s, __DATA, __LENS) +#define kSerial_RecvByte() Serial_RecvByte(&s) +#define kSerial_RecvFlush() Serial_Flush(&s) +#endif +#if KSERIAL_SEND_ENABLE || KSERIAL_RECV_ENABLE +#define kSerial_Delay(__MS) Serial_Delay(__MS) +#endif + +/* Typedef ---------------------------------------------------------------------------------*/ +/* Variables -------------------------------------------------------------------------------*/ + +#if KSERIAL_SEND_ENABLE +uint8_t ksSendBuff[KS_MAX_SEND_BUFF_SIZE] = {0}; +#endif +#if KSERIAL_RECV_ENABLE +uint8_t ksRecvBuff[KS_MAX_RECV_BUFF_SIZE] = {0}; +#endif + +/* Prototypes ------------------------------------------------------------------------------*/ +/* Functions -------------------------------------------------------------------------------*/ + +/** + * @brief kSerial_GetTypeSize + */ +uint32_t kSerial_GetTypeSize( uint32_t type ) +{ + type &= 0x0F; + if ((type > KS_F64) || (type == KS_R0)) + { + return (0); + } + else + { + return (1 << (type & 0x03)); + } +} + +/** + * @brief kSerial_CheckHeader + */ +uint32_t kSerial_CheckHeader( const uint8_t *packet, void *param, uint32_t *type, uint32_t *nbyte ) +{ + uint32_t checksum = 0; + + if ((packet[0] == 'K') && (packet[1] == 'S')) + { + *type = packet[3] & 0x0F; + *nbyte = (((uint32_t)packet[3] << 4) & 0x0F00) | packet[2]; + for (uint32_t i = 2; i < 6; i++) + { + checksum += packet[i]; + } + checksum &= 0xFF; + if (packet[6] == checksum) + { + ((uint8_t*)param)[0] = packet[4]; + ((uint8_t*)param)[1] = packet[5]; + return KS_OK; + } + } + + return KS_ERROR; +} + +/** + * @brief kSerial_CheckEnd + */ +uint32_t kSerial_CheckEnd( const uint8_t *packet, const uint32_t nbyte ) +{ + if (packet[nbyte + 8 - 1] == '\r') + { + return KS_OK; + } + + return KS_ERROR; +} + +/** + * @brief kSerial_Check + */ +uint32_t kSerial_Check( const uint8_t *packet, const uint32_t bufsize, void *param, uint32_t *type, uint32_t *nbyte ) +{ + uint8_t checksum = 0; + + if ((packet[0] == 'K') && (packet[1] == 'S')) + { + *type = packet[3] & (uint8_t)0x0F; + *nbyte = (((uint32_t)packet[3] << 4) & 0x0F00) | packet[2]; + checksum = packet[2] + packet[3] + packet[4] + packet[5]; + if (checksum == packet[6]) + { + uint32_t lastidx = *nbyte + 8 - 1; + if ((lastidx < bufsize) && (packet[lastidx] == '\r')) + { + ((uint8_t*)param)[0] = packet[4]; + ((uint8_t*)param)[1] = packet[5]; + return KS_OK; + } + } + } + + return KS_ERROR; +} + +/** + * @brief kSerial_GetBytesData + */ +void kSerial_GetBytesData( const uint8_t *packet, void *pdata, const uint32_t nbyte ) +{ + for (uint32_t i = 0; i < nbyte; i++) + { + ((uint8_t*)pdata)[i] = packet[7 + i]; + } +} + +/** + * @brief kSerial_Pack + */ +uint32_t kSerial_Pack( uint8_t *packet, const void *param, const uint32_t type, const uint32_t lens, const void *pdata ) +{ + uint32_t lensHiBit; + uint32_t packetDataBytes; // in bytes + uint32_t checksum = 0; + uint32_t typeSize = kSerial_GetTypeSize(type); + + packetDataBytes = (typeSize > 1) ? (lens * typeSize) : (lens); + lensHiBit = (packetDataBytes & 0x0F00) >> 4; + + packet[0] = 'K'; /* header 'K' */ + packet[1] = 'S'; /* header 'S' */ + packet[2] = packetDataBytes; /* data bytes */ + packet[3] = lensHiBit | (type & 0x0F); /* data type */ + + if (param != NULL) + { + packet[4] = ((uint8_t*)param)[0]; /* parameter 1 */ + packet[5] = ((uint8_t*)param)[1]; /* parameter 2 */ + } + else + { + packet[4] = 0; + packet[5] = 0; + } + for (uint32_t i = 2; i < 6; i++) + { + checksum += packet[i]; + } + packet[6] = checksum; /* checksum */ + + for (uint32_t i = 0; i < packetDataBytes; i++) + { + packet[7 + i] = ((uint8_t*)pdata)[i]; /* data ...... */ + } + packet[7 + packetDataBytes] = '\r'; /* finish '\r' */ + + return (packetDataBytes + 8); +} + +/** + * @brief kSerial_Unpack + */ +uint32_t kSerial_Unpack( const uint8_t *packet, const uint32_t bufsize, void *param, uint32_t *type, uint32_t *nbyte, void *pdata ) +{ + uint32_t status; + + status = kSerial_Check(packet, bufsize, param, type, nbyte); + if (status == KS_OK) + { + for (uint32_t i = 0; i < *nbyte; i++) + { + ((uint8_t*)pdata)[i] = packet[7 + i]; + } + } + + return status; +} + +/** + * @brief kSerial_UnpackBuffer + */ +#include +uint32_t kSerial_UnpackBuffer( const uint8_t *buffer, const uint32_t buffersize, kserial_packet_t *packet, uint32_t *packetcnt ) +{ + uint32_t offset = 0; + uint32_t newindex = 0; + uint32_t typeSize; + + *packetcnt = 0; + while ((buffersize - offset) > 7) // min packet bytes = 8 + { + uint8_t param[2] = {0}; + uint32_t type = 0; + uint32_t nbyte = 0; + uint32_t status = kSerial_Check(&buffer[offset], buffersize - offset, param, &type, &nbyte); + if (status == KS_OK) + { + packet[*packetcnt].param[0] = param[0]; + packet[*packetcnt].param[1] = param[1]; + packet[*packetcnt].type = type; + packet[*packetcnt].nbyte = nbyte; + typeSize = kSerial_GetTypeSize(type); + if (typeSize != 0) + { + packet[*packetcnt].lens = packet[*packetcnt].nbyte / typeSize; + } + packet[*packetcnt].data = (void *)calloc(packet[*packetcnt].nbyte, sizeof(uint8_t)); + kSerial_GetBytesData(&buffer[offset], packet[*packetcnt].data, packet[*packetcnt].nbyte); + offset += nbyte + 8; + newindex = offset; + (*packetcnt)++; + } + else + { + offset++; + } + } + + return newindex; +} + +/** + * @brief kSerial_SendPacket + */ +uint32_t kSerial_SendPacket( void *param, void *sdata, const uint32_t lens, const uint32_t type ) +{ +#if KSERIAL_SEND_ENABLE + uint32_t nbytes; + nbytes = kSerial_Pack(ksSendBuff, param, type, lens, sdata); + kSerial_Send(ksSendBuff, nbytes); + // TODO: fix return + return nbytes; +#else + return KS_ERROR; +#endif +} + +/** + * @brief kSerial_RecvPacket + */ +uint32_t kSerial_RecvPacket( void *param, void *rdata, uint32_t *lens, uint32_t *type ) +{ +#if KSERIAL_RECV_ENABLE + static uint32_t index = 0; + static uint32_t bytes = 0; + static uint32_t point = 0; + + uint32_t state; + uint32_t typeSize; + + ksRecvBuff[point] = kSerial_RecvByte(); + if (point > 6) + { + if ((ksRecvBuff[point - 7] == 'K') && (ksRecvBuff[point - 6] == 'S')) + { + index = point - 7; + bytes = ((((uint32_t)ksRecvBuff[index + 3] << 4) & 0x0F00) | ksRecvBuff[index + 2]) + 8; + } + if ((point - index + 1) == bytes) + { + state = kSerial_Unpack(&ksRecvBuff[index], param, type, lens, rdata); + if (state == KS_OK) + { + point = 0; + index = 0; + bytes = 0; + typeSize = kSerial_GetTypeSize(*type); + if (typeSize != 0) + { + *lens /= typeSize; + } + return KS_OK; + } + } + } + if (++point >= KS_MAX_RECV_BUFF_SIZE) + { + point = 0; + } + return KS_ERROR; +#else + return KS_ERROR; +#endif +} + +/** + * @brief kSerial_Read + */ +uint32_t kSerial_Read( kserial_t *ks ) +{ +#if KSERIAL_RECV_ENABLE + uint32_t available = 0; + uint32_t nbyte; + uint32_t newindex; + + do + { // add rx data to packet buffer + nbyte = kSerial_Recv(&ks->buffer[ks->count], ks->size - ks->count); + if (nbyte) + { + available = 1; + ks->count += nbyte; + } + } + while (nbyte); + + ks->pkcnt = 0; + if (available) + { + newindex = kSerial_UnpackBuffer(ks->buffer, ks->count, ks->packet, &ks->pkcnt); + if (ks->pkcnt) + { + // update packet buffer + ks->count -= newindex; + memcpy(ks->buffer, &ks->buffer[newindex], ks->count); + memset(&ks->buffer[ks->count], 0, ks->size - ks->count); + } + } + // TODO: fix return + return ks->pkcnt; +#else + return KS_ERROR; +#endif +} + +/** + * @brief kSerial_ReadFlush + */ +void kSerial_ReadFlush( kserial_t *ks ) +{ +#if KSERIAL_RECV_ENABLE + kSerial_RecvFlush(); + memset(ks->buffer, 0, ks->size); + ks->count = 0; +#endif +} + +/** + * @brief kSerial_GetPacketData + */ +void kSerial_GetPacketData( kserial_packet_t *ksp, void *pdata, const uint32_t index ) +{ + if (pdata != NULL) + { + memcpy(pdata, ksp[index].data, ksp[index].nbyte); + } + free(ksp[index].data); +} + +/** + * @brief kSerial_TwiWriteReg + * Send packet ['K', 'S', 1, R1, slaveAddress(8-bit), regAddress, ck, regData, '\r'] + */ +uint32_t kSerial_TwiWriteReg( const uint8_t slaveAddr, const uint8_t regAddr, const uint8_t regData ) +{ +#if KSERIAL_TWI_ENABLE + uint8_t param[2] = {slaveAddr << 1, regAddr}; + uint32_t type = KS_R1; + uint32_t nbytes; + + kSerial_RecvFlush(); + + nbytes = kSerial_Pack(ksSendBuff, param, type, 1, ®Data); + kSerial_Send(ksSendBuff, nbytes); +#if 0 + klogd("[W] param = %02X, %02X, type = %d, bytes = %d, data = %02X\n", param[0], param[1], type, nbytes, wdata); +#endif + return nbytes; +#else + return KS_ERROR; +#endif +} + +/** + * @brief kSerial_TwiReadReg + * Send packet ['K', 'S', 1, R1, slaveAddress(8-bit)+1, regAddress, ck, 1, '\r'] + * Recv packet ['K', 'S', 1, R1, slaveAddress(8-bit)+1, regAddress, ck, regData, '\r'] + */ +uint32_t kSerial_TwiReadReg( const uint8_t slaveAddr, const uint8_t regAddr, uint8_t *regData ) +{ +#if KSERIAL_TWI_ENABLE + uint8_t param[2] = {(slaveAddr << 1) + 1, regAddr}; + uint32_t type = KS_R1; + uint32_t nbytes; + uint32_t status; + uint32_t singleRead = 1; + + kSerial_RecvFlush(); + + nbytes = kSerial_Pack(ksSendBuff, param, type, 1, &singleRead); + kSerial_Send(ksSendBuff, nbytes); + + nbytes = 0; + while (nbytes == 0) + { + kSerial_Delay(100); + nbytes = kSerial_Recv(ksRecvBuff, KS_MAX_RECV_BUFF_SIZE); + } + + // TODO: check i2cbuff first 'KS' + status = kSerial_Unpack(ksRecvBuff, param, &type, &nbytes, ksSendBuff); + if (status == KS_OK) + { + for (uint32_t i = 0; i < nbytes; i++) + { + regData[i] = ksSendBuff[i]; + } +#if 0 + klogd("[R] param = %02X, %02X, type = %d, bytes = %d, data =", param[0], param[1], type, nbytes + 8); + for (uint32_t i = 0; i < nbytes; i++) + { + klogd(" %02X", i2cbuff[1][i]); + } + klogd("\n"); +#endif + } + return status; +#else + return KS_ERROR; +#endif +} + +/** + * @brief kSerial_TwiReadRegs + * Send packet ['K', 'S', 1, R1, slaveAddress(8-bit)+1, regAddress, ck, lens, '\r'] + * Recv packet ['K', 'S', lens, R1, slaveAddress(8-bit)+1, regAddress, ck, regData ..., '\r'] + */ +uint32_t kSerial_TwiReadRegs( const uint8_t slaveAddr, const uint8_t regAddr, uint8_t *regData, const uint8_t lens ) +{ +#if KSERIAL_TWI_ENABLE + uint8_t param[2] = {(slaveAddr << 1) + 1, regAddr}; + uint32_t type = KS_R1; + uint32_t nbytes; + uint32_t status; + + kSerial_RecvFlush(); + + nbytes = kSerial_Pack(ksSendBuff, param, type, 1, &lens); + kSerial_Send(ksSendBuff, nbytes); + + nbytes = 0; + while (nbytes == 0) + { + kSerial_Delay(100); + nbytes = kSerial_Recv(ksRecvBuff, KS_MAX_RECV_BUFF_SIZE); + } + + // TODO: check i2cbuff first 'KS' + status = kSerial_Unpack(ksRecvBuff, param, &type, &nbytes, ksSendBuff); + if (status == KS_OK) + { + for (uint32_t i = 0; i < nbytes; i++) + { + regData[i] = ksSendBuff[i]; + } +#if 0 + klogd("[R] param = %02X, %02X, type = %d, bytes = %d, data =", param[0], param[1], type, nbytes + 8); + for (uint32_t i = 0; i < nbytes; i++) + { + klogd(" %02X", i2cbuff[1][i]); + } + klogd("\n"); +#endif + } + return status; +#else + return KS_ERROR; +#endif +} + +/** + * @brief kSerial_TwiCheck + * Send packet ['K', 'S', 1, R1, 1, 0, ck, 1, '\r'] + * Recv packet ['K', 'S', 1, R1, 1, 0, ck, regData, '\r'] + */ +uint32_t kSerial_TwiCheck( void ) +{ +#if KSERIAL_TWI_ENABLE + uint8_t val; + if (kSerial_TwiReadReg(0x00, 0x00, &val) != KS_OK) + { + return KS_ERROR; + } + return KS_OK; +#else + return KS_ERROR; +#endif +} + +/** + * @brief kSerial_TwiScanDevice + * Send packet ['K', 'S', 0, R2, 0xAB, 0, ck, '\r'] + * Recv packet ['K', 'S', lens, R2, 0xAB, 0, ck, address ..., '\r'] + */ +uint32_t kSerial_TwiScanDevice( uint8_t *slaveAddr ) +{ +#if KSERIAL_TWI_ENABLE + uint8_t param[2] = {0xAB, 0}; + uint32_t type = KS_R2; + uint32_t nbytes; + uint32_t status; + uint32_t count; + + kSerial_RecvFlush(); + + nbytes = kSerial_Pack(ksSendBuff, param, type, 0, NULL); + kSerial_Send(ksSendBuff, nbytes); + + Serial_Delay(100); + nbytes = kSerial_Recv(ksRecvBuff, KS_MAX_RECV_BUFF_SIZE); + + // TODO: check i2cbuff first 'KS' + status = kSerial_Unpack(ksRecvBuff, param, &type, &count, ksSendBuff); + if (status == KS_OK) + { + for (uint32_t i = 0; i < count; i++) + { + slaveAddr[i] = ksSendBuff[i]; + } +#if 0 + printf(" >> i2c device list (found %d device)\n\n", count); + printf(" "); + for (uint32_t i = 0; i < count; i++) + { + printf(" %02X", slaveAddr[i]); + } + printf("\n\n"); +#endif + } + else + { + return 0xFF; + } + return count; +#else + return KS_ERROR; +#endif +} + +/** + * @brief kSerial_TwiScanRegister + * Send packet ['K', 'S', 0, R2, 0xCB, slaveAddress, ck, '\r'] + * Recv packet ['K', 'S', 256, R2, 0xCB, slaveAddress, ck, address ..., '\r'] + */ +uint32_t kSerial_TwiScanRegister( const uint8_t slaveAddr, uint8_t reg[256] ) +{ +#if KSERIAL_TWI_ENABLE + uint8_t param[2] = {0xCB, slaveAddr << 1}; + uint32_t type = KS_R2; + uint32_t nbytes; + uint32_t status; + + kSerial_RecvFlush(); + + nbytes = kSerial_Pack(ksSendBuff, param, type, 0, NULL); + kSerial_Send(ksSendBuff, nbytes); + + Serial_Delay(100); + nbytes = kSerial_Recv(ksRecvBuff, KS_MAX_RECV_BUFF_SIZE); + + // TODO: check i2cbuff first 'KS' + status = kSerial_Unpack(ksRecvBuff, param, &type, &nbytes, ksSendBuff); + if (status == KS_OK) + { + for (uint32_t i = 0; i < 256; i++) + { + reg[i] = ksSendBuff[i]; + } +#if 0 + printf("\n"); + printf(" >> i2c device register (address 0x%02X)\n\n", slaveAddr); + printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); + for (uint32_t i = 0; i < 256; i += 16) + { + printf(" %02X: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + i, + reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3], + reg[i + 4], reg[i + 5], reg[i + 6], reg[i + 7], + reg[i + 8], reg[i + 9], reg[i + 10], reg[i + 11], + reg[i + 12], reg[i + 13], reg[i + 14], reg[i + 15] + ); + } + printf("\n\n"); +#endif + } + return status; +#else + return KS_ERROR; +#endif +} + +/*************************************** END OF FILE ****************************************/ diff --git a/app/src/main/jni/kSerial.h b/app/src/main/jni/kSerial.h new file mode 100644 index 0000000..d5f899a --- /dev/null +++ b/app/src/main/jni/kSerial.h @@ -0,0 +1,113 @@ +/** + * __ ____ + * / /__ _ __ / __/ __ + * / //_/(_)/ /_ / / ___ ____ ___ __ __ / /_ + * / ,< / // __/_\ \ / _ \ / __// _ \/ // // __/ + * /_/|_|/_/ \__//___// .__//_/ \___/\_,_/ \__/ + * /_/ github.com/KitSprout + * + * @file kSerial.h + * @author KitSprout + * @date Jan-2020 + * @brief + * + */ + +/* Define to prevent recursive inclusion ---------------------------------------------------*/ +#ifndef __KSERIAL_H +#define __KSERIAL_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes --------------------------------------------------------------------------------*/ +#include +#include "kStatus.h" + +/* Define ----------------------------------------------------------------------------------*/ + +#ifndef KSERIAL_SEND_ENABLE +#define KSERIAL_SEND_ENABLE (0U) +#ifndef KS_MAX_SEND_BUFF_SIZE +#define KS_MAX_SEND_BUFF_SIZE (4096 + 32) +#endif +#endif + +#ifndef KSERIAL_RECV_ENABLE +#define KSERIAL_RECV_ENABLE (0U) +#ifndef KS_MAX_RECV_BUFF_SIZE +#define KS_MAX_RECV_BUFF_SIZE (4096 + 1024 + 32) +#endif +#endif + +#ifndef KSERIAL_TWI_ENABLE +#define KSERIAL_TWI_ENABLE (0U) +#endif +#if KSERIAL_TWI_ENABLE +#if !(KSERIAL_SEND_ENABLE && KSERIAL_RECV_ENABLE) +#error "Need to enable send and recv" +#endif +#endif + +/* Macro -----------------------------------------------------------------------------------*/ +/* Typedef ---------------------------------------------------------------------------------*/ + +typedef struct +{ + uint8_t param[2]; + uint32_t type; + uint32_t lens; + uint32_t nbyte; + void *data; + +} kserial_packet_t; + +typedef struct +{ + uint32_t size; + uint32_t count; + uint8_t *buffer; + + uint32_t pkcnt; + kserial_packet_t *packet; + +} kserial_t; + +/* Extern ----------------------------------------------------------------------------------*/ +/* Functions -------------------------------------------------------------------------------*/ + +uint32_t kSerial_GetTypeSize( uint32_t type ); + +uint32_t kSerial_CheckHeader( const uint8_t *packet, void *param, uint32_t *type, uint32_t *nbyte ); +uint32_t kSerial_CheckEnd( const uint8_t *packet, const uint32_t nbyte ); +uint32_t kSerial_Check( const uint8_t *packet, const uint32_t lens, void *param, uint32_t *type, uint32_t *nbyte ); +void kSerial_GetBytesData( const uint8_t *packet, void *pdata, const uint32_t nbyte ); + +uint32_t kSerial_Pack( uint8_t *packet, const void *param, const uint32_t type, const uint32_t lens, const void *pdata ); +uint32_t kSerial_Unpack( const uint8_t *packet, const uint32_t bufsize, void *param, uint32_t *type, uint32_t *nbyte, void *pdata ); +uint32_t kSerial_UnpackBuffer( const uint8_t *buffer, const uint32_t buffersize, kserial_packet_t *packet, uint32_t *packetcnt ); +void kSerial_GetPacketData( kserial_packet_t *ksp, void *pdata, const uint32_t index ); + +uint32_t kSerial_SendPacket( void *param, void *sdata, const uint32_t lens, const uint32_t type ); +uint32_t kSerial_RecvPacket( void *param, void *rdata, uint32_t *lens, uint32_t *type ); + +uint32_t kSerial_Read( kserial_t *ks ); +void kSerial_ReadFlush( kserial_t *ks ); +void kSerial_GetPacketData( kserial_packet_t *ksp, void *pdata, const uint32_t index ); + +uint32_t kSerial_TwiWriteReg( const uint8_t slaveAddr, const uint8_t regAddr, const uint8_t regData ); +uint32_t kSerial_TwiReadReg( const uint8_t slaveAddr, const uint8_t regAddr, uint8_t *regData ); +uint32_t kSerial_TwiReadRegs( const uint8_t slaveAddr, const uint8_t regAddr, uint8_t *regData, const uint8_t lens ); +uint32_t kSerial_TwiCheck( void ); +uint32_t kSerial_TwiScanDevice( uint8_t *slaveAddr ); +uint32_t kSerial_TwiScanRegister( const uint8_t slaveAddr, uint8_t reg[256] ); + + +#ifdef __cplusplus +} +#endif + +#endif + +/*************************************** END OF FILE ****************************************/ diff --git a/app/src/main/jni/kSerialJNI.cpp b/app/src/main/jni/kSerialJNI.cpp new file mode 100644 index 0000000..45f7776 --- /dev/null +++ b/app/src/main/jni/kSerialJNI.cpp @@ -0,0 +1,286 @@ +#include + +#include "kSerial.h" + +#include +#define LOG_TAG "KS_LIB" +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + +extern "C" +{ + +#define MAX_KSERIAL_PACKET_LENS (1024) +uint8_t kspackbuf[1032] = {0}; +uint32_t kspacketcnt = 0; +kserial_packet_t kspacket[MAX_KSERIAL_PACKET_LENS] = {0}; + +JNIEXPORT jbyteArray +Java_com_kitsprout_ks_KSerial_pack( + JNIEnv* env, jclass clazz, jintArray jparam, jint jtype, jint jlens, jdoubleArray jdata) { + uint8_t param[2] = {0}; + uint32_t type = (uint32_t) jtype; + uint32_t lens = (uint32_t) jlens; + uint32_t packetTotalBytes; + int *pkparam = env->GetIntArrayElements(jparam, nullptr); + double *pkdata = env->GetDoubleArrayElements(jdata, nullptr); + + param[0] = (uint8_t) pkparam[0]; + param[1] = (uint8_t) pkparam[1]; + + switch (type) { + case KS_I8: { + int8_t data[1024]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = (int8_t) pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + case KS_I16: { + int16_t data[512]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = (int16_t) pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + case KS_I32: { + int32_t data[256]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = (int32_t) pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + case KS_I64: { + int64_t data[128]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = (int64_t) pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + case KS_R0: + case KS_R1: + case KS_R2: + case KS_R3: + case KS_R4: + case KS_U8: { + uint8_t data[1024]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = (uint8_t) pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + case KS_U16: { + uint16_t data[512]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = (uint16_t) pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + case KS_U32: { + uint32_t data[256]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = (uint32_t) pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + case KS_U64: { + uint64_t data[128]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = (uint64_t) pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } +// case KS_F16: { +// half data[512]; +// for (uint32_t i = 0; i < lens; i++) { +// data[i] = (half) pkdata[i]; +// } +// break; +// } + case KS_F32: { + float data[256]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = (float) pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + case KS_F64: { + double data[128]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = pkdata[i]; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + default: { + uint8_t data[1024]; + for (uint32_t i = 0; i < lens; i++) { + data[i] = 0; + } + packetTotalBytes = kSerial_Pack(kspackbuf, param, type, lens, data); + break; + } + } + jbyteArray jpacket = env->NewByteArray(packetTotalBytes); + env->SetByteArrayRegion(jpacket, 0, packetTotalBytes, (jbyte*)kspackbuf); + + return jpacket; +} + +JNIEXPORT jint +Java_com_kitsprout_ks_KSerial_unpackBuffer( + JNIEnv* env, jclass clazz, jbyteArray jbuf, jint jlens) { + const uint32_t pkbufsize = (uint32_t) jlens; + uint8_t *pkbuf = (uint8_t *)env->GetByteArrayElements(jbuf, nullptr); + uint32_t newindex = kSerial_UnpackBuffer(pkbuf, pkbufsize, kspacket, &kspacketcnt); + return newindex; +} + +JNIEXPORT jint +Java_com_kitsprout_ks_KSerial_getPacketCount( + JNIEnv* env, jclass clazz) { + return kspacketcnt; +} + +JNIEXPORT jint +Java_com_kitsprout_ks_KSerial_getPacketType( + JNIEnv* env, jclass clazz, jint index) { + return kspacket[index].type; +} + +JNIEXPORT jint +Java_com_kitsprout_ks_KSerial_getPacketBytes( + JNIEnv* env, jclass clazz, jint index) { + + return kspacket[index].nbyte; +} + +JNIEXPORT jintArray +Java_com_kitsprout_ks_KSerial_getPacketParam( + JNIEnv* env, jclass clazz, jint index) { + jintArray jparam = env->NewIntArray(2); + int param[2]; + param[0] = kspacket[index].param[0]; + param[1] = kspacket[index].param[1]; + env->SetIntArrayRegion(jparam, 0, 2, param); + return jparam; +} + +JNIEXPORT jdoubleArray +Java_com_kitsprout_ks_KSerial_getPacketData( + JNIEnv* env, jclass clazz, jint index) { + + jdoubleArray jdata = env->NewDoubleArray(kspacket[index].lens); + double data[kspacket[index].lens]; + switch (kspacket[index].type) { + case KS_I8: { + int8_t pdata[kspacket[index].lens]; + kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = (double)pdata[i]; + } + break; + } + case KS_I16: { + int16_t pdata[kspacket[index].lens]; + kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = (double)pdata[i]; + } + break; + } + case KS_I32: { + int32_t pdata[kspacket[index].lens]; + kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = (double)pdata[i]; + } + break; + } + case KS_I64: { + int64_t pdata[kspacket[index].lens]; + kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = (double)pdata[i]; + } + break; + } + case KS_R0: + case KS_R1: + case KS_R2: + case KS_R3: + case KS_R4: + case KS_U8: { + uint8_t pdata[kspacket[index].lens]; + kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = (double)pdata[i]; + } + break; + } + case KS_U16: { + uint16_t pdata[kspacket[index].lens]; + kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = (double)pdata[i]; + } + break; + } + case KS_U32: { + uint32_t pdata[kspacket[index].lens]; + kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = (double)pdata[i]; + } + break; + } + case KS_U64: { + uint64_t pdata[kspacket[index].lens]; + kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = (double)pdata[i]; + } + break; + } +// case KS_F16: { +// float16_t pdata[kspacket[index].lens]; +// for (uint32_t i = 0; i < kspacket[index].lens; i++) { +// kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); +// data[i] = (double)pdata[i]; +// } +// break; +// } + case KS_F32: { + float pdata[kspacket[index].lens]; + kSerial_GetPacketData(kspacket, pdata, (uint32_t)index); + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = (double)pdata[i]; + } + break; + } + case KS_F64: { + kSerial_GetPacketData(kspacket, data, (uint32_t)index); + break; + } + default: { + for (uint32_t i = 0; i < kspacket[index].lens; i++) { + data[i] = 0; + } + break; + } + } + env->SetDoubleArrayRegion(jdata, 0, kspacket[index].lens, data); + + return jdata; +} + +} diff --git a/app/src/main/jni/kStatus.h b/app/src/main/jni/kStatus.h new file mode 100644 index 0000000..005fbb7 --- /dev/null +++ b/app/src/main/jni/kStatus.h @@ -0,0 +1,74 @@ +/** + * __ ____ + * / /__ _ __ / __/ __ + * / //_/(_)/ /_ / / ___ ____ ___ __ __ / /_ + * / ,< / // __/_\ \ / _ \ / __// _ \/ // // __/ + * /_/|_|/_/ \__//___// .__//_/ \___/\_,_/ \__/ + * /_/ github.com/KitSprout + * + * @file kStatus.h + * @author KitSprout + * @date Mar-2020 + * @brief + * + */ + +/* Define to prevent recursive inclusion ---------------------------------------------------*/ +#ifndef __KSTATUS_H +#define __KSTATUS_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes --------------------------------------------------------------------------------*/ +/* Define ----------------------------------------------------------------------------------*/ + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#ifndef KSSTATUS +#define KSSTATUS +#define KS_OK (0U) +#define KS_ERROR (1U) +#define KS_BUSY (2U) +#define KS_TIMEOUT (3U) +#endif + +#ifndef KSUNIT +#define KSUNIT +#define KS_U8 (0x0) /* 4'b 0000 */ +#define KS_U16 (0x1) /* 4'b 0001 */ +#define KS_U32 (0x2) /* 4'b 0010 */ +#define KS_U64 (0x3) /* 4'b 0011 */ +#define KS_I8 (0x4) /* 4'b 0100 */ +#define KS_I16 (0x5) /* 4'b 0101 */ +#define KS_I32 (0x6) /* 4'b 0110 */ +#define KS_I64 (0x7) /* 4'b 0111 */ +#define KS_F16 (0x9) /* 4'b 1001 */ +#define KS_F32 (0xA) /* 4'b 1010 */ +#define KS_F64 (0xB) /* 4'b 1011 */ +#define KS_R0 (0x8) /* 4'b 1000 */ +#define KS_R1 (0xC) /* 4'b 1100 */ +#define KS_R2 (0xD) /* 4'b 1101 */ +#define KS_R3 (0xE) /* 4'b 1110 */ +#define KS_R4 (0xF) /* 4'b 1111 */ +#endif + +/* Macro -----------------------------------------------------------------------------------*/ +/* Typedef ---------------------------------------------------------------------------------*/ +/* Extern ----------------------------------------------------------------------------------*/ +/* Functions -------------------------------------------------------------------------------*/ + +#ifdef __cplusplus +} +#endif + +#endif + +/*************************************** END OF FILE ****************************************/ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/shape.xml b/app/src/main/res/drawable/shape.xml new file mode 100644 index 0000000..e9186e1 --- /dev/null +++ b/app/src/main/res/drawable/shape.xml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..d8ec51b --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,98 @@ + + + +