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