From 2dcf9cc893a27c50d538baf8b5d47ff06fe495d8 Mon Sep 17 00:00:00 2001
From: android <android@lingyun.com>
Date: Thu, 25 Jul 2024 09:42:53 +0800
Subject: [PATCH] Add Android apk SDK source code
---
app_sdk/app_src/app/src/main/cpp/led_control.cpp | 160 +
app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp | 0
app_sdk/app_src/.idea/.gitignore | 3
app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java | 12
app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp | 0
app_sdk/app_src/.idea/deploymentTargetSelector.xml | 18
app_sdk/app_src/.idea/gradle.xml | 19
app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml | 10
app_sdk/app_src/app/src/main/res/layout/activity_can.xml | 271 ++
app_sdk/app_src/app/build.gradle.kts | 63
app_sdk/app_src/app/src/main/res/drawable/background.xml | 8
app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml | 13
app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp | 0
app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp | 0
app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java | 18
app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml | 9
app_sdk/app_src/app/.gitignore | 1
app_sdk/app_src/app/src/main/AndroidManifest.xml | 39
app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp | 0
app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml | 17
app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml | 6
app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml | 10
app_sdk/app_src/app/src/main/cpp/CMakeLists.txt | 32
app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml | 9
app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar | 0
app_sdk/app_src/app/src/main/cpp/include/gpiod.h | 1775 +++++++++++++++
app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java | 12
app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml | 109
app_sdk/app_src/.idea/misc.xml | 9
app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so | 0
app_sdk/app_src/app/src/main/cpp/native-lib.cpp | 51
app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java | 347 ++
app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml | 30
app_sdk/app_src/app/src/main/res/layout/activity_main.xml | 87
app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp | 0
app_sdk/app_src/gradlew.bat | 89
app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java | 9
app_sdk/app_src/app/src/main/res/drawable/button_selector.xml | 7
app_sdk/app_src/.gitignore | 15
app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java | 111
app_sdk/app_src/gradlew | 185 +
app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java | 247 ++
app_sdk/app_src/app/src/main/res/drawable/bt_right.xml | 12
app_sdk/app_src/.idea/vcs.xml | 6
app_sdk/app_src/app/src/main/res/drawable/bg2.jpg | 0
app_sdk/app_src/app/src/main/res/values/colors.xml | 14
app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp | 0
app_sdk/app_src/app/src/main/res/values-night/themes.xml | 16
app_sdk/app_src/app/src/main/res/values/themes.xml | 23
app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java | 9
app_sdk/app_src/app/src/main/res/drawable/bt_left.xml | 12
app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp | 0
app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties | 6
app_sdk/app_src/app/src/main/res/drawable/bg1.jpg | 0
app_sdk/app_src/gradle.properties | 21
app_sdk/app_src/app/src/main/res/drawable/bg_username.xml | 12
app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java | 69
app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java | 169 +
app_sdk/app_src/app/src/main/res/xml/backup_rules.xml | 13
app_sdk/app_src/app/src/main/cpp/can_control.cpp | 193 +
app_sdk/app_src/app/src/main/cpp/rs485_control.cpp | 516 ++++
app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java | 70
app_sdk/app_src/build.gradle.kts | 4
app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml | 423 +++
app_sdk/bin/rk3568test.apk | 0
app_sdk/app_src/gradle/libs.versions.toml | 22
app_sdk/app_src/app/src/main/res/layout/activity_adc.xml | 322 ++
app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp | 0
app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java | 17
app_sdk/app_src/app/src/main/cpp/adc_control.cpp | 522 ++++
app_sdk/app_src/app/src/main/res/values/strings.xml | 78
app_sdk/app_src/settings.gradle.kts | 23
app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp | 0
app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java | 26
app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java | 104
app_sdk/app_src/app/src/main/res/layout/activity_led.xml | 95
app_sdk/app_src/.idea/migrations.xml | 10
app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml | 14
app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp | 111
app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml | 13
app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 6
app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml | 170 +
app_sdk/app_src/.idea/compiler.xml | 6
app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml | 19
app_sdk/app_src/app/src/main/res/layout/activity_menu.xml | 70
app_sdk/app_src/app/proguard-rules.pro | 21
86 files changed, 7,038 insertions(+), 0 deletions(-)
diff --git a/app_sdk/app_src/.gitignore b/app_sdk/app_src/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/app_sdk/app_src/.gitignore
@@ -0,0 +1,15 @@
+*.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
+local.properties
diff --git a/app_sdk/app_src/.idea/.gitignore b/app_sdk/app_src/.idea/.gitignore
new file mode 100644
index 0000000..eaf91e2
--- /dev/null
+++ b/app_sdk/app_src/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/app_sdk/app_src/.idea/compiler.xml b/app_sdk/app_src/.idea/compiler.xml
new file mode 100644
index 0000000..8fabff5
--- /dev/null
+++ b/app_sdk/app_src/.idea/compiler.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <bytecodeTargetLevel target="17" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/app_sdk/app_src/.idea/deploymentTargetSelector.xml b/app_sdk/app_src/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..ba3e90b
--- /dev/null
+++ b/app_sdk/app_src/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="deploymentTargetSelector">
+ <selectionStates>
+ <SelectionState runConfigName="app">
+ <option name="selectionMode" value="DROPDOWN" />
+ <DropdownSelection timestamp="2024-05-28T14:14:11.796854900Z">
+ <Target type="DEFAULT_BOOT">
+ <handle>
+ <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\圆圆圆\.android\avd\Medium_Phone_API_28.avd" />
+ </handle>
+ </Target>
+ </DropdownSelection>
+ <DialogSelection />
+ </SelectionState>
+ </selectionStates>
+ </component>
+</project>
\ No newline at end of file
diff --git a/app_sdk/app_src/.idea/gradle.xml b/app_sdk/app_src/.idea/gradle.xml
new file mode 100644
index 0000000..0b0534c
--- /dev/null
+++ b/app_sdk/app_src/.idea/gradle.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleMigrationSettings" migrationVersion="1" />
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
+ <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ <option value="$PROJECT_DIR$/app" />
+ </set>
+ </option>
+ <option name="resolveExternalAnnotations" value="false" />
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project>
\ No newline at end of file
diff --git a/app_sdk/app_src/.idea/migrations.xml b/app_sdk/app_src/.idea/migrations.xml
new file mode 100644
index 0000000..48052b2
--- /dev/null
+++ b/app_sdk/app_src/.idea/migrations.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectMigrations">
+ <option name="MigrateToGradleLocalJavaHome">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ </set>
+ </option>
+ </component>
+</project>
\ No newline at end of file
diff --git a/app_sdk/app_src/.idea/misc.xml b/app_sdk/app_src/.idea/misc.xml
new file mode 100644
index 0000000..8978d23
--- /dev/null
+++ b/app_sdk/app_src/.idea/misc.xml
@@ -0,0 +1,9 @@
+<project version="4">
+ <component name="ExternalStorageConfigurationManager" enabled="true" />
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/build/classes" />
+ </component>
+ <component name="ProjectType">
+ <option name="id" value="Android" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/app_sdk/app_src/.idea/vcs.xml b/app_sdk/app_src/.idea/vcs.xml
new file mode 100644
index 0000000..9661ac7
--- /dev/null
+++ b/app_sdk/app_src/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/.gitignore b/app_sdk/app_src/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app_sdk/app_src/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app_sdk/app_src/app/build.gradle.kts b/app_sdk/app_src/app/build.gradle.kts
new file mode 100644
index 0000000..b0109c7
--- /dev/null
+++ b/app_sdk/app_src/app/build.gradle.kts
@@ -0,0 +1,63 @@
+plugins {
+ alias(libs.plugins.android.application)
+}
+
+android {
+ namespace = "com.example.serial"
+ compileSdk = 34
+
+ defaultConfig {
+ applicationId = "com.example.serial"
+ minSdk = 24
+ targetSdk = 34
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+
+ ndk {
+ abiFilters.add("arm64-v8a")
+ }
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ externalNativeBuild {
+ cmake {
+ path = file("src/main/cpp/CMakeLists.txt")
+ version = "3.22.1"
+ }
+ }
+ buildFeatures {
+ viewBinding = true
+ }
+ sourceSets {
+ getByName("main") {
+ jni {
+ srcDirs("src\\main\\jni", "src\\main\\jni")
+ }
+ }
+ }
+}
+
+dependencies {
+
+ implementation(libs.appcompat)
+ implementation(libs.material)
+ implementation(libs.constraintlayout)
+ implementation(libs.activity)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.ext.junit)
+ androidTestImplementation(libs.espresso.core)
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/proguard-rules.pro b/app_sdk/app_src/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/app_sdk/app_src/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
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java b/app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..d400127
--- /dev/null
+++ b/app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.example.serial;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.example.serial", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/AndroidManifest.xml b/app_sdk/app_src/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7103e33
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:sharedUserId="android.uid.system"
+ android:sharedUserMaxSdkVersion="32">
+
+ <application
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.Serial"
+ tools:targetApi="31"
+ android:requestLegacyExternalStorage="true">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".AdcActivity"/>
+ <activity android:name=".RS485Activity" />
+ <activity android:name=".MenuActivity" />
+ <activity android:name=".BuzzerActivity" />
+ <activity android:name=".CanActivity" />
+ <activity android:name=".LedActivity" />
+ <activity android:name=".VisitRootfileActivity" />
+ </application>
+
+ <uses-permission android:name="android.permission." />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+</manifest>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/cpp/CMakeLists.txt b/app_sdk/app_src/app/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..d2162ce
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,32 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html.
+# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
+
+# Sets the minimum CMake version required for this project.
+cmake_minimum_required(VERSION 3.22.1)
+project("serial")
+
+# 定义 libgpiod 目标并设置为 IMPORTED 类型
+add_library(libgpiod SHARED IMPORTED)
+
+# 设置 libgpiod 的 IMPORTED_LOCATION 属性
+set_target_properties(libgpiod
+ PROPERTIES IMPORTED_LOCATION
+ ${CMAKE_SOURCE_DIR}/../jni/${CMAKE_ANDROID_ARCH_ABI}/libgpiod.so)
+
+# 添加你的项目的目标
+add_library(${CMAKE_PROJECT_NAME}
+ SHARED # List C/C++ source files with relative paths to this CMakeLists.txt.
+ rs485_control.cpp
+ can_control.cpp
+ led_control.cpp
+ buzzer_control.cpp
+ adc_control.cpp
+ native-lib.cpp)
+
+# 将 libgpiod 链接到你的项目中
+target_link_libraries(${CMAKE_PROJECT_NAME}
+ # List libraries link to the target library
+ ${log-lib}
+ libgpiod
+ log)
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/cpp/adc_control.cpp b/app_sdk/app_src/app/src/main/cpp/adc_control.cpp
new file mode 100644
index 0000000..02209a6
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/cpp/adc_control.cpp
@@ -0,0 +1,522 @@
+#include <jni.h>
+#include <string>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <android/log.h>
+#include "include/gpiod.h"
+
+#define TAG "SerialApp"
+#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
+#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
+#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
+
+typedef struct comport_s
+{
+ char devname[12];
+ unsigned int databit, parity, stopbit, flowctrl;
+ long baudrate;
+
+ int fd;
+ int frag_size;
+}comport_t;
+
+comport_t *adc_comport;
+
+// 将int类型的波特率转换成可以识别的波特率
+static speed_t getBaudRate(int baudRate){
+ switch (baudRate) {
+ case 0:
+ return B0;
+ case 50:
+ return B50;
+ case 75:
+ return B75;
+ case 110:
+ return B110;
+ case 134:
+ return B134;
+ case 150:
+ return B150;
+ case 200:
+ return B200;
+ case 300:
+ return B300;
+ case 600:
+ return B600;
+ case 1200:
+ return B1200;
+ case 1800:
+ return B1800;
+ case 2400:
+ return B2400;
+ case 4800:
+ return B4800;
+ case 9600:
+ return B9600;
+ case 19200:
+ return B19200;
+ case 38400:
+ return B38400;
+ case 57600:
+ return B57600;
+ case 115200:
+ return B115200;
+ case 230400:
+ return B230400;
+ case 460800:
+ return B460800;
+ case 500000:
+ return B500000;
+ case 576000:
+ return B576000;
+ case 921600:
+ return B921600;
+ case 1000000:
+ return B1000000;
+ case 1152000:
+ return B1152000;
+ case 1500000:
+ return B1500000;
+ case 2000000:
+ return B2000000;
+ case 2500000:
+ return B2500000;
+ case 3000000:
+ return B3000000;
+ case 3500000:
+ return B3500000;
+ case 4000000:
+ return B4000000;
+ default:
+ return -1;
+ }
+}
+
+unsigned short crc_modbus(unsigned char *ptr, int len)
+{
+ unsigned int i;
+ unsigned short crc = 0xFFFF; //crc16位寄存器初始值
+
+ while(len--)
+ {
+ crc ^= *ptr++;
+ for (i = 0; i < 8; ++i)
+ {
+ if (crc & 1)
+ crc = (crc >> 1) ^ 0xA001; //多项式 POLY(0x8005)的高低位交换值,这是由于其模型的一>些参数决定的
+ else
+ crc = (crc >> 1);
+ }
+ }
+
+ return crc;
+}
+
+void calculate(char *result, unsigned char *data, int len)
+{
+ int i = 0;
+ unsigned char temp[6];
+ char info[8];
+ for(i=0; i<len; i++)
+ {
+ if(data[i] == 0xAA && data[i+1] == 0x55)
+ {
+ memset(temp, 0, sizeof(temp));
+ if( (data[i+2] == 0x01 || data[i+2] == 0x02) && \
+ (data[i+3] >= 0x00 && data[i+3] <= 0x03))
+ {
+ // temp用于进行CRC校验
+ temp[0] = data[i];
+ temp[1] = data[i+1];
+ temp[2] = data[i+2];
+ temp[3] = data[i+3];
+ temp[4] = data[i+4];
+ temp[5] = data[i+5];
+ //CRC,if right continue, else break.
+ uint16_t crc = crc_modbus(temp, 6);
+ printf("received buffer crc = %04X\n", crc);
+ if(data[i+6] != (crc&0xFF) || data[i+7] != ((crc>>8)&0xFF))
+ {
+ LOGE("read failed: CRC failure. received CRC: %02X %02X, calculated CRC:%02X %02X\n", \
+ temp[4], temp[5], (crc&0xFF), ((crc>>8)&0xFF));
+ continue ;
+ }
+
+ // calculate 43 02 ==> 0243
+ memset(info, 0, sizeof(info));
+ snprintf(info, sizeof(info), "%02x%02x", data[i+5], data[i+4]);
+ int length = sizeof(info) / sizeof(info[0]);
+ long long sum = 0;
+ printf("info=%s\n", info);
+ for(int j=0; j<strlen(info); j++)
+ {
+ char a = info[j];
+ if(a >= '0' && a <= '9')
+ a = a - '0';
+ else if(a >= 'A' && a <= 'F')
+ a = a - 'A' + 10;
+ else if(a >= 'a' && a <= 'f')
+ a = a - 'a' + 10;
+ int n = a << (4*(3-j));
+ sum += n;
+ }
+
+ char temp_res[350];
+ memset(temp_res, 0, sizeof(temp_res));
+ if(data[i+2] == 0x01)
+ {
+ strcat(result, "电流-");
+ snprintf(temp_res, sizeof(temp_res), "通道%d=%.2fmA ", data[i+3], (double)sum*0.01);
+ }
+ else if(data[i+2] == 0x02)
+ {
+ strcat(result, "电压-");
+ snprintf(temp_res, sizeof(temp_res), "通道%d=%.3fV ", data[i+3], (double)sum*0.001);
+ }
+ strcat(result, temp_res);
+ }
+ }
+ }
+}
+
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_AdcControl_openComport(JNIEnv *env, jclass clazz, jstring path,
+ jlong baud_rate, jint data_bits, jint parity,
+ jint stop_bits, jint flow_control) {
+ // TODO: implement openComport()
+ speed_t speed;
+ struct termios old_cfg, new_cfg;
+ int old_flags;
+ long temp;
+
+ adc_comport = (comport_t *) malloc(sizeof(comport_t));
+ if (adc_comport == NULL) {
+ LOGE("Failed to allocate memory for comport structure.");
+ return -1;
+ }
+ memset(adc_comport, 0, sizeof(adc_comport));
+
+ // 检查波特率是否合法
+ {
+ speed = getBaudRate(baud_rate);
+ if (speed == -1)
+ {
+ LOGE("Invalid buad rate");
+ return -1;
+ }
+ }
+//
+// // 打开串口设备
+ {
+ jboolean iscopy;
+ // 获取String字符串path中的UTF8编码,并将其转换成C中的字符串
+ const char* devname = (*env).GetStringUTFChars(path, &iscopy);
+ LOGD("Opening serial port %s with flags 0x%x", devname, O_RDWR);
+ strncpy(adc_comport->devname, devname, 12);
+ adc_comport->baudrate = baud_rate;
+ adc_comport->fd = -1;
+ adc_comport->frag_size = 128;
+ adc_comport->databit = data_bits;
+ adc_comport->parity = parity;
+ adc_comport->flowctrl = flow_control;
+ adc_comport->stopbit = stop_bits;
+
+ if( !strstr(adc_comport->devname, "tty") )
+ {
+ LOGE("Open Not a tty device \" %s\" \n", adc_comport->devname);
+ return -2;
+ }
+
+ adc_comport->fd = open(adc_comport->devname, O_RDWR);
+ if ( adc_comport->fd < 0 )
+ {
+ if( strstr(strerror(errno), "Permission denied") != nullptr )
+ return 1;
+ LOGE("open port failed:%s\n", strerror(errno));
+ return -3;
+ }
+ LOGD("Open device %s", adc_comport->devname);
+ // 释放JNI字符串的UTF8编码
+ (*env).ReleaseStringUTFChars(path, devname);
+
+ if ( 0 != tcgetattr(adc_comport->fd, &old_cfg))
+ {
+ LOGE("open port failed:%s\n", strerror(errno));
+ return -6;
+ }
+ }
+//
+ {
+ LOGD("starting configuring serial port");
+
+ old_cfg.c_cflag &= ~CSIZE;
+ old_cfg.c_cflag |= CREAD | CLOCAL;
+ old_cfg.c_lflag &= ~ICANON;
+ old_cfg.c_lflag &= ~ECHO;
+ old_cfg.c_lflag &= ~ECHOE;
+ old_cfg.c_lflag &= ~ECHONL;
+ old_cfg.c_lflag &= ~ISIG;
+ old_cfg.c_iflag &= ~(IXON | IXOFF | IXANY);
+ old_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
+ old_cfg.c_oflag &= ~OPOST;
+ old_cfg.c_oflag &= ~ONLCR;
+
+
+ switch (adc_comport->databit)
+ {
+ case 7:
+ old_cfg.c_cflag |= CS7;
+ break;
+ case 6:
+ old_cfg.c_cflag |= CS6;
+ break;
+ case 5:
+ old_cfg.c_cflag |= CS5;
+ break;
+ case 8:
+ old_cfg.c_cflag |= CS8;
+ break;
+ }
+
+ switch(adc_comport->parity)
+ {
+ case 1:
+ old_cfg.c_cflag |= (PARENB | PARODD);
+ old_cfg.c_cflag |= (INPCK | ISTRIP);
+ break;
+ case 2:
+ old_cfg.c_cflag |= PARENB;
+ old_cfg.c_cflag &= ~PARODD;
+ old_cfg.c_cflag |= (INPCK | ISTRIP);
+ break;
+ case 0:
+ old_cfg.c_cflag &= ~PARENB;
+ break;
+ }
+
+ if(1 != adc_comport->stopbit)
+ {
+ old_cfg.c_cflag |= CSTOPB;
+ }
+ else
+ {
+ old_cfg.c_cflag &= ~CSTOPB;
+ }
+
+ // set flow control
+ // 1: software control; 2:hardware control; 0: none
+ switch(adc_comport->flowctrl)
+ {
+ case 1:
+ old_cfg.c_cflag &= ~(CRTSCTS);
+ old_cfg.c_iflag |= (IXON | IXOFF);
+ break;
+ case 2:
+ old_cfg.c_cflag |= CRTSCTS;
+ old_cfg.c_iflag &= ~(IXON | IXOFF);
+ break;
+ case 0:
+ old_cfg.c_cflag &= ~(CRTSCTS);
+ break;
+ }
+
+
+ // set baudRate
+ cfsetispeed(&old_cfg, speed);
+ cfsetospeed(&old_cfg, speed);
+
+ old_cfg.c_cc[VTIME] = 10;
+ old_cfg.c_cc[VMIN] = 0;
+
+
+ // 将设置的串口参数应用到串口上,TCSANOW表示立即生效,(TCSADRAIN:在所有输出都被传输后生效;TCSAFLUSH:在所有输出都被传输后生效,同时丢弃所有未读取的输入)
+ if(tcsetattr(adc_comport->fd, TCSANOW, &old_cfg))
+ {
+ LOGE("tcsetattr() failed:%s", strerror(errno));
+ close(adc_comport->fd);
+ return -1;
+ }
+ LOGD("Connected device \" %s \" successfully\n", adc_comport->devname);
+ LOGD("port:%s, databit:%d, stopbit:%d, parity:%d, flowctl:%d", adc_comport->devname, adc_comport->databit, adc_comport->stopbit, adc_comport->parity, adc_comport->flowctrl);
+ }
+
+ return 0;
+}
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_AdcControl_closeComport(JNIEnv *env, jclass clazz) {
+ // TODO: implement closeadc_comport()
+ if ( !adc_comport )
+ {
+ LOGE("%s() get invalid input arguments.\n", __FUNCTION__ );
+ return -1;
+ }
+
+ if (adc_comport->fd >= 0)
+ {
+ close(adc_comport->fd);
+ LOGD("close device \" %s \" successfully\n", adc_comport->devname);
+ }
+ adc_comport->fd = -1;
+ free(adc_comport);
+ adc_comport = NULL;
+ return 0;
+}
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_AdcControl_sendToPort(JNIEnv *env, jclass clazz, jstring operate,
+ jstring channel) {
+ // TODO: implement sendToPort()
+ char *operator_native;
+ char *channel_native;
+ unsigned char data[6] = {0xAA, 0x55};
+ int len = 2; //data的长度
+ int rv = -1;
+ int i;
+ unsigned int byte;
+
+ operator_native = (char *)env->GetStringUTFChars(operate, 0);
+ channel_native = (char *)env->GetStringUTFChars(channel, 0);
+ if(!operator_native || !channel_native)
+ {
+ LOGE("Invalid parameters\n");
+ return -1;
+ }
+
+ sscanf(operator_native, "%02X", &byte);
+ data[len++] = (unsigned char)byte;
+
+ sscanf(channel_native, "%02X", &byte);
+ data[len++] = (unsigned char)byte;
+
+ // 计算CRC16-MODBUS校验码
+ uint16_t crc = crc_modbus(data, len);
+ data[len++] = crc & 0xFF;
+ data[len++] = (crc >> 8) & 0xFF;
+
+ rv = write(adc_comport->fd, data, len);
+ if(rv < 0)
+ {
+ LOGE("write can data failed:%s", strerror(errno));
+ close(adc_comport->fd);
+ return -2;
+ }
+ LOGD("write %d bytes can data success", rv);
+ for(i = 0; i < rv; i++)
+ {
+ LOGD("%02X ", data[i]);
+ }
+
+ (*env).ReleaseStringUTFChars(operate, operator_native);
+ (*env).ReleaseStringUTFChars(channel, channel_native);
+ return 0;
+}
+
+extern "C"
+JNIEXPORT jstring JNICALL
+Java_com_example_serial_AdcControl_recvFromPort(JNIEnv *env, jclass clazz, jint len, jint timeout) {
+ // TODO: implement recvFromPort()
+ int rv = 0;
+ int iRet;
+ fd_set rdfds, exfds;
+ struct timeval stTime;
+ unsigned char nativeMsg[len];
+
+ memset(nativeMsg, 0, len);
+
+ if( !adc_comport || len <= 0 )
+ {
+ LOGE("Invalid parameter.\n");
+ return nullptr;
+ }
+ if (adc_comport->fd == -1)
+ {
+ LOGE("Serail not connected.\n");
+ return nullptr;
+ }
+
+ FD_ZERO(&rdfds);
+ FD_ZERO(&exfds);
+ FD_SET(adc_comport->fd, &rdfds);
+ FD_SET(adc_comport->fd, &exfds);
+
+ if (0xFFFFFFFF != timeout)
+ {
+ stTime.tv_sec = (time_t) (timeout / 1000);
+ stTime.tv_usec = (long)(1000 * (timeout % 1000));
+
+ iRet = select(adc_comport->fd + 1, &rdfds, 0, &exfds, &stTime);
+ if (0 == iRet)
+ {
+ return nullptr;
+ }
+ else if (0 < iRet)
+ {
+ if (0 != FD_ISSET(adc_comport->fd, &exfds))
+ {
+ LOGE("Error checking recv status.\n");
+ return nullptr;
+ }
+
+ if (0 == FD_ISSET(adc_comport->fd, &rdfds))
+ {
+ LOGE("No incoming data.\n");
+ return nullptr;
+ }
+ }
+ else
+ {
+ if (EINTR == errno)
+ {
+ LOGE("catch interrupt signal.\n");
+ }
+ else
+ {
+ LOGE("Check recv status failure.\n");
+ }
+ return nullptr;
+ }
+ }
+
+ usleep(10000); /* sleep for 10ms for data incoming */
+
+ // Get data from Comport
+ LOGD("start read");
+ iRet = read(adc_comport->fd, nativeMsg, len);
+ if (0 > iRet)
+ {
+ return nullptr;
+ }
+ LOGD("Receive {%d} bytes data successfully\n", iRet);
+ for(int i=0; i<iRet; i++)
+ {
+ LOGD("%02X", nativeMsg[i]);
+ }
+
+ char *tempHex = (char *) malloc(iRet * 3);
+ char *recvHex = (char *) malloc(iRet * 3 + 12);
+ if (tempHex == NULL || recvHex == NULL)
+ {
+ LOGE("Memory allocation failed: %s", strerror(errno));
+ return nullptr;
+ }
+ memset(tempHex, 0, sizeof (tempHex));
+ memset(recvHex, 0, sizeof (recvHex));
+ for (int i = 0; i < iRet; i++) {
+ sprintf(tempHex + i * 3, "%02X ", nativeMsg[i]);
+ }
+ strcat(recvHex, "Hex result: ");
+ strcat(recvHex, tempHex);
+
+ char recvalue[512];
+ memset(recvalue, 0, sizeof (recvalue));
+ calculate(recvalue, nativeMsg, iRet);
+ strcat(recvalue, recvHex);
+ LOGD("%s", recvalue);
+ return env->NewStringUTF(recvalue);
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp b/app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp
new file mode 100644
index 0000000..92ff0b7
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp
@@ -0,0 +1,111 @@
+#include <jni.h>
+#include <string>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <android/log.h>
+
+#define TAG "BuzzerApp"
+#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
+#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
+#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
+
+static char pwm_path[100];
+
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_BuzzerControl_pwmOpen(JNIEnv *env, jclass clazz, jstring id) {
+ // TODO: implement pwmOpen()
+ char *id_native;
+ char temp[100];
+ int fd;
+ id_native = (char *)env->GetStringUTFChars(id, 0);
+ memset(pwm_path, 0, sizeof (pwm_path));
+ snprintf(pwm_path, sizeof (pwm_path), "/sys/class/pwm/pwmchip%s/pwm0", id_native);
+
+ memset(temp, 0, sizeof (temp));
+ if(access(pwm_path, F_OK))
+ {
+ LOGE("pwm_path:%s, access=%d", pwm_path, access(pwm_path, F_OK));
+ snprintf(temp, sizeof (temp), "/sys/class/pwm/pwmchip%s/export", id_native);
+ fd = open(temp, O_WRONLY);
+ if(fd < 0)
+ {
+ if( strstr(strerror(errno), "Permission denied") != nullptr )
+ {
+ LOGE("open %s error:%s\n", temp, strerror(errno));
+ return 1;
+ }
+ LOGE("open %s error:%s\n", temp, strerror(errno));
+ return -1;
+ }
+
+ if(1 != write(fd, "0", 1))
+ {
+ LOGE("Write '0' to pwmchip%s/export error\n", id_native);
+ close(fd);
+ return -2;
+ }
+ close(fd);
+ }
+ env->ReleaseStringUTFChars(id, id_native);
+ return 0;
+}
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_BuzzerControl_pwmConfig(JNIEnv *env, jclass clazz, jstring attr,
+ jstring val) {
+ // TODO: implement pwmConfig()
+ char file_path[100];
+ char *attr_native;
+ char *val_native;
+ int len;
+ int fd = -1;
+ int ret;
+
+ if(attr == NULL || val == NULL)
+ {
+ LOGE("argument invalid\n");
+ return -1;
+ }
+
+ attr_native = (char *)env->GetStringUTFChars(attr, 0);
+ val_native = (char *)env->GetStringUTFChars(val, 0);
+
+ memset(file_path, 0, sizeof(file_path));
+ snprintf(file_path, sizeof (file_path), "%s/%s", pwm_path, attr_native);
+ fd = open(file_path, O_WRONLY);
+ if (fd < 0)
+ {
+ if( strstr(strerror(errno), "Permission denied") != nullptr )
+ {
+ LOGE("open %s error:%s\n", file_path, strerror(errno));
+ fd=1;
+ } // 表示缺少权限
+ LOGE("[%s] open %s error:%s\n", __FUNCTION__ , file_path, strerror(errno));
+ return fd;
+ }
+
+ len = strlen(val_native);
+ if(len != write(fd, val_native, len))
+ {
+ if ( strstr(strerror(errno), "Invalid") != nullptr)
+ {
+ close(fd);
+ return 2; //表示输入的参数无效
+ }
+ LOGE("[%s] write %s to %s error: %s\n", __FUNCTION__ , val_native, file_path, strerror(errno));
+ close(fd);
+ return -2;
+ }
+
+ close(fd);
+ env->ReleaseStringUTFChars(attr,attr_native);
+ env->ReleaseStringUTFChars(val, val_native);
+ return 0;
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/cpp/can_control.cpp b/app_sdk/app_src/app/src/main/cpp/can_control.cpp
new file mode 100644
index 0000000..f81ffe5
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/cpp/can_control.cpp
@@ -0,0 +1,193 @@
+#include <jni.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <linux/can.h>
+#include <linux/can/raw.h>
+#include <android/log.h>
+#include <fcntl.h>
+
+#define TAG "CanControl"
+#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
+#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
+#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
+
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_CanControl_sendMessage(JNIEnv *env, jclass clazz, jstring id, jstring dlc,
+ jstring data, jstring ifname) {
+ // TODO: implement sendMessage()
+ int fd;
+ int i;
+ struct sockaddr_can addr;
+ struct ifreq ifr;
+ struct can_frame frame;
+ unsigned int byte;
+ char *token;
+
+ const char *sendId = env->GetStringUTFChars(id, 0);
+ const char *sendDlc = env->GetStringUTFChars(dlc, 0);
+ const char *sendMsg = env->GetStringUTFChars(data, 0);
+ const char *if_name = env->GetStringUTFChars(ifname, 0);
+ if ( sendId == nullptr || sendDlc == nullptr || sendMsg == nullptr || if_name == nullptr)
+ {
+ LOGE("Invalid parameter");
+ return -1;
+ }
+
+ // create socket
+ fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
+ if(fd < 0)
+ {
+ LOGE("create can socket failed:%s\n", strerror(errno));
+ return -2;
+ }
+
+ strcpy(ifr.ifr_name, if_name);
+ ioctl(fd, SIOCGIFINDEX, &ifr);
+ addr.can_family = AF_CAN;
+ addr.can_ifindex = ifr.ifr_ifindex;
+
+ if (bind(fd, (struct sockaddr*)&addr, sizeof (addr)) < 0)
+ {
+ LOGE("bind socket failed:%s\n", strerror(errno));
+ return -3;
+ }
+ sscanf(sendId, "%X", &byte);
+ frame.can_id = byte;
+ sscanf(sendDlc, "%u", &byte);
+ frame.can_dlc = byte;
+ token = strtok((char *)sendMsg, " ");
+ for (int i = 0; i < frame.can_dlc && token != NULL; ++i)
+ {
+ unsigned int data_byte;
+ sscanf(token, "%02X", &data_byte);
+ frame.data[i] = (unsigned char)data_byte;
+ token = strtok(NULL, " ");
+ }
+
+ if (write(fd, &frame, sizeof (struct can_frame)) != sizeof (struct can_frame))
+ {
+ LOGE("write data failed:%s\n", strerror(errno));
+ return -4;
+ }
+
+ LOGD("Send CAN frame: ID=0x%X DLC=%d data=", frame.can_id, frame.can_dlc);
+ for(int i = 0; i < frame.can_dlc; i++)
+ {
+ LOGD("%02X ", frame.data[i]);
+ }
+ printf("\n");
+
+ close(fd);
+ env->ReleaseStringUTFChars(id, sendId);
+ env->ReleaseStringUTFChars(dlc, sendDlc);
+ env->ReleaseStringUTFChars(data, sendMsg);
+ env->ReleaseStringUTFChars(ifname, if_name);
+ return 0;
+}
+extern "C"
+JNIEXPORT jstring JNICALL
+Java_com_example_serial_CanControl_receiveMessage(JNIEnv *env, jclass clazz, jstring ifname) {
+ // TODO: implement receiveMessage()
+ int fd;
+ int rv;
+ struct sockaddr_can addr;
+ struct ifreq ifr;
+ struct can_frame frame;
+ char recvMsg[128];
+ char buf[4];
+
+ const char *if_name = env->GetStringUTFChars(ifname, 0);
+ if (if_name == nullptr)
+ {
+ LOGE("Invalid parameter");
+ return nullptr;
+ }
+
+ fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
+ if(fd < 0)
+ {
+ LOGE("create can socket failed:%s\n", strerror(errno));
+ return nullptr;
+ }
+ strcpy(ifr.ifr_name, if_name);
+ ioctl(fd, SIOCGIFINDEX, &ifr);
+ addr.can_family = AF_CAN;
+ addr.can_ifindex = ifr.ifr_ifindex;
+
+ if (bind(fd, (struct sockaddr*)&addr, sizeof (addr)) < 0)
+ {
+ LOGE("bind socket failed:%s\n", strerror(errno));
+ return nullptr;
+ }
+
+ rv = read(fd, &frame, sizeof (struct can_frame));
+ if (rv < 0)
+ {
+ LOGE("read data failed:%s\n", strerror(errno));
+ return nullptr;
+ } else if (rv < sizeof (struct can_frame))
+ {
+ LOGE("read incomplete CAN frame\n");
+ return nullptr;
+ }
+ memset(recvMsg, 0, sizeof (recvMsg));
+ sprintf(recvMsg, "ID=%X DLC=%d Data=", frame.can_id, frame.can_dlc);
+ for (int i = 0; i < frame.can_dlc; ++i) {
+ memset(buf, 0, sizeof (buf));
+ sprintf(buf, "%02X ", frame.data[i]);
+ strcat(recvMsg, buf);
+ }
+ LOGD("recived: %s", recvMsg);
+ close(fd);
+ env->ReleaseStringUTFChars(ifname, if_name);
+ return env->NewStringUTF(recvMsg);
+}
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_CanControl_openCan(JNIEnv *env, jclass clazz, jstring ifname,
+ jstring bitrate) {
+ // TODO: implement openCan()
+ char *ifname_native;
+ char *bitrate_native;
+ char command[128];
+
+ ifname_native = (char *)env->GetStringUTFChars(ifname, 0);
+ bitrate_native = (char *)env->GetStringUTFChars(bitrate, 0);
+
+ memset(command, 0, sizeof (command));
+ snprintf(command, sizeof (command), "ip link set %s down", ifname_native);
+ if( system(command) == -1)
+ {
+ LOGE("system %s failure:%s\n", command, strerror(errno));
+ return -1;
+ }
+ LOGD("system %s success\n", command);
+
+ memset(command, 0, sizeof (command));
+ snprintf(command, sizeof(command), "ip link set %s up type can bitrate %d", ifname_native, bitrate_native);
+ if( system(command) == -1)
+ {
+ LOGE("system %s failure:%s\n", command, strerror(errno));
+ return -1;
+ }
+ LOGD("system %s success\n", command);
+
+ memset(command, 0, sizeof (command));
+ snprintf(command, sizeof(command), "ip link set %s up type can", ifname_native);
+ if( system(command) == -1)
+ {
+ LOGE("system %s failure:%s\n", command, strerror(errno));
+ return -1;
+ }
+ LOGD("system %s success\n", command);
+ env->ReleaseStringUTFChars(ifname, ifname_native);
+ env->ReleaseStringUTFChars(bitrate, bitrate_native);
+ return 0;
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/cpp/include/gpiod.h b/app_sdk/app_src/app/src/main/cpp/include/gpiod.h
new file mode 100644
index 0000000..3477f9d
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/cpp/include/gpiod.h
@@ -0,0 +1,1775 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+#ifndef __LIBGPIOD_GPIOD_H__
+#define __LIBGPIOD_GPIOD_H__
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file gpiod.h
+ */
+
+/**
+ * @mainpage libgpiod public API
+ *
+ * This is the complete documentation of the public API made available to
+ * users of libgpiod.
+ *
+ * <p>The public header is logically split into two high-level parts: the
+ * simple API and the low-level API. The former allows users to easily
+ * interact with the GPIOs in the system without dealing with the low-level
+ * data structures and resource control. The latter gives the user much more
+ * fine-grained control over the GPIO interface.
+ *
+ * <p>The low-level API is further logically split into several parts such
+ * as: GPIO chip & line operators, iterators, GPIO events handling etc.
+ *
+ * <p>General note on error handling: all routines exported by libgpiod set
+ * errno to one of the error values defined in errno.h upon failure. The way
+ * of notifying the caller that an error occurred varies between functions,
+ * but in general a function that returns an int, returns -1 on error, while
+ * a function returning a pointer bails out on error condition by returning
+ * a NULL pointer.
+ */
+
+struct gpiod_chip;
+struct gpiod_line;
+struct gpiod_chip_iter;
+struct gpiod_line_iter;
+struct gpiod_line_bulk;
+
+/**
+ * @defgroup common Common helper macros
+ * @{
+ *
+ * Commonly used utility macros.
+ */
+
+/**
+ * @brief Makes symbol visible.
+ */
+#define GPIOD_API __attribute__((visibility("default")))
+
+/**
+ * @brief Marks a function argument or variable as potentially unused.
+ */
+#define GPIOD_UNUSED __attribute__((unused))
+
+/**
+ * @brief Shift 1 by given offset.
+ * @param nr Bit position.
+ * @return 1 shifted by nr.
+ */
+#define GPIOD_BIT(nr) (1UL << (nr))
+
+/**
+ * @brief Marks a public function as deprecated.
+ */
+#define GPIOD_DEPRECATED __attribute__((deprecated))
+
+/**
+ * @}
+ *
+ * @defgroup high_level High-level API
+ * @{
+ *
+ * Simple high-level routines for straightforward GPIO manipulation without
+ * the need to use the gpiod_* structures or to keep track of resources.
+ */
+
+/**
+ * @brief Miscellaneous GPIO flags.
+ */
+enum {
+ GPIOD_CTXLESS_FLAG_OPEN_DRAIN = GPIOD_BIT(0),
+ /**< The line is an open-drain port. */
+ GPIOD_CTXLESS_FLAG_OPEN_SOURCE = GPIOD_BIT(1),
+ /**< The line is an open-source port. */
+ GPIOD_CTXLESS_FLAG_BIAS_DISABLE = GPIOD_BIT(2),
+ /**< The line has neither either pull-up nor pull-down resistor */
+ GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN = GPIOD_BIT(3),
+ /**< The line has pull-down resistor enabled */
+ GPIOD_CTXLESS_FLAG_BIAS_PULL_UP = GPIOD_BIT(4),
+ /**< The line has pull-up resistor enabled */
+};
+
+/**
+ * @brief Read current value from a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offset Offset of the GPIO line.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @return 0 or 1 (GPIO value) if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_get_value(const char *device, unsigned int offset,
+ bool active_low, const char *consumer) GPIOD_API;
+
+/**
+ * @brief Read current value from a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offset Offset of the GPIO line.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param flags The flags for the line.
+ * @return 0 or 1 (GPIO value) if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_get_value_ext(const char *device, unsigned int offset,
+ bool active_low, const char *consumer,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Read current values from a set of GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offsets Array of offsets of lines whose values should be read.
+ * @param values Buffer in which the values will be stored.
+ * @param num_lines Number of lines, must be > 0.
+ * @param active_low The active state of the lines - true if low.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_get_value_multiple(const char *device,
+ const unsigned int *offsets, int *values,
+ unsigned int num_lines, bool active_low,
+ const char *consumer) GPIOD_API;
+
+/**
+ * @brief Read current values from a set of GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offsets Array of offsets of lines whose values should be read.
+ * @param values Buffer in which the values will be stored.
+ * @param num_lines Number of lines, must be > 0.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param flags The flags for the lines.
+ * @return 0 if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_get_value_multiple_ext(const char *device,
+ const unsigned int *offsets,
+ int *values, unsigned int num_lines,
+ bool active_low, const char *consumer,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Simple set value callback signature.
+ */
+typedef void (*gpiod_ctxless_set_value_cb)(void *);
+
+/**
+ * @brief Set value of a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offset The offset of the GPIO line.
+ * @param value New value (0 or 1).
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param cb Optional callback function that will be called right after setting
+ * the value. Users can use this, for example, to pause the execution
+ * after toggling a GPIO.
+ * @param data Optional user data that will be passed to the callback function.
+ * @return 0 if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value,
+ bool active_low, const char *consumer,
+ gpiod_ctxless_set_value_cb cb,
+ void *data) GPIOD_API;
+
+/**
+ * @brief Set value of a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offset The offset of the GPIO line.
+ * @param value New value (0 or 1).
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param cb Optional callback function that will be called right after setting
+ * the value. Users can use this, for example, to pause the execution
+ * after toggling a GPIO.
+ * @param data Optional user data that will be passed to the callback function.
+ * @param flags The flags for the line.
+ * @return 0 if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_set_value_ext(const char *device, unsigned int offset,
+ int value, bool active_low,
+ const char *consumer,
+ gpiod_ctxless_set_value_cb cb,
+ void *data, int flags) GPIOD_API;
+
+/**
+ * @brief Set values of multiple GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offsets Array of offsets of lines the values of which should be set.
+ * @param values Array of integers containing new values.
+ * @param num_lines Number of lines, must be > 0.
+ * @param active_low The active state of the lines - true if low.
+ * @param consumer Name of the consumer.
+ * @param cb Optional callback function that will be called right after setting
+ * all values. Works the same as in ::gpiod_ctxless_set_value.
+ * @param data Optional user data that will be passed to the callback function.
+ * @return 0 if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_set_value_multiple(const char *device,
+ const unsigned int *offsets,
+ const int *values, unsigned int num_lines,
+ bool active_low, const char *consumer,
+ gpiod_ctxless_set_value_cb cb,
+ void *data) GPIOD_API;
+
+/**
+ * @brief Set values of multiple GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offsets Array of offsets of lines the values of which should be set.
+ * @param values Array of integers containing new values.
+ * @param num_lines Number of lines, must be > 0.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param cb Optional callback function that will be called right after setting
+ * all values. Works the same as in ::gpiod_ctxless_set_value.
+ * @param data Optional user data that will be passed to the callback function.
+ * @param flags The flags for the lines.
+ * @return 0 if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_set_value_multiple_ext(const char *device,
+ const unsigned int *offsets,
+ const int *values,
+ unsigned int num_lines,
+ bool active_low,
+ const char *consumer,
+ gpiod_ctxless_set_value_cb cb,
+ void *data, int flags) GPIOD_API;
+
+/**
+ * @brief Event types that the ctxless event monitor can wait for.
+ */
+enum {
+ /**< Wait for rising edge events only. */
+ GPIOD_CTXLESS_EVENT_RISING_EDGE = 1,
+ /**< Wait for falling edge events only. */
+ GPIOD_CTXLESS_EVENT_FALLING_EDGE,
+ /**< Wait for both types of events. */
+ GPIOD_CTXLESS_EVENT_BOTH_EDGES,
+};
+
+/**
+ * @brief Event types that can be passed to the ctxless event callback.
+ */
+enum {
+ GPIOD_CTXLESS_EVENT_CB_TIMEOUT = 1,
+ /**< Waiting for events timed out. */
+ GPIOD_CTXLESS_EVENT_CB_RISING_EDGE,
+ /**< Rising edge event occured. */
+ GPIOD_CTXLESS_EVENT_CB_FALLING_EDGE,
+ /**< Falling edge event occured. */
+};
+
+/**
+ * @brief Return status values that the ctxless event callback can return.
+ */
+enum {
+ GPIOD_CTXLESS_EVENT_CB_RET_ERR = -1,
+ /**< Stop processing events and indicate an error. */
+ GPIOD_CTXLESS_EVENT_CB_RET_OK = 0,
+ /**< Continue processing events. */
+ GPIOD_CTXLESS_EVENT_CB_RET_STOP = 1,
+ /**< Stop processing events. */
+};
+
+/**
+ * @brief Simple event callback signature.
+ *
+ * The callback function takes the following arguments: event type (int),
+ * GPIO line offset (unsigned int), event timestamp (const struct timespec *)
+ * and a pointer to user data (void *).
+ *
+ * This callback is called by the ctxless event loop functions for each GPIO
+ * event. If the callback returns ::GPIOD_CTXLESS_EVENT_CB_RET_ERR, it should
+ * also set errno.
+ */
+typedef int (*gpiod_ctxless_event_handle_cb)(int, unsigned int,
+ const struct timespec *, void *);
+
+/**
+ * @brief Return status values that the ctxless event poll callback can return.
+ *
+ * Positive value returned from the polling callback indicates the number of
+ * events that occurred on the set of monitored lines.
+ */
+enum {
+ GPIOD_CTXLESS_EVENT_POLL_RET_STOP = -2,
+ /**< The event loop should stop processing events. */
+ GPIOD_CTXLESS_EVENT_POLL_RET_ERR = -1,
+ /**< Polling error occurred (the polling function should set errno). */
+ GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT = 0,
+ /**< Poll timed out. */
+};
+
+/**
+ * @brief Helper structure for the ctxless event loop poll callback.
+ */
+struct gpiod_ctxless_event_poll_fd {
+ int fd;
+ /**< File descriptor number. */
+ bool event;
+ /**< Indicates whether an event occurred on this file descriptor. */
+};
+
+/**
+ * @brief Simple event poll callback signature.
+ *
+ * The poll callback function takes the following arguments: number of lines
+ * (unsigned int), an array of file descriptors on which input events should
+ * be monitored (struct gpiod_ctxless_event_poll_fd *), poll timeout
+ * (const struct timespec *) and a pointer to user data (void *).
+ *
+ * The callback should poll for input events on the set of descriptors and
+ * return an appropriate value that can be interpreted by the event loop
+ * routine.
+ */
+typedef int (*gpiod_ctxless_event_poll_cb)(unsigned int,
+ struct gpiod_ctxless_event_poll_fd *,
+ const struct timespec *, void *);
+
+/**
+ * @brief Wait for events on a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offset GPIO line offset to monitor.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param timeout Maximum wait time for each iteration.
+ * @param poll_cb Callback function to call when waiting for events.
+ * @param event_cb Callback function to call for each line event.
+ * @param data User data passed to the callback.
+ * @return 0 if no errors were encountered, -1 if an error occurred.
+ * @note The way the ctxless event loop works is described in detail in
+ * ::gpiod_ctxless_event_loop_multiple - this is just a wrapper aound
+ * this routine which calls it for a single GPIO line.
+ * @deprecated This function suffers from an issue where HW may not allow
+ * setting up both rising and falling egde interrupts at the same
+ * time.
+ */
+int gpiod_ctxless_event_loop(const char *device, unsigned int offset,
+ bool active_low, const char *consumer,
+ const struct timespec *timeout,
+ gpiod_ctxless_event_poll_cb poll_cb,
+ gpiod_ctxless_event_handle_cb event_cb,
+ void *data) GPIOD_API GPIOD_DEPRECATED;
+
+/**
+ * @brief Wait for events on multiple GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offsets Array of GPIO line offsets to monitor.
+ * @param num_lines Number of lines to monitor.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param timeout Maximum wait time for each iteration.
+ * @param poll_cb Callback function to call when waiting for events. Can
+ * be NULL.
+ * @param event_cb Callback function to call on event occurrence.
+ * @param data User data passed to the callback.
+ * @return 0 no errors were encountered, -1 if an error occurred.
+ * @note The poll callback can be NULL in which case the routine will fall
+ * back to a basic, ppoll() based callback.
+ * @deprecated This function suffers from an issue where HW may not allow
+ * setting up both rising and falling egde interrupts at the same
+ * time.
+ *
+ * Internally this routine opens the GPIO chip, requests the set of lines for
+ * both-edges events and calls the polling callback in a loop. The role of the
+ * polling callback is to detect input events on a set of file descriptors and
+ * notify the caller about the fds ready for reading.
+ *
+ * The ctxless event loop then reads each queued event from marked descriptors
+ * and calls the event callback. Both callbacks can stop the loop at any
+ * point.
+ *
+ * The poll_cb argument can be NULL in which case the function falls back to
+ * a default, ppoll() based callback.
+ */
+int gpiod_ctxless_event_loop_multiple(const char *device,
+ const unsigned int *offsets,
+ unsigned int num_lines, bool active_low,
+ const char *consumer,
+ const struct timespec *timeout,
+ gpiod_ctxless_event_poll_cb poll_cb,
+ gpiod_ctxless_event_handle_cb event_cb,
+ void *data) GPIOD_API GPIOD_DEPRECATED;
+
+/**
+ * @brief Wait for events on a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param event_type Type of events to listen for.
+ * @param offset GPIO line offset to monitor.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param timeout Maximum wait time for each iteration.
+ * @param poll_cb Callback function to call when waiting for events.
+ * @param event_cb Callback function to call for each line event.
+ * @param data User data passed to the callback.
+ * @return 0 if no errors were encountered, -1 if an error occurred.
+ * @note The way the ctxless event loop works is described in detail in
+ * ::gpiod_ctxless_event_monitor_multiple - this is just a wrapper aound
+ * this routine which calls it for a single GPIO line.
+ */
+int gpiod_ctxless_event_monitor(const char *device, int event_type,
+ unsigned int offset, bool active_low,
+ const char *consumer,
+ const struct timespec *timeout,
+ gpiod_ctxless_event_poll_cb poll_cb,
+ gpiod_ctxless_event_handle_cb event_cb,
+ void *data) GPIOD_API;
+
+/**
+ * @brief Wait for events on a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param event_type Type of events to listen for.
+ * @param offset GPIO line offset to monitor.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param timeout Maximum wait time for each iteration.
+ * @param poll_cb Callback function to call when waiting for events.
+ * @param event_cb Callback function to call for each line event.
+ * @param data User data passed to the callback.
+ * @param flags The flags for the line.
+ * @return 0 if no errors were encountered, -1 if an error occurred.
+ * @note The way the ctxless event loop works is described in detail in
+ * ::gpiod_ctxless_event_monitor_multiple - this is just a wrapper aound
+ * this routine which calls it for a single GPIO line.
+ */
+int gpiod_ctxless_event_monitor_ext(const char *device, int event_type,
+ unsigned int offset, bool active_low,
+ const char *consumer,
+ const struct timespec *timeout,
+ gpiod_ctxless_event_poll_cb poll_cb,
+ gpiod_ctxless_event_handle_cb event_cb,
+ void *data, int flags) GPIOD_API;
+
+/**
+ * @brief Wait for events on multiple GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param event_type Type of events to listen for.
+ * @param offsets Array of GPIO line offsets to monitor.
+ * @param num_lines Number of lines to monitor.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param timeout Maximum wait time for each iteration.
+ * @param poll_cb Callback function to call when waiting for events. Can
+ * be NULL.
+ * @param event_cb Callback function to call on event occurrence.
+ * @param data User data passed to the callback.
+ * @return 0 no errors were encountered, -1 if an error occurred.
+ * @note The poll callback can be NULL in which case the routine will fall
+ * back to a basic, ppoll() based callback.
+ *
+ * Internally this routine opens the GPIO chip, requests the set of lines for
+ * the type of events specified in the event_type parameter and calls the
+ * polling callback in a loop. The role of the polling callback is to detect
+ * input events on a set of file descriptors and notify the caller about the
+ * fds ready for reading.
+ *
+ * The ctxless event loop then reads each queued event from marked descriptors
+ * and calls the event callback. Both callbacks can stop the loop at any
+ * point.
+ *
+ * The poll_cb argument can be NULL in which case the function falls back to
+ * a default, ppoll() based callback.
+ */
+int gpiod_ctxless_event_monitor_multiple(
+ const char *device, int event_type,
+ const unsigned int *offsets,
+ unsigned int num_lines, bool active_low,
+ const char *consumer, const struct timespec *timeout,
+ gpiod_ctxless_event_poll_cb poll_cb,
+ gpiod_ctxless_event_handle_cb event_cb,
+ void *data) GPIOD_API;
+
+/**
+ * @brief Wait for events on multiple GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param event_type Type of events to listen for.
+ * @param offsets Array of GPIO line offsets to monitor.
+ * @param num_lines Number of lines to monitor.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param timeout Maximum wait time for each iteration.
+ * @param poll_cb Callback function to call when waiting for events. Can
+ * be NULL.
+ * @param event_cb Callback function to call on event occurrence.
+ * @param data User data passed to the callback.
+ * @param flags The flags for the lines.
+ * @return 0 no errors were encountered, -1 if an error occurred.
+ * @note The poll callback can be NULL in which case the routine will fall
+ * back to a basic, ppoll() based callback.
+ *
+ * Internally this routine opens the GPIO chip, requests the set of lines for
+ * the type of events specified in the event_type parameter and calls the
+ * polling callback in a loop. The role of the polling callback is to detect
+ * input events on a set of file descriptors and notify the caller about the
+ * fds ready for reading.
+ *
+ * The ctxless event loop then reads each queued event from marked descriptors
+ * and calls the event callback. Both callbacks can stop the loop at any
+ * point.
+ *
+ * The poll_cb argument can be NULL in which case the function falls back to
+ * a default, ppoll() based callback.
+ */
+int gpiod_ctxless_event_monitor_multiple_ext(
+ const char *device, int event_type,
+ const unsigned int *offsets,
+ unsigned int num_lines, bool active_low,
+ const char *consumer, const struct timespec *timeout,
+ gpiod_ctxless_event_poll_cb poll_cb,
+ gpiod_ctxless_event_handle_cb event_cb,
+ void *data, int flags) GPIOD_API;
+
+
+/**
+ * @brief Determine the chip name and line offset of a line with given name.
+ * @param name The name of the GPIO line to lookup.
+ * @param chipname Buffer in which the name of the GPIO chip will be stored.
+ * @param chipname_size Size of the chip name buffer.
+ * @param offset Pointer to an integer in which the line offset will be stored.
+ * @return -1 on error, 0 if the line with given name doesn't exist and 1 if
+ * the line was found. In the first two cases the contents of chipname
+ * and offset remain unchanged.
+ * @note The chip name is truncated if the buffer can't hold its entire size.
+ * @attention GPIO line names are not unique in the linux kernel, neither
+ * globally nor within a single chip. This function finds the first
+ * line with given name.
+ */
+int gpiod_ctxless_find_line(const char *name, char *chipname,
+ size_t chipname_size,
+ unsigned int *offset) GPIOD_API;
+
+/**
+ * @}
+ *
+ * @defgroup chips GPIO chip operations
+ * @{
+ *
+ * Functions and data structures dealing with GPIO chips.
+ */
+
+/**
+ * @brief Open a gpiochip by path.
+ * @param path Path to the gpiochip device file.
+ * @return GPIO chip handle or NULL if an error occurred.
+ */
+struct gpiod_chip *gpiod_chip_open(const char *path) GPIOD_API;
+
+/**
+ * @brief Open a gpiochip by name.
+ * @param name Name of the gpiochip to open.
+ * @return GPIO chip handle or NULL if an error occurred.
+ *
+ * This routine appends name to '/dev/' to create the path.
+ */
+struct gpiod_chip *gpiod_chip_open_by_name(const char *name) GPIOD_API;
+
+/**
+ * @brief Open a gpiochip by number.
+ * @param num Number of the gpiochip.
+ * @return GPIO chip handle or NULL if an error occurred.
+ *
+ * This routine appends num to '/dev/gpiochip' to create the path.
+ */
+struct gpiod_chip *gpiod_chip_open_by_number(unsigned int num) GPIOD_API;
+
+/**
+ * @brief Open a gpiochip by label.
+ * @param label Label of the gpiochip to open.
+ * @return GPIO chip handle or NULL if the chip with given label was not found
+ * or an error occured.
+ * @note If the chip cannot be found but no other error occurred, errno is set
+ * to ENOENT.
+ */
+struct gpiod_chip *gpiod_chip_open_by_label(const char *label) GPIOD_API;
+
+/**
+ * @brief Open a gpiochip based on the best guess what the path is.
+ * @param descr String describing the gpiochip.
+ * @return GPIO chip handle or NULL if an error occurred.
+ *
+ * This routine tries to figure out whether the user passed it the path to the
+ * GPIO chip, its name, label or number as a string. Then it tries to open it
+ * using one of the gpiod_chip_open** variants.
+ */
+struct gpiod_chip *gpiod_chip_open_lookup(const char *descr) GPIOD_API;
+
+/**
+ * @brief Close a GPIO chip handle and release all allocated resources.
+ * @param chip The GPIO chip object.
+ */
+void gpiod_chip_close(struct gpiod_chip *chip) GPIOD_API;
+
+/**
+ * @brief Get the GPIO chip name as represented in the kernel.
+ * @param chip The GPIO chip object.
+ * @return Pointer to a human-readable string containing the chip name.
+ */
+const char *gpiod_chip_name(struct gpiod_chip *chip) GPIOD_API;
+
+/**
+ * @brief Get the GPIO chip label as represented in the kernel.
+ * @param chip The GPIO chip object.
+ * @return Pointer to a human-readable string containing the chip label.
+ */
+const char *gpiod_chip_label(struct gpiod_chip *chip) GPIOD_API;
+
+/**
+ * @brief Get the number of GPIO lines exposed by this chip.
+ * @param chip The GPIO chip object.
+ * @return Number of GPIO lines.
+ */
+unsigned int gpiod_chip_num_lines(struct gpiod_chip *chip) GPIOD_API;
+
+/**
+ * @brief Get the handle to the GPIO line at given offset.
+ * @param chip The GPIO chip object.
+ * @param offset The offset of the GPIO line.
+ * @return Pointer to the GPIO line handle or NULL if an error occured.
+ */
+struct gpiod_line *
+gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset) GPIOD_API;
+
+/**
+ * @brief Retrieve a set of lines and store them in a line bulk object.
+ * @param chip The GPIO chip object.
+ * @param offsets Array of offsets of lines to retrieve.
+ * @param num_offsets Number of lines to retrieve.
+ * @param bulk Line bulk object in which to store the line handles.
+ * @return 0 on success, -1 on error.
+ */
+int gpiod_chip_get_lines(struct gpiod_chip *chip,
+ unsigned int *offsets, unsigned int num_offsets,
+ struct gpiod_line_bulk *bulk) GPIOD_API;
+
+/**
+ * @brief Retrieve all lines exposed by a chip and store them in a bulk object.
+ * @param chip The GPIO chip object.
+ * @param bulk Line bulk object in which to store the line handles.
+ * @return 0 on success, -1 on error.
+ */
+int gpiod_chip_get_all_lines(struct gpiod_chip *chip,
+ struct gpiod_line_bulk *bulk) GPIOD_API;
+
+/**
+ * @brief Find a GPIO line by name among lines associated with given GPIO chip.
+ * @param chip The GPIO chip object.
+ * @param name The name of the GPIO line.
+ * @return Pointer to the GPIO line handle or NULL if the line could not be
+ * found or an error occurred.
+ * @note In case a line with given name is not associated with given chip, the
+ * function sets errno to ENOENT.
+ * @attention GPIO line names are not unique in the linux kernel, neither
+ * globally nor within a single chip. This function finds the first
+ * line with given name.
+ */
+struct gpiod_line *
+gpiod_chip_find_line(struct gpiod_chip *chip, const char *name) GPIOD_API;
+
+/**
+ * @brief Find a set of GPIO lines by names among lines exposed by this chip.
+ * @param chip The GPIO chip object.
+ * @param names Array of pointers to C-strings containing the names of the
+ * lines to lookup. Must end with a NULL-pointer.
+ * @param bulk Line bulk object in which the located lines will be stored.
+ * @return 0 if all lines were located, -1 on error.
+ * @note If at least one line from the list could not be found among the lines
+ * exposed by this chip, the function sets errno to ENOENT.
+ * @attention GPIO line names are not unique in the linux kernel, neither
+ * globally nor within a single chip. This function finds the first
+ * line with given name.
+ */
+int gpiod_chip_find_lines(struct gpiod_chip *chip, const char **names,
+ struct gpiod_line_bulk *bulk) GPIOD_API;
+
+/**
+ * @}
+ *
+ * @defgroup lines GPIO line operations
+ * @{
+ *
+ * Functions and data structures dealing with GPIO lines.
+ *
+ * @defgroup line_bulk Operating on multiple lines
+ * @{
+ *
+ * Convenience data structures and helper functions for storing and operating
+ * on multiple lines at once.
+ */
+
+/**
+ * @brief Maximum number of GPIO lines that can be requested at once.
+ */
+#define GPIOD_LINE_BULK_MAX_LINES 64
+
+/**
+ * @brief Helper structure for storing a set of GPIO line objects.
+ *
+ * This structure is used in all operations involving sets of GPIO lines. If
+ * a bulk object is being passed to a function while containing zero lines,
+ * the result is undefined.
+ */
+struct gpiod_line_bulk {
+ struct gpiod_line *lines[GPIOD_LINE_BULK_MAX_LINES];
+ /**< Buffer for line pointers. */
+ unsigned int num_lines;
+ /**< Number of lines currently held in this structure. */
+};
+
+/**
+ * @brief Static initializer for GPIO bulk objects.
+ *
+ * This macro simply sets the internally held number of lines to 0.
+ */
+#define GPIOD_LINE_BULK_INITIALIZER { { NULL }, 0 }
+
+/**
+ * @brief Initialize a GPIO bulk object.
+ * @param bulk Line bulk object.
+ *
+ * This routine simply sets the internally held number of lines to 0.
+ */
+static inline void gpiod_line_bulk_init(struct gpiod_line_bulk *bulk)
+{
+ bulk->num_lines = 0;
+}
+
+/**
+ * @brief Add a single line to a GPIO bulk object.
+ * @param bulk Line bulk object.
+ * @param line Line to add.
+ */
+static inline void gpiod_line_bulk_add(struct gpiod_line_bulk *bulk,
+ struct gpiod_line *line)
+{
+ bulk->lines[bulk->num_lines++] = line;
+}
+
+/**
+ * @brief Retrieve the line handle from a line bulk object at given offset.
+ * @param bulk Line bulk object.
+ * @param offset Line offset.
+ * @return Line handle at given offset.
+ */
+static inline struct gpiod_line *
+gpiod_line_bulk_get_line(struct gpiod_line_bulk *bulk, unsigned int offset)
+{
+ return bulk->lines[offset];
+}
+
+/**
+ * @brief Retrieve the number of GPIO lines held by this line bulk object.
+ * @param bulk Line bulk object.
+ * @return Number of lines held by this line bulk.
+ */
+static inline unsigned int
+gpiod_line_bulk_num_lines(struct gpiod_line_bulk *bulk)
+{
+ return bulk->num_lines;
+}
+
+/**
+ * @brief Iterate over all line handles held by a line bulk object.
+ * @param bulk Line bulk object.
+ * @param line GPIO line handle. On each iteration, the subsequent line handle
+ * is assigned to this pointer.
+ * @param lineptr Pointer to a GPIO line handle used to store the loop state.
+ */
+#define gpiod_line_bulk_foreach_line(bulk, line, lineptr) \
+ for ((lineptr) = (bulk)->lines, (line) = *(lineptr); \
+ (lineptr) <= (bulk)->lines + ((bulk)->num_lines - 1); \
+ (lineptr)++, (line) = *(lineptr))
+
+/**
+ * @brief Iterate over all line handles held by a line bulk object (integer
+ * counter variant).
+ * @param bulk Line bulk object.
+ * @param line GPIO line handle. On each iteration, the subsequent line handle
+ * is assigned to this pointer.
+ * @param offset An integer variable used to store the loop state.
+ *
+ * This is a variant of ::gpiod_line_bulk_foreach_line which uses an integer
+ * variable (either signed or unsigned) to store the loop state. This offset
+ * variable is guaranteed to correspond to the offset of the current line in
+ * the bulk->lines array.
+ */
+#define gpiod_line_bulk_foreach_line_off(bulk, line, offset) \
+ for ((offset) = 0, (line) = (bulk)->lines[0]; \
+ (offset) < (bulk)->num_lines; \
+ (offset)++, (line) = (bulk)->lines[(offset)])
+
+/**
+ * @}
+ *
+ * @defgroup line_info Line info
+ * @{
+ *
+ * Definitions and functions for retrieving kernel information about both
+ * requested and free lines.
+ */
+
+/**
+ * @brief Possible direction settings.
+ */
+enum {
+ GPIOD_LINE_DIRECTION_INPUT = 1,
+ /**< Direction is input - we're reading the state of a GPIO line. */
+ GPIOD_LINE_DIRECTION_OUTPUT,
+ /**< Direction is output - we're driving the GPIO line. */
+};
+
+/**
+ * @brief Possible active state settings.
+ */
+enum {
+ GPIOD_LINE_ACTIVE_STATE_HIGH = 1,
+ /**< The active state of a GPIO is active-high. */
+ GPIOD_LINE_ACTIVE_STATE_LOW,
+ /**< The active state of a GPIO is active-low. */
+};
+
+/**
+ * @brief Possible internal bias settings.
+ */
+enum {
+ GPIOD_LINE_BIAS_AS_IS = 1,
+ /**< The internal bias state is unknown. */
+ GPIOD_LINE_BIAS_DISABLE,
+ /**< The internal bias is disabled. */
+ GPIOD_LINE_BIAS_PULL_UP,
+ /**< The internal pull-up bias is enabled. */
+ GPIOD_LINE_BIAS_PULL_DOWN,
+ /**< The internal pull-down bias is enabled. */
+};
+
+/**
+ * @brief Read the GPIO line offset.
+ * @param line GPIO line object.
+ * @return Line offset.
+ */
+unsigned int gpiod_line_offset(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Read the GPIO line name.
+ * @param line GPIO line object.
+ * @return Name of the GPIO line as it is represented in the kernel. This
+ * routine returns a pointer to a null-terminated string or NULL if
+ * the line is unnamed.
+ */
+const char *gpiod_line_name(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Read the GPIO line consumer name.
+ * @param line GPIO line object.
+ * @return Name of the GPIO consumer name as it is represented in the
+ * kernel. This routine returns a pointer to a null-terminated string
+ * or NULL if the line is not used.
+ */
+const char *gpiod_line_consumer(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Read the GPIO line direction setting.
+ * @param line GPIO line object.
+ * @return Returns GPIOD_LINE_DIRECTION_INPUT or GPIOD_LINE_DIRECTION_OUTPUT.
+ */
+int gpiod_line_direction(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Read the GPIO line active state setting.
+ * @param line GPIO line object.
+ * @return Returns GPIOD_LINE_ACTIVE_STATE_HIGH or GPIOD_LINE_ACTIVE_STATE_LOW.
+ */
+int gpiod_line_active_state(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Read the GPIO line bias setting.
+ * @param line GPIO line object.
+ * @return Returns GPIOD_LINE_BIAS_PULL_UP, GPIOD_LINE_BIAS_PULL_DOWN,
+ * GPIOD_LINE_BIAS_DISABLE or GPIOD_LINE_BIAS_AS_IS.
+ */
+int gpiod_line_bias(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Check if the line is currently in use.
+ * @param line GPIO line object.
+ * @return True if the line is in use, false otherwise.
+ *
+ * The user space can't know exactly why a line is busy. It may have been
+ * requested by another process or hogged by the kernel. It only matters that
+ * the line is used and we can't request it.
+ */
+bool gpiod_line_is_used(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Check if the line is an open-drain GPIO.
+ * @param line GPIO line object.
+ * @return True if the line is an open-drain GPIO, false otherwise.
+ */
+bool gpiod_line_is_open_drain(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Check if the line is an open-source GPIO.
+ * @param line GPIO line object.
+ * @return True if the line is an open-source GPIO, false otherwise.
+ */
+bool gpiod_line_is_open_source(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Re-read the line info.
+ * @param line GPIO line object.
+ * @return 0 if the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ *
+ * The line info is initially retrieved from the kernel by
+ * gpiod_chip_get_line() and is later re-read after every successful request.
+ * Users can use this function to manually re-read the line info when needed.
+ *
+ * We currently have no mechanism provided by the kernel for keeping the line
+ * info synchronized and for the sake of speed and simplicity of this low-level
+ * library we don't want to re-read the line info automatically everytime
+ * a property is retrieved. Any daemon using this library must track the state
+ * of lines on its own and call this routine if needed.
+ *
+ * The state of requested lines is kept synchronized (or rather cannot be
+ * changed by external agents while the ownership of the line is taken) so
+ * there's no need to call this function in that case.
+ */
+int gpiod_line_update(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Check if the line info needs to be updated.
+ * @param line GPIO line object.
+ * @return Always returns false.
+ * @deprecated This mechanism no longer exists in the library and this function
+ * does nothing.
+ */
+bool
+gpiod_line_needs_update(struct gpiod_line *line) GPIOD_API GPIOD_DEPRECATED;
+
+/**
+ * @}
+ *
+ * @defgroup line_request Line requests
+ * @{
+ *
+ * Interface for requesting GPIO lines from userspace for both values and
+ * events.
+ */
+
+/**
+ * @brief Available types of requests.
+ */
+enum {
+ GPIOD_LINE_REQUEST_DIRECTION_AS_IS = 1,
+ /**< Request the line(s), but don't change current direction. */
+ GPIOD_LINE_REQUEST_DIRECTION_INPUT,
+ /**< Request the line(s) for reading the GPIO line state. */
+ GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
+ /**< Request the line(s) for setting the GPIO line state. */
+ GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE,
+ /**< Only watch falling edge events. */
+ GPIOD_LINE_REQUEST_EVENT_RISING_EDGE,
+ /**< Only watch rising edge events. */
+ GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES,
+ /**< Monitor both types of events. */
+};
+
+/**
+ * @brief Miscellaneous GPIO request flags.
+ */
+enum {
+ GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN = GPIOD_BIT(0),
+ /**< The line is an open-drain port. */
+ GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE = GPIOD_BIT(1),
+ /**< The line is an open-source port. */
+ GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW = GPIOD_BIT(2),
+ /**< The active state of the line is low (high is the default). */
+ GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE = GPIOD_BIT(3),
+ /**< The line has neither either pull-up nor pull-down resistor. */
+ GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN = GPIOD_BIT(4),
+ /**< The line has pull-down resistor enabled. */
+ GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP = GPIOD_BIT(5),
+ /**< The line has pull-up resistor enabled. */
+};
+
+/**
+ * @brief Structure holding configuration of a line request.
+ */
+struct gpiod_line_request_config {
+ const char *consumer;
+ /**< Name of the consumer. */
+ int request_type;
+ /**< Request type. */
+ int flags;
+ /**< Other configuration flags. */
+};
+
+/**
+ * @brief Reserve a single line.
+ * @param line GPIO line object.
+ * @param config Request options.
+ * @param default_val Initial line value - only relevant if we're setting
+ * the direction to output.
+ * @return 0 if the line was properly reserved. In case of an error this
+ * routine returns -1 and sets the last error number.
+ *
+ * If this routine succeeds, the caller takes ownership of the GPIO line until
+ * it's released.
+ */
+int gpiod_line_request(struct gpiod_line *line,
+ const struct gpiod_line_request_config *config,
+ int default_val) GPIOD_API;
+
+/**
+ * @brief Reserve a single line, set the direction to input.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @return 0 if the line was properly reserved, -1 on failure.
+ */
+int gpiod_line_request_input(struct gpiod_line *line,
+ const char *consumer) GPIOD_API;
+
+/**
+ * @brief Reserve a single line, set the direction to output.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param default_val Initial line value.
+ * @return 0 if the line was properly reserved, -1 on failure.
+ */
+int gpiod_line_request_output(struct gpiod_line *line,
+ const char *consumer, int default_val) GPIOD_API;
+
+/**
+ * @brief Request rising edge event notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_rising_edge_events(struct gpiod_line *line,
+ const char *consumer) GPIOD_API;
+
+/**
+ * @brief Request falling edge event notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_falling_edge_events(struct gpiod_line *line,
+ const char *consumer) GPIOD_API;
+
+/**
+ * @brief Request all event type notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_both_edges_events(struct gpiod_line *line,
+ const char *consumer) GPIOD_API;
+
+/**
+ * @brief Reserve a single line, set the direction to input.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the line was properly reserved, -1 on failure.
+ */
+int gpiod_line_request_input_flags(struct gpiod_line *line,
+ const char *consumer, int flags) GPIOD_API;
+
+/**
+ * @brief Reserve a single line, set the direction to output.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @param default_val Initial line value.
+ * @return 0 if the line was properly reserved, -1 on failure.
+ */
+int gpiod_line_request_output_flags(struct gpiod_line *line,
+ const char *consumer, int flags,
+ int default_val) GPIOD_API;
+
+/**
+ * @brief Request rising edge event notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_rising_edge_events_flags(struct gpiod_line *line,
+ const char *consumer,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Request falling edge event notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_falling_edge_events_flags(struct gpiod_line *line,
+ const char *consumer,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Request all event type notifications on a single line.
+ * @param line GPIO line object.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_both_edges_events_flags(struct gpiod_line *line,
+ const char *consumer,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Reserve a set of GPIO lines.
+ * @param bulk Set of GPIO lines to reserve.
+ * @param config Request options.
+ * @param default_vals Initial line values - only relevant if we're setting
+ * the direction to output.
+ * @return 0 if the all lines were properly requested. In case of an error
+ * this routine returns -1 and sets the last error number.
+ *
+ * If this routine succeeds, the caller takes ownership of the GPIO lines
+ * until they're released. All the requested lines must be prodivided by the
+ * same gpiochip.
+ */
+int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk,
+ const struct gpiod_line_request_config *config,
+ const int *default_vals) GPIOD_API;
+
+/**
+ * @brief Reserve a set of GPIO lines, set the direction to input.
+ * @param bulk Set of GPIO lines to reserve.
+ * @param consumer Name of the consumer.
+ * @return 0 if the lines were properly reserved, -1 on failure.
+ */
+int gpiod_line_request_bulk_input(struct gpiod_line_bulk *bulk,
+ const char *consumer) GPIOD_API;
+
+/**
+ * @brief Reserve a set of GPIO lines, set the direction to output.
+ * @param bulk Set of GPIO lines to reserve.
+ * @param consumer Name of the consumer.
+ * @param default_vals Initial line values.
+ * @return 0 if the lines were properly reserved, -1 on failure.
+ */
+int gpiod_line_request_bulk_output(struct gpiod_line_bulk *bulk,
+ const char *consumer,
+ const int *default_vals) GPIOD_API;
+
+/**
+ * @brief Request rising edge event notifications on a set of lines.
+ * @param bulk Set of GPIO lines to request.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_bulk_rising_edge_events(struct gpiod_line_bulk *bulk,
+ const char *consumer) GPIOD_API;
+
+/**
+ * @brief Request falling edge event notifications on a set of lines.
+ * @param bulk Set of GPIO lines to request.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_bulk_falling_edge_events(struct gpiod_line_bulk *bulk,
+ const char *consumer) GPIOD_API;
+
+/**
+ * @brief Request all event type notifications on a set of lines.
+ * @param bulk Set of GPIO lines to request.
+ * @param consumer Name of the consumer.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_bulk_both_edges_events(struct gpiod_line_bulk *bulk,
+ const char *consumer) GPIOD_API;
+
+/**
+ * @brief Reserve a set of GPIO lines, set the direction to input.
+ * @param bulk Set of GPIO lines to reserve.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the lines were properly reserved, -1 on failure.
+ */
+int gpiod_line_request_bulk_input_flags(struct gpiod_line_bulk *bulk,
+ const char *consumer,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Reserve a set of GPIO lines, set the direction to output.
+ * @param bulk Set of GPIO lines to reserve.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @param default_vals Initial line values.
+ * @return 0 if the lines were properly reserved, -1 on failure.
+ */
+int gpiod_line_request_bulk_output_flags(struct gpiod_line_bulk *bulk,
+ const char *consumer, int flags,
+ const int *default_vals) GPIOD_API;
+
+/**
+ * @brief Request rising edge event notifications on a set of lines.
+ * @param bulk Set of GPIO lines to request.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_bulk_rising_edge_events_flags(
+ struct gpiod_line_bulk *bulk,
+ const char *consumer,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Request falling edge event notifications on a set of lines.
+ * @param bulk Set of GPIO lines to request.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_bulk_falling_edge_events_flags(
+ struct gpiod_line_bulk *bulk,
+ const char *consumer,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Request all event type notifications on a set of lines.
+ * @param bulk Set of GPIO lines to request.
+ * @param consumer Name of the consumer.
+ * @param flags Additional request flags.
+ * @return 0 if the operation succeeds, -1 on failure.
+ */
+int gpiod_line_request_bulk_both_edges_events_flags(
+ struct gpiod_line_bulk *bulk,
+ const char *consumer,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Release a previously reserved line.
+ * @param line GPIO line object.
+ */
+void gpiod_line_release(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Release a set of previously reserved lines.
+ * @param bulk Set of GPIO lines to release.
+ *
+ * If the lines were not previously requested together, the behavior is
+ * undefined.
+ */
+void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk) GPIOD_API;
+
+/**
+ * @brief Check if the calling user has ownership of this line.
+ * @param line GPIO line object.
+ * @return True if given line was requested, false otherwise.
+ */
+bool gpiod_line_is_requested(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Check if the calling user has neither requested ownership of this
+ * line nor configured any event notifications.
+ * @param line GPIO line object.
+ * @return True if given line is free, false otherwise.
+ */
+bool gpiod_line_is_free(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @}
+ *
+ * @defgroup line_value Reading & setting line values
+ * @{
+ *
+ * Functions allowing to read and set GPIO line values for single lines and
+ * in bulk.
+ */
+
+/**
+ * @brief Read current value of a single GPIO line.
+ * @param line GPIO line object.
+ * @return 0 or 1 if the operation succeeds. On error this routine returns -1
+ * and sets the last error number.
+ */
+int gpiod_line_get_value(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Read current values of a set of GPIO lines.
+ * @param bulk Set of GPIO lines to reserve.
+ * @param values An array big enough to hold line_bulk->num_lines values.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ *
+ * If succeeds, this routine fills the values array with a set of values in
+ * the same order, the lines are added to line_bulk. If the lines were not
+ * previously requested together, the behavior is undefined.
+ */
+int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk,
+ int *values) GPIOD_API;
+
+/**
+ * @brief Set the value of a single GPIO line.
+ * @param line GPIO line object.
+ * @param value New value.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+int gpiod_line_set_value(struct gpiod_line *line, int value) GPIOD_API;
+
+/**
+ * @brief Set the values of a set of GPIO lines.
+ * @param bulk Set of GPIO lines to reserve.
+ * @param values An array holding line_bulk->num_lines new values for lines.
+ * A NULL pointer is interpreted as a logical low for all lines.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ *
+ * If the lines were not previously requested together, the behavior is
+ * undefined.
+ */
+int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk,
+ const int *values) GPIOD_API;
+
+/**
+ * @}
+ *
+ * @defgroup line_config Setting line configuration
+ * @{
+ *
+ * Functions allowing modification of config options of GPIO lines requested
+ * from user-space.
+ */
+
+/**
+ * @brief Update the configuration of a single GPIO line.
+ * @param line GPIO line object.
+ * @param direction Updated direction which may be one of
+ * GPIOD_LINE_REQUEST_DIRECTION_AS_IS,
+ * GPIOD_LINE_REQUEST_DIRECTION_INPUT, or
+ * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT.
+ * @param flags Replacement flags.
+ * @param value The new output value for the line when direction is
+ * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+int gpiod_line_set_config(struct gpiod_line *line, int direction,
+ int flags, int value) GPIOD_API;
+
+/**
+ * @brief Update the configuration of a set of GPIO lines.
+ * @param bulk Set of GPIO lines.
+ * @param direction Updated direction which may be one of
+ * GPIOD_LINE_REQUEST_DIRECTION_AS_IS,
+ * GPIOD_LINE_REQUEST_DIRECTION_INPUT, or
+ * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT.
+ * @param flags Replacement flags.
+ * @param values An array holding line_bulk->num_lines new logical values
+ * for lines when direction is
+ * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT.
+ * A NULL pointer is interpreted as a logical low for all lines.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ *
+ * If the lines were not previously requested together, the behavior is
+ * undefined.
+ */
+int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk,
+ int direction, int flags,
+ const int *values) GPIOD_API;
+
+
+/**
+ * @brief Update the configuration flags of a single GPIO line.
+ * @param line GPIO line object.
+ * @param flags Replacement flags.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+int gpiod_line_set_flags(struct gpiod_line *line, int flags) GPIOD_API;
+
+/**
+ * @brief Update the configuration flags of a set of GPIO lines.
+ * @param bulk Set of GPIO lines.
+ * @param flags Replacement flags.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ *
+ * If the lines were not previously requested together, the behavior is
+ * undefined.
+ */
+int gpiod_line_set_flags_bulk(struct gpiod_line_bulk *bulk,
+ int flags) GPIOD_API;
+
+/**
+ * @brief Set the direction of a single GPIO line to input.
+ * @param line GPIO line object.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+int gpiod_line_set_direction_input(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Set the direction of a set of GPIO lines to input.
+ * @param bulk Set of GPIO lines.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ *
+ * If the lines were not previously requested together, the behavior is
+ * undefined.
+ */
+int
+gpiod_line_set_direction_input_bulk(struct gpiod_line_bulk *bulk) GPIOD_API;
+
+/**
+ * @brief Set the direction of a single GPIO line to output.
+ * @param line GPIO line object.
+ * @param value The logical value output on the line.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ */
+int gpiod_line_set_direction_output(struct gpiod_line *line,
+ int value) GPIOD_API;
+
+/**
+ * @brief Set the direction of a set of GPIO lines to output.
+ * @param bulk Set of GPIO lines.
+ * @param values An array holding line_bulk->num_lines new logical values
+ * for lines. A NULL pointer is interpreted as a logical low
+ * for all lines.
+ * @return 0 is the operation succeeds. In case of an error this routine
+ * returns -1 and sets the last error number.
+ *
+ * If the lines were not previously requested together, the behavior is
+ * undefined.
+ */
+int gpiod_line_set_direction_output_bulk(struct gpiod_line_bulk *bulk,
+ const int *values) GPIOD_API;
+
+/**
+ * @}
+ *
+ * @defgroup line_event Line events handling
+ * @{
+ *
+ * Structures and functions allowing to poll lines for events and read them,
+ * both for individual lines as well as in bulk. Also contains functions for
+ * retrieving the associated file descriptors and operate on them for easy
+ * integration with standard unix interfaces.
+ */
+
+/**
+ * @brief Event types.
+ */
+enum {
+ GPIOD_LINE_EVENT_RISING_EDGE = 1,
+ /**< Rising edge event. */
+ GPIOD_LINE_EVENT_FALLING_EDGE,
+ /**< Falling edge event. */
+};
+
+/**
+ * @brief Structure holding event info.
+ */
+struct gpiod_line_event {
+ struct timespec ts;
+ /**< Best estimate of time of event occurrence. */
+ int event_type;
+ /**< Type of the event that occurred. */
+};
+
+/**
+ * @brief Wait for an event on a single line.
+ * @param line GPIO line object.
+ * @param timeout Wait time limit.
+ * @return 0 if wait timed out, -1 if an error occurred, 1 if an event
+ * occurred.
+ */
+int gpiod_line_event_wait(struct gpiod_line *line,
+ const struct timespec *timeout) GPIOD_API;
+
+/**
+ * @brief Wait for events on a set of lines.
+ * @param bulk Set of GPIO lines to monitor.
+ * @param timeout Wait time limit.
+ * @param event_bulk Bulk object in which to store the line handles on which
+ * events occurred. Can be NULL.
+ * @return 0 if wait timed out, -1 if an error occurred, 1 if at least one
+ * event occurred.
+ */
+int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk,
+ const struct timespec *timeout,
+ struct gpiod_line_bulk *event_bulk) GPIOD_API;
+
+/**
+ * @brief Read next pending event from the GPIO line.
+ * @param line GPIO line object.
+ * @param event Buffer to which the event data will be copied.
+ * @return 0 if the event was read correctly, -1 on error.
+ * @note This function will block if no event was queued for this line.
+ */
+int gpiod_line_event_read(struct gpiod_line *line,
+ struct gpiod_line_event *event) GPIOD_API;
+
+/**
+ * @brief Read up to a certain number of events from the GPIO line.
+ * @param line GPIO line object.
+ * @param events Buffer to which the event data will be copied. Must hold at
+ * least the amount of events specified in num_events.
+ * @param num_events Specifies how many events can be stored in the buffer.
+ * @return On success returns the number of events stored in the buffer, on
+ * failure -1 is returned.
+ */
+int gpiod_line_event_read_multiple(struct gpiod_line *line,
+ struct gpiod_line_event *events,
+ unsigned int num_events) GPIOD_API;
+
+/**
+ * @brief Get the event file descriptor.
+ * @param line GPIO line object.
+ * @return Number of the event file descriptor or -1 if the user tries to
+ * retrieve the descriptor from a line that wasn't configured for
+ * event monitoring.
+ *
+ * Users may want to poll the event file descriptor on their own. This routine
+ * allows to access it.
+ */
+int gpiod_line_event_get_fd(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Read the last GPIO event directly from a file descriptor.
+ * @param fd File descriptor.
+ * @param event Buffer in which the event data will be stored.
+ * @return 0 if the event was read correctly, -1 on error.
+ *
+ * Users who directly poll the file descriptor for incoming events can also
+ * directly read the event data from it using this routine. This function
+ * translates the kernel representation of the event to the libgpiod format.
+ */
+int gpiod_line_event_read_fd(int fd, struct gpiod_line_event *event) GPIOD_API;
+
+/**
+ * @brief Read up to a certain number of events directly from a file descriptor.
+ * @param fd File descriptor.
+ * @param events Buffer to which the event data will be copied. Must hold at
+ * least the amount of events specified in num_events.
+ * @param num_events Specifies how many events can be stored in the buffer.
+ * @return On success returns the number of events stored in the buffer, on
+ * failure -1 is returned.
+ */
+int gpiod_line_event_read_fd_multiple(int fd, struct gpiod_line_event *events,
+ unsigned int num_events) GPIOD_API;
+
+/**
+ * @}
+ *
+ * @defgroup line_misc Misc line functions
+ * @{
+ *
+ * Functions that didn't fit anywhere else.
+ */
+
+/**
+ * @brief Get a GPIO line handle by GPIO chip description and offset.
+ * @param device String describing the gpiochip.
+ * @param offset The offset of the GPIO line.
+ * @return GPIO line handle or NULL if an error occurred.
+ *
+ * This routine provides a shorter alternative to calling
+ * ::gpiod_chip_open_lookup and ::gpiod_chip_get_line.
+ *
+ * If this function succeeds, the caller is responsible for closing the
+ * associated GPIO chip.
+ */
+struct gpiod_line *
+gpiod_line_get(const char *device, unsigned int offset) GPIOD_API;
+
+/**
+ * @brief Find a GPIO line by its name.
+ * @param name Name of the GPIO line.
+ * @return Returns the GPIO line handle if the line exists in the system or
+ * NULL if it couldn't be located or an error occurred.
+ * @attention GPIO lines are not unique in the linux kernel, neither globally
+ * nor within a single chip. This function finds the first line with
+ * given name.
+ *
+ * If this routine succeeds, the user must manually close the GPIO chip owning
+ * this line to avoid memory leaks. If the line could not be found, this
+ * functions sets errno to ENOENT.
+ */
+struct gpiod_line *gpiod_line_find(const char *name) GPIOD_API;
+
+/**
+ * @brief Close a GPIO chip owning this line and release all resources.
+ * @param line GPIO line object
+ *
+ * After this function returns, the line must no longer be used.
+ */
+void gpiod_line_close_chip(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @brief Get the handle to the GPIO chip controlling this line.
+ * @param line The GPIO line object.
+ * @return Pointer to the GPIO chip handle controlling this line.
+ */
+struct gpiod_chip *gpiod_line_get_chip(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @}
+ *
+ * @}
+ *
+ * @defgroup iterators Iterators for GPIO chips and lines
+ * @{
+ *
+ * These functions and data structures allow easy iterating over GPIO
+ * chips and lines.
+ */
+
+/**
+ * @brief Create a new gpiochip iterator.
+ * @return Pointer to a new chip iterator object or NULL if an error occurred.
+ *
+ * Internally this routine scans the /dev/ directory for GPIO chip device
+ * files, opens them and stores their the handles until ::gpiod_chip_iter_free
+ * or ::gpiod_chip_iter_free_noclose is called.
+ */
+struct gpiod_chip_iter *gpiod_chip_iter_new(void) GPIOD_API;
+
+/**
+ * @brief Release all resources allocated for the gpiochip iterator and close
+ * the most recently opened gpiochip (if any).
+ * @param iter The gpiochip iterator object.
+ */
+void gpiod_chip_iter_free(struct gpiod_chip_iter *iter) GPIOD_API;
+
+/**
+ * @brief Release all resources allocated for the gpiochip iterator but
+ * don't close the most recently opened gpiochip (if any).
+ * @param iter The gpiochip iterator object.
+ *
+ * Users may want to break the loop when iterating over gpiochips and keep
+ * the most recently opened chip active while freeing the iterator data.
+ * This routine enables that.
+ */
+void gpiod_chip_iter_free_noclose(struct gpiod_chip_iter *iter) GPIOD_API;
+
+/**
+ * @brief Get the next gpiochip handle.
+ * @param iter The gpiochip iterator object.
+ * @return Pointer to the next open gpiochip handle or NULL if no more chips
+ * are present in the system.
+ * @note The previous chip handle will be closed using ::gpiod_chip_iter_free.
+ */
+struct gpiod_chip *
+gpiod_chip_iter_next(struct gpiod_chip_iter *iter) GPIOD_API;
+
+/**
+ * @brief Get the next gpiochip handle without closing the previous one.
+ * @param iter The gpiochip iterator object.
+ * @return Pointer to the next open gpiochip handle or NULL if no more chips
+ * are present in the system.
+ * @note This function works just like ::gpiod_chip_iter_next but doesn't
+ * close the most recently opened chip handle.
+ */
+struct gpiod_chip *
+gpiod_chip_iter_next_noclose(struct gpiod_chip_iter *iter) GPIOD_API;
+
+/**
+ * @brief Iterate over all GPIO chips present in the system.
+ * @param iter An initialized GPIO chip iterator.
+ * @param chip Pointer to a GPIO chip handle. On each iteration the newly
+ * opened chip handle is assigned to this argument.
+ *
+ * The user must not close the GPIO chip manually - instead the previous chip
+ * handle is closed automatically on the next iteration. The last chip to be
+ * opened is closed internally by ::gpiod_chip_iter_free.
+ */
+#define gpiod_foreach_chip(iter, chip) \
+ for ((chip) = gpiod_chip_iter_next(iter); \
+ (chip); \
+ (chip) = gpiod_chip_iter_next(iter))
+
+/**
+ * @brief Iterate over all chips present in the system without closing them.
+ * @param iter An initialized GPIO chip iterator.
+ * @param chip Pointer to a GPIO chip handle. On each iteration the newly
+ * opened chip handle is assigned to this argument.
+ *
+ * The user must close all the GPIO chips manually after use, until then, the
+ * chips remain open. Free the iterator by calling
+ * ::gpiod_chip_iter_free_noclose to avoid closing the last chip automatically.
+ */
+#define gpiod_foreach_chip_noclose(iter, chip) \
+ for ((chip) = gpiod_chip_iter_next_noclose(iter); \
+ (chip); \
+ (chip) = gpiod_chip_iter_next_noclose(iter))
+
+/**
+ * @brief Create a new line iterator.
+ * @param chip Active gpiochip handle over the lines of which we want
+ * to iterate.
+ * @return New line iterator or NULL if an error occurred.
+ */
+struct gpiod_line_iter *
+gpiod_line_iter_new(struct gpiod_chip *chip) GPIOD_API;
+
+/**
+ * @brief Free all resources associated with a GPIO line iterator.
+ * @param iter Line iterator object.
+ */
+void gpiod_line_iter_free(struct gpiod_line_iter *iter) GPIOD_API;
+
+/**
+ * @brief Get the next GPIO line handle.
+ * @param iter The GPIO line iterator object.
+ * @return Pointer to the next GPIO line handle or NULL if there are no more
+ * lines left.
+ */
+struct gpiod_line *
+gpiod_line_iter_next(struct gpiod_line_iter *iter) GPIOD_API;
+
+/**
+ * @brief Iterate over all GPIO lines of a single chip.
+ * @param iter An initialized GPIO line iterator.
+ * @param line Pointer to a GPIO line handle - on each iteration, the
+ * next GPIO line will be assigned to this argument.
+ */
+#define gpiod_foreach_line(iter, line) \
+ for ((line) = gpiod_line_iter_next(iter); \
+ (line); \
+ (line) = gpiod_line_iter_next(iter))
+
+/**
+ * @}
+ *
+ * @defgroup misc Stuff that didn't fit anywhere else
+ * @{
+ *
+ * Various libgpiod-related functions.
+ */
+
+/**
+ * @brief Get the API version of the library as a human-readable string.
+ * @return Human-readable string containing the library version.
+ */
+const char *gpiod_version_string(void) GPIOD_API;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __LIBGPIOD_GPIOD_H__ */
diff --git a/app_sdk/app_src/app/src/main/cpp/led_control.cpp b/app_sdk/app_src/app/src/main/cpp/led_control.cpp
new file mode 100644
index 0000000..2be1b9f
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/cpp/led_control.cpp
@@ -0,0 +1,160 @@
+#include <jni.h>
+#include <string>
+#include <android/log.h>
+#include "include/gpiod.h"
+
+enum
+{
+ LED_R=0,
+ LED_Y,
+ LED_G,
+ LED_MAX,
+};
+
+enum
+{
+ ON=0,
+ OFF,
+};
+
+typedef struct led_gpio_s
+{
+ int idx;
+ int gpio;
+ const char *desc;
+ struct gpiod_line *line;
+}led_gpio_t;
+
+led_gpio_t leds[LED_MAX] =
+ {
+ {LED_R, 18, "red", NULL},
+ {LED_Y, 22, "yellow", NULL},
+ {LED_G, 20, "green", NULL},
+ };
+
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_LedControl_ledOpen(JNIEnv *env, jclass clazz) {
+ // TODO: implement ledOpen()
+ struct gpiod_line *line;
+ struct gpiod_chip *chip;
+ const char *chipname = "gpiochip0";
+ int ret;
+ int i, j;
+ int rv = 0;
+
+ chip = gpiod_chip_open_by_name(chipname);
+ if( !chip )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "gpiod open '%s' failed:%s\n", chipname, strerror(errno));
+ return -1;
+ }
+
+ for( i=0; i<LED_MAX; i++ )
+ {
+ // 获取句柄
+ leds[i].line = gpiod_chip_get_line(chip, leds[i].gpio);
+ if( !leds[i].line )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "Get line[%d] failed:%s\n", leds[i].gpio, strerror(errno));
+ gpiod_line_release(leds[i].line);
+ gpiod_chip_close(chip);
+ return -2;
+ }
+
+ // 判断该GPIO接口是否被占用
+ ret = gpiod_line_is_used(leds[i].line);
+ if( ret )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "line[%d] is used!\n", leds[i].gpio);
+ gpiod_chip_close(chip);
+ return -3;
+ }
+
+ // 设置该接口为输出模式
+ ret = gpiod_line_request_output(leds[i].line, leds[i].desc, 0);
+ if( ret < 0 )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "set line[%d] output failed:%s\n", leds[i].gpio, strerror(errno));
+ gpiod_line_release(leds[i].line);
+ gpiod_chip_close(chip);
+ return -4;
+ }
+ }
+}
+
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_LedControl_ledCtrl(JNIEnv *env, jclass clazz, jint which, jint status) {
+ // TODO: implement ledCtrl()
+ if( which<0 || which>=LED_MAX )
+ {
+ return -1;
+ }
+
+ struct gpiod_chip *chip;
+ const char *chipname = "gpiochip0";
+ int ret;
+ int rv = 0;
+
+ chip = gpiod_chip_open_by_name(chipname);
+ if( !chip )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "gpiod open '%s' failed:%s\n", chipname, strerror(errno));
+ return -1;
+ }
+
+ // 获取句柄
+ leds[which].line = gpiod_chip_get_line(chip, leds[which].gpio);
+ if( !leds[which].line )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "Get line[%d] failed:%s\n", leds[which].gpio, strerror(errno));
+ gpiod_line_release(leds[which].line);
+ gpiod_chip_close(chip);
+ return -2;
+ }
+
+ // 判断该GPIO接口是否被占用
+ ret = gpiod_line_is_used(leds[which].line);
+ if( ret )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "line[%d] is used!\n", leds[which].gpio);
+ gpiod_chip_close(chip);
+ return -3;
+ }
+
+ // 设置该接口为输出模式
+ ret = gpiod_line_request_output(leds[which].line, leds[which].desc, 0);
+ if( ret < 0 )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "set line[%d] output failed:%s\n", leds[which].gpio, strerror(errno));
+ gpiod_line_release(leds[which].line);
+ gpiod_chip_close(chip);
+ return -4;
+ }
+
+ __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "turn %s led GPIO#%d %s\n", leds[which].desc, leds[which].gpio, status?"off":"on");
+
+ if( ON == status )
+ {
+ rv = gpiod_line_set_value(leds[which].line, 0);
+ if( rv < 0 )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "turn %s ON failed:%s\n", leds[which].desc, strerror(errno));
+ return -5;
+ }
+ }
+ else
+ {
+ rv = gpiod_line_set_value(leds[which].line, 1);
+ if( rv < 0 )
+ {
+ __android_log_print(ANDROID_LOG_INFO, "LEDDemo", "turn %s OFF failed:%s\n", leds[which].desc, strerror(errno));
+ return -5;
+ }
+ }
+
+ gpiod_line_release(leds[which].line);
+ gpiod_chip_close(chip);
+ return 0;
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/cpp/native-lib.cpp b/app_sdk/app_src/app/src/main/cpp/native-lib.cpp
new file mode 100644
index 0000000..1af95fe
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/cpp/native-lib.cpp
@@ -0,0 +1,51 @@
+#include <jni.h>
+#include <string>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <android/log.h>
+#define TAG "BuzzerApp"
+#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
+#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
+#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
+
+static char pwm_path[100];
+
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_BuzzerControl_openPwm(JNIEnv *env, jclass clazz, jstring id) {
+ // TODO: implement openPwm()
+ char *id_native;
+ char temp[100];
+ int fd;
+ id_native = (char *)env->GetStringUTFChars(id, 0);
+ memset(pwm_path, 0, sizeof (pwm_path));
+ snprintf(pwm_path, sizeof (pwm_path), "/sys/class/pwm/pwmchip%s/pwm0", id_native);
+
+ memset(temp, 0, sizeof (temp));
+ if(access(pwm_path, F_OK))
+ {
+ snprintf(temp, sizeof (temp), "/sys/class/pwm/pwmchip%s/export", id_native);
+ fd = open(temp, O_WRONLY);
+ if(fd < 0)
+ {
+ LOGE("open %s error:%s\n", temp, strerror(errno));
+ return -1;
+ }
+
+ if(1 != write(fd, "0", 1))
+ {
+ LOGE("Write '0' to pwmchip%s/export error\n", id_native);
+ close(fd);
+ return -2;
+ }
+ close(fd);
+ }
+ env->ReleaseStringUTFChars(id, id_native);
+ return 0;
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/cpp/rs485_control.cpp b/app_sdk/app_src/app/src/main/cpp/rs485_control.cpp
new file mode 100644
index 0000000..cec9b7b
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/cpp/rs485_control.cpp
@@ -0,0 +1,516 @@
+#include <jni.h>
+#include <string>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <android/log.h>
+#include "include/gpiod.h"
+
+#define TAG "SerialApp"
+#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
+#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
+#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
+
+typedef struct comport_s
+{
+ char devname[12];
+ unsigned int databit, parity, stopbit, flowctrl;
+ long baudrate;
+
+ int fd;
+ int frag_size;
+}comport_t;
+
+comport_t *comport;
+
+// 将int类型的波特率转换成可以识别的波特率
+static speed_t getBaudRate(int baudRate){
+ switch (baudRate) {
+ case 0:
+ return B0;
+ case 50:
+ return B50;
+ case 75:
+ return B75;
+ case 110:
+ return B110;
+ case 134:
+ return B134;
+ case 150:
+ return B150;
+ case 200:
+ return B200;
+ case 300:
+ return B300;
+ case 600:
+ return B600;
+ case 1200:
+ return B1200;
+ case 1800:
+ return B1800;
+ case 2400:
+ return B2400;
+ case 4800:
+ return B4800;
+ case 9600:
+ return B9600;
+ case 19200:
+ return B19200;
+ case 38400:
+ return B38400;
+ case 57600:
+ return B57600;
+ case 115200:
+ return B115200;
+ case 230400:
+ return B230400;
+ case 460800:
+ return B460800;
+ case 500000:
+ return B500000;
+ case 576000:
+ return B576000;
+ case 921600:
+ return B921600;
+ case 1000000:
+ return B1000000;
+ case 1152000:
+ return B1152000;
+ case 1500000:
+ return B1500000;
+ case 2000000:
+ return B2000000;
+ case 2500000:
+ return B2500000;
+ case 3000000:
+ return B3000000;
+ case 3500000:
+ return B3500000;
+ case 4000000:
+ return B4000000;
+ default:
+ return -1;
+ }
+}
+
+extern "C"
+JNIEXPORT int JNICALL
+Java_com_example_serial_RS485Control_openSerialPort(JNIEnv *env, jclass clazz, jstring path,
+ jlong baud_rate, jint data_bits, jint parity,
+ jint stop_bits, jint flow_control) {
+ // TODO: implement openSerialPort()
+ speed_t speed;
+ struct termios old_cfg, new_cfg;
+ int old_flags;
+ long temp;
+
+ comport = (comport_t *) malloc(sizeof(comport_t));
+ if (comport == NULL) {
+ LOGE("Failed to allocate memory for comport structure.");
+ return -1;
+ }
+ memset(comport, 0, sizeof(comport));
+
+// mSend_len = max_len;
+
+ // 检查波特率是否合法
+ {
+ speed = getBaudRate(baud_rate);
+ if (speed == -1)
+ {
+ LOGE("Invalid buad rate");
+ return -1;
+ }
+ }
+
+ // 打开串口设备
+ {
+ jboolean iscopy;
+ // 获取String字符串path中的UTF8编码,并将其转换成C中的字符串
+ const char* devname = (*env).GetStringUTFChars(path, &iscopy);
+ LOGD("Opening serial port %s with flags 0x%x", devname, O_RDWR);
+ strncpy(comport->devname, devname, 12);
+ comport->baudrate = baud_rate;
+ comport->fd = -1;
+ comport->frag_size = 128;
+ comport->databit = data_bits;
+ comport->parity = parity;
+ comport->flowctrl = flow_control;
+ comport->stopbit = stop_bits;
+
+ if( !strstr(comport->devname, "tty") )
+ {
+ LOGE("Open Not a tty device \" %s\" \n", comport->devname);
+ return -2;
+ }
+
+ comport->fd = open(comport->devname, O_RDWR);
+ if ( comport->fd < 0 )
+ {
+ if( strstr(strerror(errno), "Permission denied") != nullptr )
+ return 1;
+ LOGE("open port failed:%s\n", strerror(errno));
+ return -3;
+ }
+ LOGD("Open device %s", comport->devname);
+ // 释放JNI字符串的UTF8编码
+ (*env).ReleaseStringUTFChars(path, devname);
+
+ if( (-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0))) && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK)))
+ {
+ if(-1 == tcflush(comport->fd, TCIOFLUSH))
+ {
+ LOGE("open port failed:%s\n", strerror(errno));
+ return -4;
+ }
+ }
+ else
+ {
+ LOGE("open port failed:%s\n", strerror(errno));
+ return -5;
+ }
+
+ if ( 0 != tcgetattr(comport->fd, &old_cfg))
+ {
+ LOGE("open port failed:%s\n", strerror(errno));
+ return -6;
+ }
+ }
+
+ {
+ LOGD("starting configuring serial port");
+
+ old_cfg.c_cflag &= ~CSIZE;
+ old_cfg.c_lflag &= ~ICANON;
+ old_cfg.c_lflag &= ~ECHO;
+ old_cfg.c_lflag &= ~ECHOE;
+ old_cfg.c_lflag &= ~ECHONL;
+ old_cfg.c_lflag &= ~ISIG;
+ old_cfg.c_iflag &= ~(IXON | IXOFF | IXANY);
+ old_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
+ old_cfg.c_oflag &= ~OPOST;
+ old_cfg.c_oflag &= ~ONLCR;
+ old_cfg.c_cflag |= CREAD | CLOCAL;
+
+ switch (comport->databit)
+ {
+ case 7:
+ old_cfg.c_cflag |= CS7;
+ break;
+ case 6:
+ old_cfg.c_cflag |= CS6;
+ break;
+ case 5:
+ old_cfg.c_cflag |= CS5;
+ break;
+ case 8:
+ old_cfg.c_cflag |= CS8;
+ break;
+ }
+
+ switch(comport->parity)
+ {
+ case 1:
+ old_cfg.c_cflag |= (PARENB | PARODD);
+ old_cfg.c_cflag |= (INPCK | ISTRIP);
+ break;
+ case 2:
+ old_cfg.c_cflag |= PARENB;
+ old_cfg.c_cflag &= ~PARODD;
+ old_cfg.c_cflag |= (INPCK | ISTRIP);
+ break;
+ case 0:
+ old_cfg.c_cflag &= ~PARENB;
+ break;
+ }
+
+ if(1 != comport->stopbit)
+ {
+ old_cfg.c_cflag |= CSTOPB;
+ }
+ else
+ {
+ old_cfg.c_cflag &= ~CSTOPB;
+ }
+
+ // set flow control
+ // 1: software control; 2:hardware control; 0: none
+ switch(comport->flowctrl)
+ {
+ case 1:
+ old_cfg.c_cflag &= ~(CRTSCTS);
+ old_cfg.c_iflag |= (IXON | IXOFF);
+ break;
+ case 2:
+ old_cfg.c_cflag |= CRTSCTS;
+ old_cfg.c_iflag &= ~(IXON | IXOFF);
+ break;
+ case 0:
+ old_cfg.c_cflag &= ~(CRTSCTS);
+ break;
+ }
+
+
+ // set baudRate
+ cfsetispeed(&old_cfg, speed);
+ cfsetospeed(&old_cfg, speed);
+
+ new_cfg.c_cc[VMIN] = 10;
+ new_cfg.c_cc[VTIME] = 0;
+
+
+ // 将设置的串口参数应用到串口上,TCSANOW表示立即生效,(TCSADRAIN:在所有输出都被传输后生效;TCSAFLUSH:在所有输出都被传输后生效,同时丢弃所有未读取的输入)
+ if(tcsetattr(comport->fd, TCSANOW, &old_cfg))
+ {
+ LOGE("tcsetattr() failed:%s", strerror(errno));
+ close(comport->fd);
+ return -1;
+ }
+ LOGD("Connected device \" %s \" successfully\n", comport->devname);
+ LOGD("port:%s, databit:%d, stopbit:%d, parity:%d, flowctl:%d", comport->devname, comport->databit, comport->stopbit, comport->parity, comport->flowctrl);
+ }
+ return 0;
+}
+extern "C"
+JNIEXPORT int JNICALL
+Java_com_example_serial_RS485Control_closeSerialPort(JNIEnv *env, jclass clazz) {
+ // TODO: implement closeSerialPort()
+ if ( !comport )
+ {
+ LOGE("%s() get invalid input arguments.\n", __FUNCTION__ );
+ return -1;
+ }
+
+ if (comport->fd >= 0)
+ {
+ close(comport->fd);
+ LOGD("close device \" %s \" successfully\n", comport->devname);
+ }
+ comport->fd = -1;
+ free(comport);
+ comport = NULL;
+ return 0;
+}
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_RS485Control_sendToPort(JNIEnv *env, jclass clazz, jstring msg, jint len) {
+ // TODO: implement sendToPort()
+ // msg: 表示发送的数据;len: 发送的长度
+ int rv;
+ char *ptr, left_bytes;
+ int send = 0;
+ const char *sendMsg = env->GetStringUTFChars(msg, 0);
+ if ( sendMsg == nullptr )
+ {
+ return -1;
+ }
+
+ if( !comport || !msg || len <= 0 )
+ {
+ LOGE("Invalid parameter.\n");
+ return -1;
+ }
+ if (comport->fd == -1)
+ {
+ LOGE("Serail not connected.\n");
+ return -2;
+ }
+
+ left_bytes = len;
+ ptr = (char *)sendMsg;
+
+ while( left_bytes > 0 )
+ {
+ /* Large data, then slice them to frag and send */
+ send = left_bytes>comport->frag_size ? comport->frag_size : left_bytes;
+
+ rv = write(comport->fd, ptr, send);
+ if( rv<0 )
+ {
+ return -4;
+ }
+
+ left_bytes -= rv;
+ ptr += rv;
+ }
+ LOGD("send %s successfully\n", sendMsg);
+
+ env->ReleaseStringUTFChars(msg, sendMsg);
+ return rv;
+}
+
+
+extern "C"
+JNIEXPORT jstring JNICALL
+Java_com_example_serial_RS485Control_recvFromPort(JNIEnv *env, jclass clazz, jint len, jint timeout) {
+ int rv = 0;
+ int iRet;
+ fd_set rdfds, exfds;
+ struct timeval stTime;
+
+ jstring result;
+ char nativeMsg[len];
+ memset(nativeMsg, 0, len);
+
+ if( !comport || len <= 0 )
+ {
+ LOGE("Invalid parameter.\n");
+ return nullptr;
+ }
+ if (comport->fd == -1)
+ {
+ LOGE("Serail not connected.\n");
+ return nullptr;
+ }
+
+ FD_ZERO(&rdfds);
+ FD_ZERO(&exfds);
+ FD_SET(comport->fd, &rdfds);
+ FD_SET(comport->fd, &exfds);
+
+ if (0xFFFFFFFF != timeout)
+ {
+ stTime.tv_sec = (time_t) (timeout / 1000);
+ stTime.tv_usec = (long)(1000 * (timeout % 1000));
+
+ iRet = select(comport->fd + 1, &rdfds, 0, &exfds, &stTime);
+ if (0 == iRet)
+ {
+ return nullptr;
+ }
+ else if (0 < iRet)
+ {
+ if (0 != FD_ISSET(comport->fd, &exfds))
+ {
+ LOGE("Error checking recv status.\n");
+ return nullptr;
+ }
+
+ if (0 == FD_ISSET(comport->fd, &rdfds))
+ {
+ LOGE("No incoming data.\n");
+ return nullptr;
+ }
+ }
+ else
+ {
+ if (EINTR == errno)
+ {
+ LOGE("catch interrupt signal.\n");
+// rv = 0;
+ }
+ else
+ {
+ LOGE("Check recv status failure.\n");
+// rv = -7;
+ }
+ return nullptr;
+ }
+ }
+
+ usleep(10000); /* sleep for 10ms for data incoming */
+
+ // Get data from Com port
+ iRet = read(comport->fd, nativeMsg, len);
+ if (0 > iRet)
+ {
+ if (EINTR == errno)
+ rv = 0; // Interrupted signal catched
+ else
+ rv = -3; // Failed to read comport
+
+ return nullptr;
+ }
+ LOGD("Receive {%s} successfully\n", nativeMsg);
+
+ return env->NewStringUTF(nativeMsg);
+}
+
+extern "C"
+JNIEXPORT jstring JNICALL
+Java_com_example_serial_RS485Control_saveToFile(JNIEnv *env, jclass clazz, jstring msg, jint len,
+ jstring file_name) {
+ // TODO: implement saveToFile()
+ int mFd = -1;
+ int rv;
+ mFd = open("dev/log.txt", O_CREAT | O_RDWR | O_APPEND, 0666);
+ rv = write(mFd, msg, len);
+ if (rv < 0) {
+ LOGE("Save data to file failed:%s\n" , strerror(errno));
+ return nullptr;
+ }
+ LOGD("save received data into file successfully!\n");
+ char buf[1024];
+ rv = read(mFd, buf, len);
+ if (rv < 0) {
+ LOGE("Read data failed:%s\n" , strerror(errno));
+ return nullptr;
+ }
+ strcat(reinterpret_cast<char *>(buf), "file");
+ close(mFd);
+ return reinterpret_cast<jstring>(buf);
+}
+
+// RS485是半双工通信的,由GPIO的高低电平来控制其是接收状态还是发送状态
+// 0: 接收(默认);1:发送
+// 切换状态成功返回0,失败返回-1
+extern "C"
+JNIEXPORT jint JNICALL
+Java_com_example_serial_RS485Control_changeState(JNIEnv *env, jclass clazz, jint state) {
+ // TODO: implement changeState()
+ struct gpiod_chip *chip;
+ struct gpiod_line *line;
+ const char *chipname = "gpiochip4";
+ int rv = 0;
+ int gpio = 26;
+
+
+ chip = gpiod_chip_open_by_name(chipname);
+ if( !chip )
+ {
+ LOGE("Gpio open '%s' failed:%s\n", chipname, strerror(errno));
+ return -1;
+ }
+
+ line = gpiod_chip_get_line(chip, gpio);
+ if ( !line )
+ {
+ LOGE("Get gpio[%d] failed:%s\n", gpio, strerror(errno));
+ rv = -1;
+ goto Cleanup;
+ }
+
+ rv = gpiod_line_is_used(line);
+ if( rv )
+ {
+ LOGE("GPIO[%d] is used\n", gpio);
+ rv = -1;
+ goto Cleanup;
+ }
+
+ rv = gpiod_line_request_output(line, "rs485", 0);
+ if ( rv < 0 )
+ {
+ LOGE("Set GPIO[%d] as output failed:%s\n", gpio, strerror(errno));
+ rv = -1;
+ goto Cleanup;
+ }
+
+ rv = gpiod_line_set_value(line, state);
+ if ( rv < 0 )
+ {
+ LOGE("Set GPIO[%d]'s value failed:%s\n", gpio, strerror(errno));
+ rv = -1;
+ goto Cleanup;
+ }
+ LOGD("Set GPIO[%d]'s value as %d successfully\n", gpio, state);
+ rv = 0;
+
+ Cleanup:
+ gpiod_line_release(line);
+ gpiod_chip_close(chip);
+ return rv;
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java
new file mode 100644
index 0000000..2e5cb0c
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java
@@ -0,0 +1,247 @@
+package com.example.serial;
+
+import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.annotation.SuppressLint;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.example.serial.databinding.ActivityMainBinding;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+public class AdcActivity extends AppCompatActivity {
+
+ // 与串口参数相关的组件
+ private ActivityMainBinding binding;
+ private Spinner mAdcSpBaud;
+ private Spinner mAdcSpSerial;
+ private Spinner mAdcSpData;
+ private Spinner mAdcSpStop;
+ private Spinner mAdcSpParity;
+ private Spinner mAdcSpFlow;
+ private Spinner mAdcSpOperate;
+ private Spinner mAdcSpChannel;
+
+ private Button mAdcBtnSend;
+ private Button mAdcBtnOpen;
+ private Button mAdcBtnClose;
+ private Button mAdcBtnRecv;
+ private TextView mAdcRecv;
+
+ // 设置串口的参数
+ private String serial = "/dev/ttyS6";
+ private long baudRate;
+ private int dataBits;
+ private double stopBits;
+ private int parity;
+ private int flowControl; // 0:None, 1:RTS/CTS, 2:XON/XOFF
+ private String operate; // 电流or电压
+ private String channel;
+
+ // 获取已经设定的值
+ String[] serialArray;
+ String[] baudArray;
+ String[] dataArray;
+ String[] stopArray;
+ String[] parityArray;
+ String[] flowArray;
+ String[] operateArray;
+ String[] channelArray;
+ private AdcControl adcControl;
+
+ private int len = 16;
+ private int timmout = 1000;
+
+ boolean turnFlag = false;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_adc);
+
+ adcControl = new AdcControl();
+ // 找到控件
+ mAdcSpSerial = findViewById(R.id.adc_sp_serial);
+ mAdcSpBaud = findViewById(R.id.adc_sp_baud);
+ mAdcSpData = findViewById(R.id.adc_sp_data);
+ mAdcSpStop = findViewById(R.id.adc_sp_stop);
+ mAdcSpParity = findViewById(R.id.adc_sp_parity);
+ mAdcSpFlow = findViewById(R.id.adc_sp_flow);
+ mAdcSpOperate = findViewById(R.id.adc_sp_operate);
+ mAdcSpChannel = findViewById(R.id.adc_sp_channel);
+
+ mAdcBtnSend = findViewById(R.id.adc_btn_send);
+ mAdcBtnOpen = findViewById(R.id.adc_btn_open);
+ mAdcBtnClose = findViewById(R.id.adc_btn_close);
+ mAdcBtnRecv = findViewById(R.id.adc_btn_recv);
+ mAdcRecv = findViewById(R.id.adc_txt_rev);
+
+ // 设置并监听下拉列表
+ setSpinner(mAdcSpSerial);
+ setSpinner(mAdcSpBaud);
+ setSpinner(mAdcSpData);
+ setSpinner(mAdcSpStop);
+ setSpinner(mAdcSpParity);
+ setSpinner(mAdcSpFlow);
+ setSpinner(mAdcSpOperate);
+ setSpinner(mAdcSpChannel);
+
+ // 监听所有的button
+ setListener();
+ mAdcRecv.setMovementMethod(ScrollingMovementMethod.getInstance());
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ adcControl.closeComport();
+ super.onDestroy();
+ }
+
+ private void setSpinner(Spinner spinner){
+ ArrayAdapter<CharSequence> adapter;
+ int id = spinner.getId();
+
+ if (id == R.id.adc_sp_serial){
+ adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.adc_serial_name, android.R.layout.simple_spinner_item);
+ }else if (id == R.id.adc_sp_baud) {
+ adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.baud_rate, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.adc_sp_data) {
+ adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.data_bits, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.adc_sp_stop) {
+ adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.stop_bits, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.adc_sp_parity) {
+ adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.parity, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.adc_sp_flow) {
+ adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.flow_control, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.adc_sp_operate) {
+ adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.adc_operate, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.adc_sp_channel){
+ adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.adc_channel, android.R.layout.simple_spinner_item);
+ } else {
+ throw new IllegalStateException("Unexpected value: " + spinner.getId());
+ }
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_item);
+ spinner.setAdapter(adapter);
+
+ spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ int spinnerId = spinner.getId();
+
+ if (spinnerId == R.id.adc_sp_serial) {
+ serialArray = getResources().getStringArray(R.array.adc_serial_name);
+ serial = serialArray[position];
+ } else if (spinnerId == R.id.adc_sp_baud) {
+ baudArray = getResources().getStringArray(R.array.baud_rate);
+ baudRate = Integer.parseInt(baudArray[position]);
+ } else if (spinnerId == R.id.adc_sp_data) {
+ dataArray = getResources().getStringArray(R.array.data_bits);
+ dataBits = Integer.parseInt(dataArray[position]);
+ } else if (spinnerId == R.id.adc_sp_stop) {
+ stopArray = getResources().getStringArray(R.array.stop_bits);
+ stopBits = Double.parseDouble(stopArray[position]);
+ } else if (spinnerId == R.id.adc_sp_parity) {
+ parityArray = getResources().getStringArray(R.array.parity);
+ if (parityArray[position].equals("偶校验"))
+ parity = 2;
+ else if (parityArray[position].equals("奇校验"))
+ parity = 1;
+ else if (parityArray[position].equals("None"))
+ parity = 0;
+ } else if (spinnerId == R.id.adc_sp_flow) {
+ flowArray = getResources().getStringArray(R.array.flow_control);
+ flowControl = position;
+ } else if (spinnerId == R.id.adc_sp_operate) {
+ operateArray = getResources().getStringArray(R.array.adc_operate);
+ operate = operateArray[position];
+ } else if (spinnerId == R.id.adc_sp_channel) {
+ channelArray = getResources().getStringArray(R.array.adc_channel);
+ channel = channelArray[position];
+ } else {
+ throw new IllegalStateException("Unexpected value: " + spinner.getId());
+ }
+ }
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+ }
+
+ private void setListener(){
+ OnClick onClick = new OnClick();
+ mAdcBtnSend.setOnClickListener(onClick);
+ mAdcBtnOpen.setOnClickListener(onClick);
+ mAdcBtnClose.setOnClickListener(onClick);
+ mAdcBtnRecv.setOnClickListener(onClick);
+ }
+ private class OnClick implements View.OnClickListener{
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.adc_btn_open) {
+ if (turnFlag) {
+ Toast.makeText(getApplicationContext(), "串口已打开, 不能重复打开", Toast.LENGTH_SHORT).show();
+ }else {
+ int rv = adcControl.openComport(serial, (int) baudRate, dataBits, parity, (int) stopBits, flowControl);
+ if(rv == 1){
+ Toast.makeText(getApplicationContext(), "请添加"+serial+"的权限", Toast.LENGTH_SHORT).show();
+ }else if (rv < 0) {
+ Toast.makeText(getApplicationContext(), "打开串口失败", Toast.LENGTH_SHORT).show();
+ } else {
+ turnFlag = true;
+ Toast.makeText(getApplicationContext(), "成功打开串口"+serial, Toast.LENGTH_SHORT).show();
+ }
+ }
+ } else if (id == R.id.adc_btn_close){
+ if (!turnFlag) {
+ Toast.makeText(getApplicationContext(), "串口已关闭,不能重复关闭", Toast.LENGTH_SHORT).show();
+ }else {
+ // 关闭串口
+ int rv = adcControl.closeComport();
+ if (rv < 0) {
+ Toast.makeText(getApplicationContext(), "关闭串口失败", Toast.LENGTH_SHORT).show();
+ } else {
+ turnFlag = false;
+ Toast.makeText(getApplicationContext(), "关闭串口", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }else if (id == R.id.adc_btn_send) {
+ int rv = adcControl.sendToPort(operate, channel);
+ if (rv < 0) {
+ Toast.makeText(getApplicationContext(), "发送数据失败!", Toast.LENGTH_SHORT).show();
+ }
+ Toast.makeText(getApplicationContext(), "发送数据成功", Toast.LENGTH_SHORT).show();
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ df.setTimeZone(TimeZone.getTimeZone("GMT+8"));
+ String date = df.format(new Date());
+ String buf = adcControl.recvFromPort(len, timmout); // 超时时间设置为 1000 ms
+ if (buf != null) {
+ String revData = date + "\t"+ "Received: " + buf;
+ mAdcRecv.append("\n" + revData);
+ mAdcRecv.invalidate();
+ mAdcRecv.requestLayout();
+ }
+ } else if (id == R.id.adc_btn_recv) {
+ mAdcRecv.setText(" ");
+ }
+ }
+ }
+}
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java b/app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java
new file mode 100644
index 0000000..feca1c8
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java
@@ -0,0 +1,12 @@
+package com.example.serial;
+
+public class AdcControl {
+ static {
+ System.loadLibrary("serial");
+ }
+ public static native int openComport(String path, long baudRate, int dataBits, int parity, int stopBits, int flowControl);
+ public static native int closeComport();
+
+ public static native int sendToPort(String operate, String channel);
+ public static native String recvFromPort(int len, int timeout);
+}
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java
new file mode 100644
index 0000000..c1a61c3
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java
@@ -0,0 +1,104 @@
+package com.example.serial;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class BuzzerActivity extends AppCompatActivity {
+
+ private Button play;
+ private Button stop;
+ private EditText period;
+ private EditText duty_cycle;
+
+ private String periodBuf;
+ private String dutyBuf;
+ private BuzzerControl buzzerControl;
+ private int rv;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_buzzer);
+ buzzerControl = new BuzzerControl();
+
+ play = findViewById(R.id.play);
+ period = findViewById(R.id.period);
+ duty_cycle = findViewById(R.id.duty_cycle);
+ stop = findViewById(R.id.stop);
+
+
+ play.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ periodBuf = period.getText().toString();
+ dutyBuf = duty_cycle.getText().toString();
+
+ if(periodBuf.isEmpty() || dutyBuf.isEmpty()){
+ periodBuf = period.getHint().toString();
+ dutyBuf = duty_cycle.getHint().toString();
+ period.setText(periodBuf);
+ duty_cycle.setText(dutyBuf);
+ }
+ // 打开pwmchip2/pwm0
+ rv = buzzerControl.pwmOpen("2");
+ if (rv == 1) {
+ // 表示缺少权限
+ Toast.makeText(BuzzerActivity.this, "请添加/sys/class/pwm/pwmchip2/export的权限", Toast.LENGTH_SHORT).show();
+ }else if( rv < 0 ){
+ Toast.makeText(BuzzerActivity.this, "打开PWM失败", Toast.LENGTH_SHORT).show();
+ } else {
+ // 首先设置周期
+ rv = buzzerControl.pwmConfig("period", periodBuf);
+ if (rv == 1) {
+ // 表示缺少权限
+ Toast.makeText(BuzzerActivity.this, "请添加/sys/class/pwm/pwmchip2/pwm0/period的权限", Toast.LENGTH_SHORT).show();
+ } else if (rv == 2) {
+ Toast.makeText(BuzzerActivity.this, "周期的值无效,请重新输入", Toast.LENGTH_SHORT).show();
+ }else if (rv < 0) {
+ Toast.makeText(BuzzerActivity.this, "设置周期失败", Toast.LENGTH_SHORT).show();
+ }else {
+ // 其次设置占空比
+ rv = buzzerControl.pwmConfig("duty_cycle", dutyBuf);
+ if (rv == 1) {
+ // 表示缺少权限
+ Toast.makeText(BuzzerActivity.this, "请添加/sys/class/pwm/pwmchip2/pwm0/duty_cycle的权限", Toast.LENGTH_SHORT).show();
+ } else if (rv == 2) {
+ Toast.makeText(BuzzerActivity.this, "占空比的值无效,请重新输入", Toast.LENGTH_SHORT).show();
+ }
+ if (rv < 0) {
+ Toast.makeText(BuzzerActivity.this, "设置占空比失败", Toast.LENGTH_SHORT).show();
+ } else {
+ // 最后使能pwm
+ rv = buzzerControl.pwmConfig("enable", "1");
+ if (rv == 1) {
+ // 表示缺少权限
+ Toast.makeText(BuzzerActivity.this, "请添加/sys/class/pwm/pwmchip2/pwm0/enable的权限", Toast.LENGTH_SHORT).show();
+ } else if (rv < 0) {
+ Toast.makeText(BuzzerActivity.this, "使能PWM失败", Toast.LENGTH_SHORT).show();
+ } else
+ Toast.makeText(BuzzerActivity.this, "打开PWM, 正在播放", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ }
+ });
+
+ stop.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ rv = buzzerControl.pwmConfig("enable", "0");
+ if( rv < 0 || rv == 1 || rv == 2)
+ Toast.makeText(BuzzerActivity.this, "禁止PWM失败", Toast.LENGTH_SHORT).show();
+ else {
+ Toast.makeText(BuzzerActivity.this, "禁止PWM,暂停播放", Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+
+ }
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java b/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java
new file mode 100644
index 0000000..0c28db3
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java
@@ -0,0 +1,9 @@
+package com.example.serial;
+
+public class BuzzerControl {
+ static{
+ System.loadLibrary("serial");
+ }
+ public static native int pwmOpen(String id); // 返回1表示缺少权限
+ public static native int pwmConfig(String attr, String val); // 返回1表示缺少权限,2表示输入的值无效
+}
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java
new file mode 100644
index 0000000..88be31b
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java
@@ -0,0 +1,169 @@
+package com.example.serial;
+import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class CanActivity extends AppCompatActivity {
+ private volatile boolean receiving = false;
+ private Thread receiveThread;
+
+ private String data; // 存放发送的数据
+ private String canId;
+ private String dlc;
+ private String canName;
+ private String status = "send";
+
+ private Button mBtnSend;
+ private Button mBtnRecv;
+ private Button mBtnSendClear;
+ private Button mBtnRecvClear;
+ private TextView mTvSend;
+ private TextView mTvRecv;
+ private EditText mEdId;
+ private EditText mEdDlc;
+ private EditText mEdData;
+ private Spinner mSpCan;
+ private Spinner mSpBit;
+
+ private CanControl canControl = new CanControl();
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ EdgeToEdge.enable(this);
+ setContentView(R.layout.activity_can);
+
+ mBtnSend = findViewById(R.id.can_send);
+ mBtnRecv = findViewById(R.id.can_recv);
+ mBtnSendClear = findViewById(R.id.can_send_clear);
+ mBtnRecvClear = findViewById(R.id.can_recv_clear);
+ mTvSend = findViewById(R.id.can_tv_send);
+ mTvRecv = findViewById(R.id.can_tv_recv);
+ mEdId = findViewById(R.id.can_ed_id);
+ mEdDlc = findViewById(R.id.can_ed_dlc);
+ mEdData = findViewById(R.id.can_ed_data);
+ mSpCan = findViewById(R.id.sp_can);
+
+ mTvSend.setMovementMethod(ScrollingMovementMethod.getInstance());
+ mTvRecv.setMovementMethod(ScrollingMovementMethod.getInstance());
+
+
+ ArrayAdapter<CharSequence> adapter_name;
+ adapter_name = ArrayAdapter.createFromResource(CanActivity.this, R.array.can_name, android.R.layout.simple_spinner_item);
+ adapter_name.setDropDownViewResource(android.R.layout.simple_spinner_item);
+ mSpCan.setAdapter(adapter_name);
+ mSpCan.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ canName = getResources().getStringArray(R.array.can_name)[position];
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+
+ setListener();
+
+
+ }
+
+ private void setListener() {
+ OnClick onClick = new OnClick();
+ mBtnRecv.setOnClickListener(onClick);
+ mBtnRecvClear.setOnClickListener(onClick);
+ mBtnSend.setOnClickListener(onClick);
+ mBtnSendClear.setOnClickListener(onClick);
+ }
+
+ private class OnClick implements View.OnClickListener {
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.can_send) {
+ status = "send";
+ if (canName == null){
+ Toast.makeText(CanActivity.this, "请选择CAN!", Toast.LENGTH_SHORT).show();
+ }
+ canId = mEdId.getText().toString();
+ dlc = mEdDlc.getText().toString();
+ data = mEdData.getText().toString();
+ if (Integer.parseInt(canId) > 0xFFF || Integer.parseInt(canId) < 0x000) {
+ Toast.makeText(CanActivity.this, "请输入正确的ID!", Toast.LENGTH_SHORT).show();
+ }else if (Integer.parseInt(dlc) > 7 || Integer.parseInt(canId) < 0){
+ Toast.makeText(CanActivity.this, "请输入正确的DLC!", Toast.LENGTH_SHORT).show();
+ }else {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ df.setTimeZone(TimeZone.getTimeZone("GMT+8"));
+ String date = df.format(new Date());
+ int rv = canControl.sendMessage(canId, dlc, data, canName);
+ if (rv < 0) {
+ Log.e("Send", "Send failed");
+ Toast.makeText(CanActivity.this, "发送失败,检查网络是否连接!", Toast.LENGTH_SHORT).show();
+ }else {
+ mTvSend.post(() -> {
+ String sendData = date + "\t" + "ID=" + canId + " DLC=" + dlc + " Data=" + data;
+ String currentText = mTvSend.getText().toString();
+ String newText = currentText + "\n" + sendData; // 追加新数据
+ mTvSend.append("\n" + sendData);
+ });
+ }
+ }
+ } else if (id == R.id.can_send_clear) {
+ mTvSend.setText("");
+ } else if (id == R.id.can_recv) {
+ if (canName == null){
+ Toast.makeText(CanActivity.this, "请选择CAN!", Toast.LENGTH_SHORT).show();
+ }
+ status = "recv";
+ startReceiving();
+ } else if (id == R.id.can_recv_clear) {
+ status = "stopRecv";
+ mTvRecv.setText("");
+ }
+ }
+ }
+ public void startReceiving() {
+ receiving = true;
+ Log.d("TAG", "startReceiving: "+status);
+ receiveThread = new Thread(() -> {
+ Log.d("TAG", "startReceiving: "+status);
+ while (status.equals("recv")) {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ df.setTimeZone(TimeZone.getTimeZone("GMT+8"));
+ String date = df.format(new Date());
+ String buf = canControl.receiveMessage(canName);
+ if (buf == null) {
+ Log.e("Recv", "Receive failed");
+ }
+ Log.d("Recv", buf);
+ mTvRecv.post(() -> {
+ String revData = date + "\t" + buf;
+ String currentText = mTvRecv.getText().toString();
+ String newText = currentText + "\n" + revData; // 追加新数据
+ mTvRecv.setText(newText);
+ });
+ try {
+ Thread.sleep(10); // 每 10 ms 读取一次数据
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ receiveThread.start();
+ }
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java b/app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java
new file mode 100644
index 0000000..8e51579
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java
@@ -0,0 +1,9 @@
+package com.example.serial;
+
+public class CanControl {
+ static{
+ System.loadLibrary("serial");
+ }
+ public static native int sendMessage(String id, String dlc, String data, String ifname);
+ public static native String receiveMessage(String ifname);
+}
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java
new file mode 100644
index 0000000..9ea86b8
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java
@@ -0,0 +1,111 @@
+package com.example.serial;
+import static java.lang.Thread.sleep;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.graphics.PorterDuff;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class LedActivity extends AppCompatActivity {
+ private boolean redTag = false;
+ private boolean greenTag = false;
+ private boolean yellowTag = false;
+ private Button ledRed = null;
+ private Button ledGreen = null;
+ private Button ledYellow = null;
+ private ImageView imageRed = null;
+ private ImageView imageGreen = null;
+ private ImageView imageYellow = null;
+ private LedControl ledControl;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_led);
+
+ ledRed = (Button)findViewById(R.id.red);
+ ledYellow = (Button)findViewById(R.id.yellow);
+ ledGreen = (Button)findViewById(R.id.green);
+
+ imageRed = findViewById(R.id.imageRed);
+ imageYellow = findViewById(R.id.imageYellow);
+ imageGreen = findViewById(R.id.imageGreen);
+
+ ledControl = new LedControl();
+ ledControl.ledCtrl(1, 0);
+ try {
+ sleep(1);
+ ledControl.ledCtrl(1, 1);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+
+ ledRed.setOnClickListener(new View.OnClickListener(){
+ @Override
+ public void onClick(View v) {
+ redTag = !redTag;
+ if( redTag ) {
+ if(ledControl.ledCtrl(0, 0) < 0)
+ Toast.makeText(LedActivity.this, "点亮红灯失败", Toast.LENGTH_SHORT).show();
+ else
+ imageRed.setColorFilter(getResources().getColor(android.R.color.holo_red_light), PorterDuff.Mode.SRC_IN);
+ }else{
+ if(ledControl.ledCtrl(0, 1) < 0)
+ Toast.makeText(LedActivity.this, "关闭红灯失败", Toast.LENGTH_SHORT).show();
+ else
+ imageRed.setColorFilter(getResources().getColor(android.R.color.darker_gray), PorterDuff.Mode.SRC_IN);
+ }
+ }
+ });
+ ledYellow.setOnClickListener(new View.OnClickListener(){
+ @Override
+ public void onClick(View v) {
+ yellowTag = !yellowTag;
+ if( yellowTag ) {
+ if( ledControl.ledCtrl(1, 0) < 0){
+ Toast.makeText(LedActivity.this, "点亮黄灯失败!", Toast.LENGTH_SHORT).show();
+ }else {
+ imageYellow.setColorFilter(getResources().getColor(android.R.color.holo_orange_light), PorterDuff.Mode.SRC_IN);
+ }
+ }else{
+ if( ledControl.ledCtrl(1, 1) < 0 ){
+ Toast.makeText(LedActivity.this, "关闭黄灯失败!", Toast.LENGTH_SHORT).show();
+ }else
+ imageYellow.setColorFilter(getResources().getColor(android.R.color.darker_gray), PorterDuff.Mode.SRC_IN);
+ }
+ }
+ });
+
+ ledGreen.setOnClickListener(new View.OnClickListener(){
+ @Override
+ public void onClick(View v) {
+ greenTag = !greenTag;
+ if( greenTag ) {
+ if(ledControl.ledCtrl(2, 0) < 0)
+ Toast.makeText(LedActivity.this, "打开绿灯失败", Toast.LENGTH_SHORT).show();
+ else
+ imageGreen.setColorFilter(getResources().getColor(android.R.color.holo_green_light), PorterDuff.Mode.SRC_IN);
+ }else{
+ if(ledControl.ledCtrl(2, 1) < 0)
+ Toast.makeText(LedActivity.this, "关闭绿灯失败", Toast.LENGTH_SHORT).show();
+ else
+ imageGreen.setColorFilter(getResources().getColor(android.R.color.darker_gray), PorterDuff.Mode.SRC_IN);
+ }
+ }
+ });
+
+ ledControl.ledCtrl(0, 1);
+ ledControl.ledCtrl(1, 1);
+ ledControl.ledCtrl(2, 1);
+ }
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java b/app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java
new file mode 100644
index 0000000..a08b756
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java
@@ -0,0 +1,12 @@
+package com.example.serial;
+public class LedControl {
+ static{
+ System.loadLibrary("serial");
+ }
+ public static native int ledOpen();
+ public static native int ledCtrl(int which, int status);
+
+ static{
+ System.loadLibrary("gpiod");
+ }
+}
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java
new file mode 100644
index 0000000..051fea9
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java
@@ -0,0 +1,70 @@
+package com.example.serial;
+
+import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.example.serial.databinding.ActivityMainBinding;
+
+public class MainActivity extends AppCompatActivity {
+ private Button mBtnRS485;
+ private Button mBtnCan;
+ private Button mBtnLed;
+ private Button mBtnBuzzer;
+ private Button mBtnAdc;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState){
+ super.onCreate(savedInstanceState);
+ EdgeToEdge.enable(this);
+ setContentView(R.layout.activity_menu);
+
+ mBtnBuzzer = findViewById(R.id.btn_buzzer);
+ mBtnAdc = findViewById(R.id.btn_adc);
+ mBtnCan = findViewById(R.id.btn_can);
+ mBtnLed = findViewById(R.id.btn_led);
+ mBtnRS485 = findViewById(R.id.btn_rs485);
+
+ setListener();
+ }
+
+ private void setListener(){
+ OnClick onClick = new OnClick();
+
+ mBtnRS485.setOnClickListener(onClick);
+ mBtnLed.setOnClickListener(onClick);
+ mBtnCan.setOnClickListener(onClick);
+ mBtnBuzzer.setOnClickListener(onClick);
+ mBtnAdc.setOnClickListener(onClick);
+ }
+
+ private class OnClick implements View.OnClickListener{
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ public void onClick(View v) {
+ Intent intent = null;
+ int id = v.getId();
+ if (id == R.id.btn_rs485){
+ intent = new Intent(MainActivity.this, RS485Activity.class);
+ }else if (id == R.id.btn_can){
+ intent = new Intent(MainActivity.this, CanActivity.class);
+ }else if (id == R.id.btn_buzzer) {
+ intent = new Intent(MainActivity.this, BuzzerActivity.class);
+ }else if (id == R.id.btn_led) {
+ intent = new Intent(MainActivity.this, LedActivity.class);
+ }else if (id == R.id.btn_adc) {
+ intent = new Intent(MainActivity.this, AdcActivity.class);
+ }
+ startActivity(intent);
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java
new file mode 100644
index 0000000..0f23957
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java
@@ -0,0 +1,69 @@
+package com.example.serial;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+
+import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+
+public class MenuActivity extends AppCompatActivity {
+ private Button mBtnRS485;
+ private Button mBtnCan;
+ private Button mBtnLed;
+ private Button mBtnBuzzer;
+ private Button mBtnAdc;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState){
+ super.onCreate(savedInstanceState);
+ EdgeToEdge.enable(this);
+ setContentView(R.layout.activity_menu);
+
+ mBtnBuzzer = findViewById(R.id.btn_buzzer);
+ mBtnAdc = findViewById(R.id.btn_adc);
+ mBtnCan = findViewById(R.id.btn_can);
+ mBtnLed = findViewById(R.id.btn_led);
+ mBtnRS485 = findViewById(R.id.btn_rs485);
+
+ setListener();
+ }
+
+ private void setListener(){
+ OnClick onClick = new OnClick();
+
+ mBtnRS485.setOnClickListener(onClick);
+ mBtnLed.setOnClickListener(onClick);
+ mBtnCan.setOnClickListener(onClick);
+ mBtnBuzzer.setOnClickListener(onClick);
+ mBtnAdc.setOnClickListener(onClick);
+ }
+
+ private class OnClick implements View.OnClickListener{
+ @SuppressLint("NonConstantResourceId")
+ @Override
+ public void onClick(View v) {
+ Intent intent = null;
+ int id = v.getId();
+ if (id == R.id.btn_rs485){
+ intent = new Intent(MenuActivity.this, RS485Activity.class);
+ }else if (id == R.id.btn_can){
+ intent = new Intent(MenuActivity.this, CanActivity.class);
+ }else if (id == R.id.btn_buzzer) {
+ intent = new Intent(MenuActivity.this, BuzzerActivity.class);
+ }else if (id == R.id.btn_led) {
+ intent = new Intent(MenuActivity.this, LedActivity.class);
+ }else if (id == R.id.btn_adc) {
+ intent = new Intent(MenuActivity.this, AdcActivity.class);
+ }
+ startActivity(intent);
+
+ }
+ }
+}
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java b/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java
new file mode 100644
index 0000000..48f6354
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java
@@ -0,0 +1,347 @@
+
+package com.example.serial;
+
+import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.example.serial.databinding.ActivityMainBinding;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+public class RS485Activity extends AppCompatActivity {
+
+ // Used to load the 'serial' library on application startup.
+ static {
+ System.loadLibrary("serial");
+ }
+
+ // 与串口参数相关的组件
+ private ActivityMainBinding binding;
+ private Spinner mSpBaud;
+ private Spinner mSpSerial;
+ private Spinner mSpData;
+ private Spinner mSpStop;
+ private Spinner mSpParity;
+ private Spinner mSpFlow;
+
+ // 和发送信息相关的控件
+ private Button mBtnClearSend;
+ private Button mBtnSend;
+ private EditText mEtSend;
+ // 和接收信息相关的控件
+ private Button mBtnClearRev;
+ private TextView mTvRev;
+ private TextView mTvShow;
+ private Button mBtnRecv;
+
+ // 打开串口&关闭串口
+ private Button mBtnOpen;
+ private Button mBtnClose;
+ private Button mBtnSendState;
+ private Button mBtnRecvState;
+
+
+ // 设置串口的参数
+ private String serial;
+ private long baudRate;
+ private int dataBits;
+ private double stopBits;
+ private int parity;
+ private int flowControl; // 0:None, 1:RTS/CTS, 2:XON/XOFF
+
+ // 获取已经设定的值
+ String[] serialArray;
+ String[] baudArray;
+ String[] dataArray;
+ String[] stopArray;
+ String[] parityArray;
+ String[] flowArray;
+ String sendData;
+ String currenState = null;
+ int sendOnTime;
+ int state = -1;
+ private static final int MSG_SEND_DATA = 1;
+
+ boolean turnFlag = false;
+ private RS485Control RS485Control;
+ Runnable runnable;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ EdgeToEdge.enable(this);
+ setContentView(R.layout.activity_rs485);
+
+ RS485Control = new RS485Control();
+ // 找到控件
+ mSpSerial = findViewById(R.id.sp_serial);
+ mSpBaud = findViewById(R.id.sp_baud);
+ mSpData = findViewById(R.id.sp_data);
+ mSpStop = findViewById(R.id.sp_stop);
+ mSpParity = findViewById(R.id.sp_parity);
+ mSpFlow = findViewById(R.id.sp_flow);
+
+ mBtnOpen = findViewById(R.id.btn_open);
+ mBtnClose = findViewById(R.id.btn_close);
+ mBtnClearRev = findViewById(R.id.btn_clear_rev);
+ mBtnClearSend = findViewById(R.id.btn_clear_send);
+ mBtnSend = findViewById(R.id.btn_send);
+ mEtSend = findViewById(R.id.et_send);
+ mTvRev = findViewById(R.id.txt_rev);
+ mTvShow = findViewById(R.id.txt_show);
+ mBtnRecv = findViewById(R.id.btn_recv);
+ mBtnRecvState = findViewById(R.id.btn_recv_state);
+ mBtnSendState = findViewById(R.id.btn_send_state);
+
+ // 还没有连接串口时,显示not connected
+ mTvShow.setTextColor(Color.parseColor("#FF0000"));
+ mTvShow.setText("NOT CONNECTED");
+ mTvShow.setPaintFlags(mTvShow.getPaintFlags() | Paint.ANTI_ALIAS_FLAG);
+ // 设置并监听下拉列表
+ setSpinner(mSpSerial);
+ setSpinner(mSpBaud);
+ setSpinner(mSpData);
+ setSpinner(mSpStop);
+ setSpinner(mSpParity);
+ setSpinner(mSpFlow);
+
+ mTvRev.setMovementMethod(ScrollingMovementMethod.getInstance());
+
+ // 监听所有的button
+ setListener();
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ RS485Control.closeSerialPort();
+ }
+
+ private void setSpinner(Spinner spinner){
+ ArrayAdapter<CharSequence> adapter;
+ int id = spinner.getId();
+
+ if (id == R.id.sp_serial){
+ adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.serial_name, android.R.layout.simple_spinner_item);
+ }else if (id == R.id.sp_baud) {
+ adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.baud_rate, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.sp_data) {
+ adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.data_bits, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.sp_stop) {
+ adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.stop_bits, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.sp_parity) {
+ adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.parity, android.R.layout.simple_spinner_item);
+ } else if (id == R.id.sp_flow) {
+ adapter = ArrayAdapter.createFromResource(RS485Activity.this, R.array.flow_control, android.R.layout.simple_spinner_item);
+ } else {
+ throw new IllegalStateException("Unexpected value: " + spinner.getId());
+ }
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_item);
+ spinner.setAdapter(adapter);
+
+ spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ int spinnerId = spinner.getId();
+
+ if (spinnerId == R.id.sp_serial) {
+ serialArray = getResources().getStringArray(R.array.serial_name);
+ serial = serialArray[position];
+// Toast.makeText(MainActivity.this, "串口:" + serial, Toast.LENGTH_SHORT).show();
+ } else if (spinnerId == R.id.sp_baud) {
+ baudArray = getResources().getStringArray(R.array.baud_rate);
+ baudRate = Integer.parseInt(baudArray[position]);
+// Toast.makeText(MainActivity.this, "波特率:" + baudRate, Toast.LENGTH_SHORT).show();
+ } else if (spinnerId == R.id.sp_data) {
+ dataArray = getResources().getStringArray(R.array.data_bits);
+ dataBits = Integer.parseInt(dataArray[position]);
+// Toast.makeText(MainActivity.this, "数据位:" + dataBits, Toast.LENGTH_SHORT).show();
+ } else if (spinnerId == R.id.sp_stop) {
+ stopArray = getResources().getStringArray(R.array.stop_bits);
+ stopBits = Double.parseDouble(stopArray[position]);
+// Toast.makeText(MainActivity.this, "停止位:" + stopBits, Toast.LENGTH_SHORT).show();
+ } else if (spinnerId == R.id.sp_parity) {
+ parityArray = getResources().getStringArray(R.array.parity);
+ if (parityArray[position].equals("偶校验"))
+ parity = 2;
+ else if (parityArray[position].equals("奇校验"))
+ parity = 1;
+ else if (parityArray[position].equals("None"))
+ parity = 0;
+// Toast.makeText(MainActivity.this, "校验位:" + parityArray[position], Toast.LENGTH_SHORT).show();
+ } else if (spinnerId == R.id.sp_flow) {
+ flowArray = getResources().getStringArray(R.array.flow_control);
+ flowControl = position;
+ } else {
+ throw new IllegalStateException("Unexpected value: " + spinner.getId());
+ }
+ }
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+ }
+
+ private void setListener(){
+ OnClick onClick = new OnClick();
+ mBtnOpen.setOnClickListener(onClick);
+ mBtnClose.setOnClickListener(onClick);
+ mBtnClearRev.setOnClickListener(onClick);
+ mBtnClearSend.setOnClickListener(onClick);
+ mBtnSend.setOnClickListener(onClick);
+ mBtnRecvState.setOnClickListener(onClick);
+ mBtnSendState.setOnClickListener(onClick);
+ mBtnRecv.setOnClickListener(onClick);
+ }
+ private class OnClick implements View.OnClickListener{
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.btn_open) {
+ if (turnFlag) {
+ Toast.makeText(RS485Activity.this, "串口已打开, 不能重复打开", Toast.LENGTH_SHORT).show();
+ }else {
+ int rv = RS485Control.openSerialPort(serial, (int) baudRate, dataBits, parity, (int) stopBits, flowControl);
+ if(rv == 1){
+ Toast.makeText(RS485Activity.this, "请添加"+serial+"的权限", Toast.LENGTH_SHORT).show();
+ }else if (rv < 0) {
+ Toast.makeText(RS485Activity.this, "打开串口失败", Toast.LENGTH_SHORT).show();
+ } else {
+ turnFlag = true;
+// startReceiving(); // 串口打开后默认开始读
+ serialOpened();
+ Toast.makeText(RS485Activity.this, "成功打开串口", Toast.LENGTH_SHORT).show();
+ }
+ }
+ } else if (id == R.id.btn_close){
+ if (!turnFlag) {
+ Toast.makeText(RS485Activity.this, "串口已关闭,不能重复关闭", Toast.LENGTH_SHORT).show();
+ }else {
+ // 关闭串口
+ int rv = RS485Control.closeSerialPort();
+// Toast.makeText(MainActivity.this, "close", Toast.LENGTH_SHORT).show();
+ if (rv < 0) {
+ Toast.makeText(RS485Activity.this, "关闭串口失败", Toast.LENGTH_SHORT).show();
+ } else {
+ turnFlag = false;
+ serialClosed();
+ Toast.makeText(RS485Activity.this, "关闭串口", Toast.LENGTH_SHORT).show();
+ }
+ }
+ } else if (id == R.id.btn_clear_rev) {
+ // 清空接收区数据
+ mTvRev.setText("");
+ } else if (id == R.id.btn_clear_send) {
+ // 清空发送区内的数据
+ mEtSend.getText().clear();
+ } else if (id == R.id.btn_send) {
+ // 读取发送区中的数据
+ sendData = mEtSend.getText().toString();
+
+ // 判断是否已连接串口, 如果没有连接串口则需先连接串口
+ if( !turnFlag ){
+ Toast.makeText(RS485Activity.this, "串口未打开,请打开串口!", Toast.LENGTH_SHORT).show();
+ }else if(sendData.isEmpty()){
+ // 判断发送区是否为空,如果为空则弹出提示
+ Toast.makeText(RS485Activity.this, "发送区为空,请输入数据!", Toast.LENGTH_SHORT).show();
+ } else if (state != 0) {
+ Toast.makeText(RS485Activity.this, "串口未处于发送状态,不能发送数据!", Toast.LENGTH_SHORT).show();
+ } else {
+ int rv = RS485Control.sendToPort(sendData, sendData.length());
+ if( rv < 0 )
+ {
+ Toast.makeText(RS485Activity.this, "发送数据失败!", Toast.LENGTH_SHORT).show();
+ }
+ Toast.makeText(RS485Activity.this, "发送数据成功", Toast.LENGTH_SHORT).show();
+
+ }
+ } else if (id == R.id.btn_send_state) {
+ // 当gpio4-io26为0时,处于发送模式
+ if( RS485Control.changeState(0) < 0 ){
+ Log.e("Change State for send", "Change State as send failed");
+ }else {
+ state = 0;
+ if (turnFlag)
+ serialOpened();
+ else
+ serialClosed();
+ }
+ } else if (id == R.id.btn_recv_state) {
+ // 当gpio4-io26为1时,处于接收模式
+ if (RS485Control.changeState(1) < 0) {
+ Log.e("Change State for recv", "Change State as recv failed");
+ } else {
+ state = 1;
+ if (turnFlag)
+ serialOpened();
+ else
+ serialClosed();
+ }
+ } else if (id == R.id.btn_recv) {
+ // 当按下按钮后接收一次数据
+ int len = 1024;
+ int timeout = 10;
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ df.setTimeZone(TimeZone.getTimeZone("GMT+8"));
+ String date = df.format(new Date());
+ String buf = RS485Control.recvFromPort(len, timeout); // 超时时间设置为 1000 ms
+ if (buf != null) {
+ String revData = date + "\t" + buf;
+ String currentText = mTvRev.getText().toString();
+ String newText = currentText + "\n" + revData; // 追加新数据
+// mTvRev.setText(newText);
+ mTvRev.append("\n" + revData);
+ mTvRev.invalidate();
+ mTvRev.requestLayout();
+ } else if (buf == null) {
+ System.err.println("Error receiving data.");
+ }
+ }
+ }
+ }
+
+ private void serialClosed(){
+ mTvShow.setTextColor(Color.parseColor("#FF0000"));
+ if(currenState == null){
+ mTvShow.setText("NOT CONNECTED");
+ } else if (currenState.equals("recv"))
+ mTvShow.setText("NOT CONNECTED"+"接收模式");
+ else if (currenState.equals("send")) {
+ mTvShow.setText("NOT CONNECTED"+"发送模式");
+ }
+ mTvShow.setPaintFlags(mTvShow.getPaintFlags() | Paint.ANTI_ALIAS_FLAG);
+ }
+
+ private void serialOpened(){
+ mTvShow.setTextColor(Color.parseColor("#1FA324"));
+ if (state == 1)
+ mTvShow.setText(serial+" OPENED, "+baudRate+", "+dataBits+", "+stopBits+", "+parity+", "+"接收模式");
+ else if (state == 0) {
+ mTvShow.setText(serial+" OPENED, "+baudRate+", "+dataBits+", "+stopBits+", "+parity+", "+"发送模式");
+ }else{
+ mTvShow.setText(serial+" OPENED, "+baudRate+", "+dataBits+", "+stopBits+", "+parity);
+ }
+
+ mTvShow.setPaintFlags(mTvShow.getPaintFlags() | Paint.ANTI_ALIAS_FLAG);
+ }
+}
diff --git a/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java b/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java
new file mode 100644
index 0000000..e99cb92
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java
@@ -0,0 +1,18 @@
+package com.example.serial;
+
+public class RS485Control {
+ static {
+ System.loadLibrary("serial");
+ }
+
+ public static native int openSerialPort(String path, long baudRate, int dataBits, int parity, int stopBits, int flowControl);
+ public static native int closeSerialPort();
+
+ public static native int sendToPort(String msg, int len);
+ public static native String recvFromPort(int len, int timeout);
+
+ public static native int changeState(int state); //
+
+ public static native String saveToFile(String msg, int len, String fileName);
+
+}
diff --git a/app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so b/app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so
new file mode 100644
index 0000000..e1a7642
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/drawable/background.xml b/app_sdk/app_src/app/src/main/res/drawable/background.xml
new file mode 100644
index 0000000..c790b42
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:color="@color/white"
+ android:width="2dp"/>
+ <corners
+ android:radius="5dp"/>
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/bg1.jpg b/app_sdk/app_src/app/src/main/res/drawable/bg1.jpg
new file mode 100644
index 0000000..b7ede45
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/bg1.jpg
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/drawable/bg2.jpg b/app_sdk/app_src/app/src/main/res/drawable/bg2.jpg
new file mode 100644
index 0000000..85df69e
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/bg2.jpg
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml b/app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml
new file mode 100644
index 0000000..ad1402a
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+<!-- <solid-->
+<!-- android:color="#FFFFFF"/>-->
+ <stroke
+ android:width="2dp"
+ android:color="#BDB6B9"/>
+ <corners
+ android:radius="5dp"/>
+ <padding
+ android:bottom="0.1dp"
+ android:left="0.5dp"
+ android:right="0.5dp"
+ android:top="0dp"/>
+ <solid
+ android:color="#E7D9EA"/>
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/bg_username.xml b/app_sdk/app_src/app/src/main/res/drawable/bg_username.xml
new file mode 100644
index 0000000..6395d2c
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/bg_username.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:width="1.5dp"
+ android:color="@color/white"
+ />
+
+
+ <corners
+ android:radius="5dp"
+ />
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml b/app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml
new file mode 100644
index 0000000..e1333cd
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:width="1dp"
+ android:color="@color/white"
+ />
+
+ <corners
+ android:radius="10dp"
+ />
+
+ <solid
+ android:color="@color/white"/>
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/bt_left.xml b/app_sdk/app_src/app/src/main/res/drawable/bt_left.xml
new file mode 100644
index 0000000..e792ddf
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/bt_left.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:width="1.5dp"
+ android:color="@color/white"
+ />
+
+ <corners
+ android:topRightRadius="5dp"
+ android:bottomRightRadius="5dp"
+ />
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/bt_right.xml b/app_sdk/app_src/app/src/main/res/drawable/bt_right.xml
new file mode 100644
index 0000000..77f8f76
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/bt_right.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:width="1.5dp"
+ android:color="@color/white"
+ />
+
+ <corners
+ android:topLeftRadius="5dp"
+ android:bottomLeftRadius="5dp"
+ />
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/button_selector.xml b/app_sdk/app_src/app/src/main/res/drawable/button_selector.xml
new file mode 100644
index 0000000..9b7fd4c
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/button_selector.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true"
+ android:drawable="@drawable/shape_pressed"/>
+ <item android:state_checked="false"
+ android:drawable="@drawable/shape_normal"/>
+</selector>
diff --git a/app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml b/app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml
new file mode 100644
index 0000000..0888d6a
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:width="2dp"
+ android:color="@color/white"/>
+ <solid
+ android:color="@color/white"/>
+ <corners
+ android:radius="5dp"/>
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml b/app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml
new file mode 100644
index 0000000..0ebc7e9
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:width="1dp"
+ android:color="@color/black"
+ />
+ <solid
+ android:color="@color/white"/>
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml b/app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml
new file mode 100644
index 0000000..b8829df
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="90"
+ android:endColor="#DCD6D6"
+ android:centerColor="#CEB7EA"
+ android:startColor="@color/purple_700" />
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml b/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M9,0L9,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,0L19,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,0L29,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,0L39,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,0L49,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,0L59,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,0L69,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,0L79,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M89,0L89,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M99,0L99,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,9L108,9"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,19L108,19"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,29L108,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,39L108,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,49L108,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,59L108,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,69L108,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,79L108,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,89L108,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,99L108,99"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,29L89,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,39L89,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,49L89,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,59L89,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,69L89,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,79L89,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,19L29,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,19L39,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,19L49,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,19L59,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,19L69,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,19L79,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml b/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="85.84757"
+ android:endY="92.4963"
+ android:startX="42.9492"
+ android:startY="49.59793"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000" />
+</vector>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml b/app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml
new file mode 100644
index 0000000..b470b19
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:width="2dp"
+ android:color="@color/white"/>
+ <corners
+ android:radius="10dp"/>
+ <solid
+ android:color="@color/white"/>
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml b/app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml
new file mode 100644
index 0000000..1ec89ac
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/colorButtonNormal" />
+ <stroke android:width="1dip" android:color="@color/white"/>
+ <!-- 圆角 -->
+ <corners android:radius="10dp" />
+ <!-- 边距 -->
+ <padding
+ android:bottom="5dp"
+ android:left="5dp"
+ android:right="5dp"
+ android:top="5dp" />
+</shape>
diff --git a/app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml b/app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml
new file mode 100644
index 0000000..c2eb775
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/colorButtonPressed" />
+ <stroke android:width="1dip" android:color="@color/colorButtonPressed"/>
+ <!-- 圆角 -->
+ <corners android:radius="10dp" />
+ <!-- 边距 -->
+ <padding
+ android:bottom="5dp"
+ android:left="5dp"
+ android:right="5dp"
+ android:top="5dp" />
+</shape>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_adc.xml b/app_sdk/app_src/app/src/main/res/layout/activity_adc.xml
new file mode 100644
index 0000000..0bc116f
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/layout/activity_adc.xml
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/gradient_bg"
+ android:orientation="vertical"
+ tools:context=".MainActivity"
+ android:hardwareAccelerated="true">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="ADC通信"
+ android:textSize="25sp"
+ android:textColor="#673AB7"
+ android:layout_marginTop="20dp"
+ android:layout_marginRight="10dp"
+ android:layout_marginLeft="10dp"
+ android:layout_gravity="center"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="串口设置"
+ android:textSize="17sp"
+ android:textColor="#E91E63"
+ android:layout_marginRight="10dp"
+ android:layout_marginLeft="10dp"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:background="@drawable/bg_frame"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:baselineAligned="false"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="串口号"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/adc_sp_serial"
+ android:layout_width="150dp"
+ android:layout_height="40dp"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_gravity="right"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="波特率"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/adc_sp_baud"
+ android:layout_width="150dp"
+ android:layout_height="40dp"
+ android:gravity="center" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="数据位"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/adc_sp_data"
+ android:layout_width="150dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="停止位"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/adc_sp_stop"
+ android:layout_width="150dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_weight="1"
+ android:layout_gravity="right"
+ android:layout_marginLeft="30dp"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="校验位"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/adc_sp_parity"
+ android:layout_width="150dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:layout_marginRight="30dp">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="流控制"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/adc_sp_flow"
+ android:layout_width="150dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="串口开/关"
+ android:textColor="#172DA6"
+ android:textSize="17sp"
+ android:layout_marginLeft="10dp"/>
+ <TextView
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="操作设置"
+ android:textColor="#009688"
+ android:textSize="17sp"
+ android:layout_marginLeft="40dp"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="5dp">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:background="@drawable/bg_frame"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/adc_btn_open"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:text="打开串口"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"
+ android:layout_marginRight="40dp"/>
+ <Button
+ android:id="@+id/adc_btn_close"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:text="关闭串口"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="@drawable/bg_frame"
+ android:layout_marginRight="10dp">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:layout_marginRight="0dp"
+ android:gravity="center">
+ <TextView
+ android:layout_width="100dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="电流(1)/电压(2)"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/adc_sp_operate"
+ android:layout_width="100dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:layout_marginRight="0dp"
+ android:gravity="center">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="通道"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/adc_sp_channel"
+ android:layout_width="100dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="right">
+ <Button
+ android:id="@+id/adc_btn_send"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:text="发送"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"
+ android:layout_gravity="right"
+ android:layout_marginRight="20dp"/>
+ <Button
+ android:id="@+id/adc_btn_recv"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:text="清空缓冲区"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"
+ android:layout_gravity="right"
+ android:layout_marginRight="20dp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <ScrollView
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/adc_txt_rev"
+ android:layout_width="match_parent"
+ android:layout_height="100dp"
+ android:layout_marginLeft="10dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginRight="10dp"
+ android:background="@color/white"
+ android:hint="接收数据显示"
+ android:textColor="@color/black"
+ android:textSize="15sp" />
+ </LinearLayout>
+ </ScrollView>
+ </LinearLayout>
+
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml b/app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml
new file mode 100644
index 0000000..af3db71
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/sample_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="蜂鸣器"
+ android:textSize="40sp"
+ android:layout_marginTop="50dp"
+ android:layout_gravity="center"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginLeft="100dp"
+ android:layout_marginRight="100dp">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="50dp"
+ android:layout_marginLeft="150dp">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="周期(ns):"
+ android:textSize="20sp"
+ android:textColor="@color/black"
+ />
+ <EditText
+ android:id="@+id/period"
+ android:layout_width="200dp"
+ android:layout_height="wrap_content"
+ android:textSize="15sp"
+ android:hint="1000000"
+
+ android:textColor="@color/black"
+ android:layout_marginLeft="10dp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="50dp">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="占空比(ns):"
+ android:textSize="20sp"
+ android:textColor="@color/black"
+ />
+ <EditText
+ android:id="@+id/duty_cycle"
+ android:layout_width="200dp"
+ android:layout_height="wrap_content"
+ android:textSize="15sp"
+ android:hint="50000"
+ android:textColor="@color/black"
+ android:layout_marginLeft="10dp"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="20dp"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/play"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="播放"
+ android:textSize="15sp"
+ />
+
+ <Button
+ android:id="@+id/stop"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="暂停"
+ android:textSize="15sp"
+ android:layout_marginLeft="100dp"/>
+ </LinearLayout>
+
+
+
+
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_can.xml b/app_sdk/app_src/app/src/main/res/layout/activity_can.xml
new file mode 100644
index 0000000..1d49412
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/layout/activity_can.xml
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_gravity="center"
+ tools:context=".MainActivity"
+ android:background="@drawable/bg1">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="CAN通信"
+ android:layout_gravity="center"
+ android:textColor="@color/white"
+ android:textSize="20dp"
+ android:textStyle="bold"
+ android:layout_marginTop="30dp"/>
+ <TextView
+ android:layout_marginTop="10dp"
+ android:id="@+id/sample_text"
+ android:layout_marginLeft="30dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="发送数据设置"
+ android:textSize="20sp"
+ android:textColor="@color/white"/>
+
+ <LinearLayout
+ android:gravity="center"
+ android:layout_marginLeft="30dp"
+ android:layout_marginRight="30dp"
+ android:layout_marginTop="10dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="@drawable/background">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="10dp"
+ android:gravity="left">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="20dp"
+ android:orientation="horizontal"
+ android:gravity="left">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="CAN:"
+ android:textColor="@color/black"
+ android:gravity="center"/>
+ <Spinner
+ android:id="@+id/sp_can"
+ android:layout_width="120dp"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="5dp"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="ID(HEX):"
+ android:textSize="15sp"
+ android:gravity="center"
+ android:textColor="@color/black"/>
+ <EditText
+ android:id="@+id/can_ed_id"
+ android:layout_width="60dp"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:layout_marginLeft="5dp"
+ android:text="000"
+ android:textSize="15sp"
+ android:textColor="@color/black"
+ android:background="@drawable/ed_bacground"
+ />
+ </LinearLayout>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginBottom="10dp"
+ android:layout_marginTop="5dp"
+ android:layout_marginLeft="13dp"
+ android:gravity="left">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="DLC(0...7):"
+ android:layout_gravity="left"
+ android:textSize="15sp"
+ android:textColor="@color/black"/>
+ <EditText
+ android:id="@+id/can_ed_dlc"
+ android:layout_width="90dp"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ android:text="2"
+ android:textSize="15sp"
+ android:layout_marginLeft="5dp"
+ android:textColor="@color/black"
+ android:background="@drawable/ed_bacground"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="3"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:layout_marginLeft="190dp">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Data(HEX):"
+ android:textSize="15sp"
+ android:textColor="@color/black"/>
+ <EditText
+ android:id="@+id/can_ed_data"
+ android:layout_width="300dp"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dp"
+ android:text=" 00 00 "
+ android:textSize="15sp"
+ android:textColor="@color/black"
+ android:background="@drawable/ed_bacground"
+ />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_marginTop="10dp"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_gravity="center">
+ <Button
+ android:id="@+id/can_send"
+ android:layout_width="wrap_content"
+ android:layout_height="40dp"
+ android:layout_marginLeft="50dp"
+ android:background="@drawable/bt_bg"
+ android:text="发送"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"/>
+ <Button
+ android:id="@+id/can_send_clear"
+ android:layout_width="wrap_content"
+ android:layout_height="40dp"
+ android:layout_marginRight="50dp"
+ android:layout_marginLeft="50dp"
+ android:layout_gravity="right"
+ android:background="@drawable/bt_bg"
+ android:text="清空发送区"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_marginTop="10dp"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_gravity="center">
+ <Button
+ android:id="@+id/can_recv"
+ android:layout_width="wrap_content"
+ android:layout_height="40dp"
+ android:background="@drawable/bt_bg"
+ android:text="接收"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"/>
+ <Button
+ android:id="@+id/can_recv_clear"
+ android:layout_width="wrap_content"
+ android:layout_height="40dp"
+ android:layout_marginRight="50dp"
+ android:layout_marginLeft="50dp"
+ android:layout_gravity="right"
+ android:background="@drawable/bt_bg"
+ android:text="清空接收区"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="10dp">
+ <ScrollView
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="150dp">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:layout_marginRight="30dp"
+ android:layout_marginLeft="30dp">
+ <TextView
+ android:layout_width="25dp"
+ android:layout_height="150dp"
+ android:text="发送区"
+ android:textSize="15sp"
+ android:background="@color/white"/>
+ <TextView
+ android:id="@+id/can_tv_send"
+ android:layout_width="340dp"
+ android:layout_height="150dp"
+ android:background="@color/white"
+ />
+ </LinearLayout>
+ </ScrollView>
+
+ <ScrollView
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="150dp">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:layout_marginRight="30dp">
+ <TextView
+ android:layout_width="25dp"
+ android:layout_height="150dp"
+ android:text="接收区"
+ android:textSize="15sp"
+ android:background="@color/white"/>
+ <TextView
+ android:id="@+id/can_tv_recv"
+ android:layout_width="340dp"
+ android:layout_height="150dp"
+ android:background="@color/white"
+ />
+ </LinearLayout>
+ </ScrollView>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_led.xml b/app_sdk/app_src/app/src/main/res/layout/activity_led.xml
new file mode 100644
index 0000000..946e27f
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/layout/activity_led.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".LedActivity">
+
+ <Button
+ android:id="@+id/yellow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="yellow"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.506"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.519" />
+
+ <Button
+ android:id="@+id/red"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="red"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.146"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.519" />
+
+ <TextView
+ android:id="@+id/sample_text"
+ android:layout_width="200dp"
+ android:layout_height="45dp"
+ android:text="LedDemo RK3568"
+ android:textSize="20sp"
+ android:gravity="center"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.286" />
+
+ <Button
+ android:id="@+id/green"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="green"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.873"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.519" />
+
+ <ImageView
+ android:id="@+id/imageRed"
+ android:layout_width="45dp"
+ android:layout_height="45dp"
+ app:srcCompat="@android:drawable/presence_invisible"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.15"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.4"
+ tools:layout_editor_absoluteX="59dp"
+ tools:layout_editor_absoluteY="289dp" />
+
+ <ImageView
+ android:id="@+id/imageGreen"
+ android:layout_width="45dp"
+ android:layout_height="45dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.86"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.4"
+ app:srcCompat="@android:drawable/presence_invisible" />
+
+ <ImageView
+ android:id="@+id/imageYellow"
+ android:layout_width="45dp"
+ android:layout_height="45dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.4"
+ app:srcCompat="@android:drawable/presence_invisible" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_main.xml b/app_sdk/app_src/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..048fd6a
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@drawable/bg2"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:id="@+id/sample_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="RK3568"
+ android:textColor="@color/white"
+ android:textSize="25sp"
+ android:gravity="center"
+ android:maxLines="1"
+ android:maxEms="20"
+ android:ellipsize="end"
+ android:layout_marginTop="100dp"
+ />
+ <EditText
+ android:id="@+id/ed_1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="50dp"
+ android:gravity="left"
+ android:textSize="15sp"
+ android:textColor="@color/white"
+ android:hint="用户名"
+ android:textColorHint="@color/white"
+ android:maxLines="1"
+ android:padding="10dp"
+ android:background="@drawable/bg_username"
+ android:layout_margin="10dp"
+ />
+ <EditText
+ android:id="@+id/ed_2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:gravity="left"
+ android:textSize="15sp"
+ android:textColor="@color/white"
+ android:textColorHint="@color/white"
+ android:hint="密码"
+ android:maxLines="1"
+ android:padding="10dp"
+ android:inputType="textPassword"
+ android:background="@drawable/bg_username"
+ android:layout_margin="10dp"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="20dp">
+
+ <Button
+ android:id="@+id/btn_1"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="登录"
+ android:textColor="@color/black"
+ android:layout_marginLeft="10dp"
+ android:background="@drawable/bt_right"
+ android:theme="@style/MyRadioButton"
+ />
+ <Button
+ android:id="@+id/btn_2"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="注册"
+ android:textColor="@color/black"
+ android:layout_marginLeft="5dp"
+ android:layout_marginRight="10dp"
+ android:background="@drawable/bt_left"
+ android:theme="@style/MyRadioButton"
+ />
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_menu.xml b/app_sdk/app_src/app/src/main/res/layout/activity_menu.xml
new file mode 100644
index 0000000..820beff
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/layout/activity_menu.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:background="@color/white"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:background="#8F5A5A"
+ android:layout_marginTop="20dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="RK3568测试目录"
+ android:textColor="@color/white"
+ android:textSize="20sp"
+ android:textStyle="bold"
+ android:layout_marginLeft="20dp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="15dp"
+ android:layout_marginTop="15dp"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/btn_rs485"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="RS485通信"/>
+
+ <Button
+ android:id="@+id/btn_led"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:text="三色LED灯"/>
+
+ <Button
+ android:id="@+id/btn_can"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:text="CAN通信"/>
+
+ <Button
+ android:id="@+id/btn_buzzer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:text="蜂鸣器"/>
+
+ <Button
+ android:id="@+id/btn_adc"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:text="ADC采样"/>
+
+
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml b/app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml
new file mode 100644
index 0000000..ba0b373
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml
@@ -0,0 +1,423 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/gradient_bg"
+ android:orientation="vertical"
+ tools:context=".RS485Activity"
+ android:hardwareAccelerated="true">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="RS485通信"
+ android:textSize="25sp"
+ android:textColor="#673AB7"
+ android:layout_marginTop="20dp"
+ android:layout_marginRight="10dp"
+ android:layout_marginLeft="10dp"
+ android:layout_gravity="center"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="串口设置"
+ android:textSize="17sp"
+ android:textColor="#E91E63"
+ android:layout_marginTop="0dp"
+ android:layout_marginRight="10dp"
+ android:layout_marginLeft="10dp"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:background="@drawable/bg_frame"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:baselineAligned="false"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="串口号"
+ android:textColor="@color/black"/>
+
+ <Spinner
+ android:id="@+id/sp_serial"
+ android:layout_width="150dp"
+ android:layout_height="40dp"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_gravity="right"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="波特率"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/sp_baud"
+ android:layout_width="150dp"
+ android:layout_height="40dp"
+ android:gravity="center" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="数据位"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/sp_data"
+ android:layout_width="150dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="停止位"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/sp_stop"
+ android:layout_width="150dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_weight="1"
+ android:layout_gravity="right"
+ android:layout_marginLeft="30dp"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="校验位"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/sp_parity"
+ android:layout_width="150dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:layout_marginRight="30dp">
+ <TextView
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="流控制"
+ android:textColor="@color/black"/>
+ <Spinner
+ android:id="@+id/sp_flow"
+ android:layout_width="150dp"
+ android:layout_height="40dp" />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="串口开/关"
+ android:textColor="#172DA6"
+ android:textSize="17sp"
+ android:layout_marginLeft="10dp"/>
+ <TextView
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="串口状态"
+ android:textColor="#009688"
+ android:textSize="17sp"
+ android:layout_marginLeft="40dp"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="5dp">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:background="@drawable/bg_frame"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/btn_open"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:text="打开串口"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"
+ android:layout_marginRight="40dp"/>
+ <Button
+ android:id="@+id/btn_close"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:text="关闭串口"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:background="@drawable/bg_frame"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/btn_send_state"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:text="发送模式"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"
+ android:layout_marginRight="40dp"/>
+ <Button
+ android:id="@+id/btn_recv_state"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:text="接收模式"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginTop="5dp">
+
+ </LinearLayout>
+
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="left"
+ android:layout_marginTop="5dp"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp">
+
+
+
+ <TextView
+ android:id="@+id/txt_show"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:textColor="@color/black"
+ android:textSize="13sp"
+ android:layout_marginLeft="10dp"
+ android:gravity="left|bottom"
+ android:layout_marginBottom="5dp"
+ />
+
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+<!-- <EditText-->
+<!-- android:id="@+id/et_send"-->
+<!-- android:layout_width="0dp"-->
+<!-- android:layout_weight="1"-->
+<!-- android:layout_height="100dp"-->
+<!-- android:layout_marginLeft="10dp"-->
+<!-- android:layout_marginTop="10dp"-->
+<!-- android:layout_marginRight="10dp"-->
+<!-- android:background="@color/white"-->
+<!-- android:hint="发送信息"-->
+<!-- android:textSize="15sp"-->
+<!-- android:textColor="@color/black" />-->
+ <ScrollView
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+ <EditText
+ android:id="@+id/et_send"
+ android:layout_width="match_parent"
+ android:layout_height="100dp"
+ android:layout_marginLeft="10dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginRight="10dp"
+ android:background="@color/white"
+ android:hint="发送信息"
+ android:textSize="15sp"
+ android:textColor="@color/black" />
+ </TableRow>
+ </ScrollView>
+
+ <ScrollView
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/txt_rev"
+ android:layout_width="match_parent"
+ android:layout_height="100dp"
+ android:layout_marginLeft="10dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginRight="10dp"
+ android:background="@color/white"
+ android:textColor="@color/black"
+ />
+ </LinearLayout>
+ </ScrollView>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="10dp">
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="3"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginLeft="10dp">
+
+ <Button
+ android:id="@+id/btn_send"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:layout_marginTop="5dp"
+ android:layout_marginRight="10dp"
+ android:layout_marginLeft="20dp"
+ android:background="@drawable/bt_bg"
+ android:text="发送"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"/>
+
+ <Button
+ android:id="@+id/btn_clear_send"
+ android:layout_width="wrap_content"
+ android:layout_height="45dp"
+ android:layout_marginTop="5dp"
+ android:layout_marginRight="10dp"
+ android:background="@drawable/bt_bg"
+ android:text="清空发送区"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/btn_recv"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="45dp"
+ android:layout_gravity="right"
+ android:layout_marginTop="5dp"
+ android:layout_marginRight="10dp"
+ android:background="@drawable/bt_bg"
+ android:text="接收"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"
+ />
+
+ <Button
+ android:id="@+id/btn_clear_rev"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="45dp"
+ android:layout_gravity="right"
+ android:layout_marginTop="5dp"
+ android:layout_marginRight="10dp"
+ android:background="@drawable/bt_bg"
+ android:text="清空缓冲区"
+ android:textColor="@color/black"
+ android:theme="@style/MyRadioButton"
+ />
+ </LinearLayout>
+ </LinearLayout>
+
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+ <monochrome android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+ <monochrome android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/app_sdk/app_src/app/src/main/res/values-night/themes.xml b/app_sdk/app_src/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..7e90457
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.Serial" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_200</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/black</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_200</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/values/colors.xml b/app_sdk/app_src/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..06e2f5f
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/values/colors.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="purple_200">#FFBB86FC</color>
+ <color name="purple_500">#FF6200EE</color>
+ <color name="purple_700">#FF3700B3</color>
+ <color name="teal_200">#FF03DAC5</color>
+ <color name="teal_700">#FF018786</color>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+ <color name="hui">#1FA324</color>
+ <color name="red">#DC0707</color>
+ <color name="colorButtonPressed">#5D6FD6</color> <!-- 灰色 -->
+ <color name="colorButtonNormal">#FFF7F7</color> <!-- 默认颜色 -->
+</resources>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/values/strings.xml b/app_sdk/app_src/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ac24100
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/values/strings.xml
@@ -0,0 +1,78 @@
+<resources>
+ <string name="app_name">RK3568</string>
+ <string-array name="serial_name">
+ <item>/dev/ttyS9</item>
+ </string-array>
+ <string-array name="adc_serial_name">
+ <item>/dev/ttyS6</item>
+ </string-array>
+ <string-array name="baud_rate">
+ <item>110</item>
+ <item>300</item>
+ <item>600</item>
+ <item>1200</item>
+ <item>2400</item>
+ <item>2400</item>
+ <item>4800</item>
+ <item>9600</item>
+ <item>14400</item>
+ <item>19200</item>
+ <item>38400</item>
+ <item>56000</item>
+ <item>57600</item>
+ <item>115200</item>
+ <item>128000</item>
+ <item>120400</item>
+ <item>256000</item>
+ <item>460800</item>
+ <item>500000</item>
+ <item>512000</item>
+ <item>600000</item>
+ <item>750000</item>
+ <item>921600</item>
+ <item>150000</item>
+ <item>200000</item>
+ </string-array>
+ <string-array name="data_bits">
+ <item>5</item>
+ <item>6</item>
+ <item>7</item>
+ <item>8</item>
+ </string-array>
+ <string-array name="stop_bits">
+ <item>1</item>
+ <item>1.5</item>
+ <item>2</item>
+ </string-array>
+ <string-array name="parity">
+ <item>None</item>
+ <item>奇校验</item>
+ <item>偶校验</item>
+ </string-array>
+ <string-array name="flow_control">
+ <item>None</item>
+ <item>RTS/CTS</item>
+ <item>XON/XOFF</item>
+ </string-array>
+ <string-array name="can_name">
+ <item>can0</item>
+ <item>can1</item>
+ </string-array>
+ <string-array name="bitrate">
+ <item>1000000</item>
+ <item>800000</item>
+ <item>500000</item>
+ <item>2500000</item>
+ <item>100000</item>
+ </string-array>
+ <string-array name="adc_operate">
+ <item>1</item>
+ <item>2</item>
+ </string-array>
+ <string-array name="adc_channel">
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </string-array>
+</resources>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/values/themes.xml b/app_sdk/app_src/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..ab43507
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/values/themes.xml
@@ -0,0 +1,23 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.Serial" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_500</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/white</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_700</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+ <!-- Customize your theme here. -->
+ </style>
+ <style name="MyRadioButton" parent="Theme.AppCompat.Light">
+ <item name="colorControlNormal">@color/black</item>
+ <item name="colorControlActivated">@color/purple_200</item>
+ </style>
+ <style name="MenuButton" parent="Theme.AppCompat.DayNight.Dialog">
+ <item name="colorControlNormal">@color/purple_200</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/xml/backup_rules.xml b/app_sdk/app_src/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..fa0f996
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older that API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<full-backup-content>
+ <!--
+ <include domain="sharedpref" path="."/>
+ <exclude domain="sharedpref" path="device.xml"/>
+-->
+</full-backup-content>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml b/app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample data extraction rules file; uncomment and customize as necessary.
+ See https://developer.android.com/about/versions/12/backup-restore#xml-changes
+ for details.
+-->
+<data-extraction-rules>
+ <cloud-backup>
+ <!-- TODO: Use <include> and <exclude> to control what is backed up.
+ <include .../>
+ <exclude .../>
+ -->
+ </cloud-backup>
+ <!--
+ <device-transfer>
+ <include .../>
+ <exclude .../>
+ </device-transfer>
+ -->
+</data-extraction-rules>
\ No newline at end of file
diff --git a/app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java b/app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java
new file mode 100644
index 0000000..3becf4f
--- /dev/null
+++ b/app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.example.serial;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/build.gradle.kts b/app_sdk/app_src/build.gradle.kts
new file mode 100644
index 0000000..3756278
--- /dev/null
+++ b/app_sdk/app_src/build.gradle.kts
@@ -0,0 +1,4 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+}
\ No newline at end of file
diff --git a/app_sdk/app_src/gradle.properties b/app_sdk/app_src/gradle.properties
new file mode 100644
index 0000000..4387edc
--- /dev/null
+++ b/app_sdk/app_src/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/app_sdk/app_src/gradle/libs.versions.toml b/app_sdk/app_src/gradle/libs.versions.toml
new file mode 100644
index 0000000..ffe5f54
--- /dev/null
+++ b/app_sdk/app_src/gradle/libs.versions.toml
@@ -0,0 +1,22 @@
+[versions]
+agp = "8.4.0"
+junit = "4.13.2"
+junitVersion = "1.1.5"
+espressoCore = "3.5.1"
+appcompat = "1.6.1"
+material = "1.10.0"
+constraintlayout = "2.1.4"
+activity = "1.8.0"
+
+[libraries]
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+
diff --git a/app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar b/app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
--- /dev/null
+++ b/app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties b/app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..b51df17
--- /dev/null
+++ b/app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon May 13 22:16:04 CST 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/app_sdk/app_src/gradlew b/app_sdk/app_src/gradlew
new file mode 100644
index 0000000..4f906e0
--- /dev/null
+++ b/app_sdk/app_src/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/app_sdk/app_src/gradlew.bat b/app_sdk/app_src/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/app_sdk/app_src/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/app_sdk/app_src/settings.gradle.kts b/app_sdk/app_src/settings.gradle.kts
new file mode 100644
index 0000000..acccceb
--- /dev/null
+++ b/app_sdk/app_src/settings.gradle.kts
@@ -0,0 +1,23 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "Serial"
+include(":app")
diff --git a/app_sdk/bin/rk3568test.apk b/app_sdk/bin/rk3568test.apk
new file mode 100644
index 0000000..415bd4a
--- /dev/null
+++ b/app_sdk/bin/rk3568test.apk
Binary files differ
--
Gitblit v1.9.1