android
2024-07-25 2dcf9cc893a27c50d538baf8b5d47ff06fe495d8
Add Android apk SDK source code

Signed-off-by: android <android@lingyun.com>
86 files added
7038 ■■■■■ changed files
app_sdk/app_src/.gitignore 15 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/.idea/.gitignore 3 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/.idea/compiler.xml 6 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/.idea/deploymentTargetSelector.xml 18 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/.idea/gradle.xml 19 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/.idea/migrations.xml 10 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/.idea/misc.xml 9 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/.idea/vcs.xml 6 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/.gitignore 1 ●●●● patch | view | raw | blame | history
app_sdk/app_src/app/build.gradle.kts 63 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/proguard-rules.pro 21 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java 26 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/AndroidManifest.xml 39 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/cpp/CMakeLists.txt 32 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/cpp/adc_control.cpp 522 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp 111 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/cpp/can_control.cpp 193 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/cpp/include/gpiod.h 1775 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/cpp/led_control.cpp 160 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/cpp/native-lib.cpp 51 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/cpp/rs485_control.cpp 516 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java 247 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java 12 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java 104 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java 9 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java 169 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java 9 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java 111 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java 12 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java 70 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java 69 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java 347 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java 18 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/background.xml 8 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/bg1.jpg patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/bg2.jpg patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml 17 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/bg_username.xml 12 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml 14 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/bt_left.xml 12 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/bt_right.xml 12 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/button_selector.xml 7 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml 10 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml 9 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml 9 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml 170 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml 30 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml 10 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml 13 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml 13 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/layout/activity_adc.xml 322 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml 109 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/layout/activity_can.xml 271 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/layout/activity_led.xml 95 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/layout/activity_main.xml 87 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/layout/activity_menu.xml 70 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml 423 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml 6 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml 6 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/values-night/themes.xml 16 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/values/colors.xml 14 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/values/strings.xml 78 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/values/themes.xml 23 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/xml/backup_rules.xml 13 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml 19 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java 17 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/build.gradle.kts 4 ●●●● patch | view | raw | blame | history
app_sdk/app_src/gradle.properties 21 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/gradle/libs.versions.toml 22 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar patch | view | raw | blame | history
app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties 6 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/gradlew 185 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/gradlew.bat 89 ●●●●● patch | view | raw | blame | history
app_sdk/app_src/settings.gradle.kts 23 ●●●●● patch | view | raw | blame | history
app_sdk/bin/rk3568test.apk patch | view | raw | blame | history
app_sdk/app_src/.gitignore
New file
@@ -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
app_sdk/app_src/.idea/.gitignore
New file
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
app_sdk/app_src/.idea/compiler.xml
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="CompilerConfiguration">
    <bytecodeTargetLevel target="17" />
  </component>
</project>
app_sdk/app_src/.idea/deploymentTargetSelector.xml
New file
@@ -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>
app_sdk/app_src/.idea/gradle.xml
New file
@@ -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>
app_sdk/app_src/.idea/migrations.xml
New file
@@ -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>
app_sdk/app_src/.idea/misc.xml
New file
@@ -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>
app_sdk/app_src/.idea/vcs.xml
New file
@@ -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>
app_sdk/app_src/app/.gitignore
New file
@@ -0,0 +1 @@
/build
app_sdk/app_src/app/build.gradle.kts
New file
@@ -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)
}
app_sdk/app_src/app/proguard-rules.pro
New file
@@ -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
app_sdk/app_src/app/src/androidTest/java/com/example/serial/ExampleInstrumentedTest.java
New file
@@ -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());
    }
}
app_sdk/app_src/app/src/main/AndroidManifest.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/cpp/CMakeLists.txt
New file
@@ -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)
app_sdk/app_src/app/src/main/cpp/adc_control.cpp
New file
@@ -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);
}
app_sdk/app_src/app/src/main/cpp/buzzer_control.cpp
New file
@@ -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;
}
app_sdk/app_src/app/src/main/cpp/can_control.cpp
New file
@@ -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;
}
app_sdk/app_src/app/src/main/cpp/include/gpiod.h
New file
@@ -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__ */
app_sdk/app_src/app/src/main/cpp/led_control.cpp
New file
@@ -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;
}
app_sdk/app_src/app/src/main/cpp/native-lib.cpp
New file
@@ -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;
}
app_sdk/app_src/app/src/main/cpp/rs485_control.cpp
New file
@@ -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;
}
app_sdk/app_src/app/src/main/java/com/example/serial/AdcActivity.java
New file
@@ -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(" ");
            }
        }
    }
}
app_sdk/app_src/app/src/main/java/com/example/serial/AdcControl.java
New file
@@ -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);
}
app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerActivity.java
New file
@@ -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();
                }
            }
        });
    }
}
app_sdk/app_src/app/src/main/java/com/example/serial/BuzzerControl.java
New file
@@ -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表示输入的值无效
}
app_sdk/app_src/app/src/main/java/com/example/serial/CanActivity.java
New file
@@ -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();
    }
}
app_sdk/app_src/app/src/main/java/com/example/serial/CanControl.java
New file
@@ -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);
}
app_sdk/app_src/app/src/main/java/com/example/serial/LedActivity.java
New file
@@ -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);
    }
}
app_sdk/app_src/app/src/main/java/com/example/serial/LedControl.java
New file
@@ -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");
    }
}
app_sdk/app_src/app/src/main/java/com/example/serial/MainActivity.java
New file
@@ -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);
        }
    }
}
app_sdk/app_src/app/src/main/java/com/example/serial/MenuActivity.java
New file
@@ -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);
        }
    }
}
app_sdk/app_src/app/src/main/java/com/example/serial/RS485Activity.java
New file
@@ -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);
    }
}
app_sdk/app_src/app/src/main/java/com/example/serial/RS485Control.java
New file
@@ -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);
}
app_sdk/app_src/app/src/main/jni/arm64-v8a/libgpiod.so
Binary files differ
app_sdk/app_src/app/src/main/res/drawable/background.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/bg1.jpg
app_sdk/app_src/app/src/main/res/drawable/bg2.jpg
app_sdk/app_src/app/src/main/res/drawable/bg_frame.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/bg_username.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/bt_bg.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/bt_left.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/bt_right.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/button_selector.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/cb_bg.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/ed_bacground.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/gradient_bg.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/ic_launcher_background.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/ic_launcher_foreground.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/rb_bg.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/shape_normal.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/drawable/shape_pressed.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/layout/activity_adc.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/layout/activity_buzzer.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/layout/activity_can.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/layout/activity_led.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/layout/activity_main.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/layout/activity_menu.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/layout/activity_rs485.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
app_sdk/app_src/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
app_sdk/app_src/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
app_sdk/app_src/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
app_sdk/app_src/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
app_sdk/app_src/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
app_sdk/app_src/app/src/main/res/values-night/themes.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/values/colors.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/values/strings.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/values/themes.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/xml/backup_rules.xml
New file
@@ -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>
app_sdk/app_src/app/src/main/res/xml/data_extraction_rules.xml
New file
@@ -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>
app_sdk/app_src/app/src/test/java/com/example/serial/ExampleUnitTest.java
New file
@@ -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);
    }
}
app_sdk/app_src/build.gradle.kts
New file
@@ -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
}
app_sdk/app_src/gradle.properties
New file
@@ -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
app_sdk/app_src/gradle/libs.versions.toml
New file
@@ -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" }
app_sdk/app_src/gradle/wrapper/gradle-wrapper.jar
Binary files differ
app_sdk/app_src/gradle/wrapper/gradle-wrapper.properties
New file
@@ -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
app_sdk/app_src/gradlew
New file
@@ -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" "$@"
app_sdk/app_src/gradlew.bat
New file
@@ -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
app_sdk/app_src/settings.gradle.kts
New file
@@ -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")
app_sdk/bin/rk3568test.apk
Binary files differ