Kindle+ESP32开发原生智能家居应用

项目运行于 Kindle 设备,基于多线程架构,电子墨水屏作为人机交互界面,通过 TCP 客户端连接 ESP32 服务端,实现屏幕触屏交互、本地时间日期显示、与 ESP32 硬件通信、传感器数据展示。

  • 程序启动时通过命令行参数传入 ESP32 服务端 IP 地址,端口固定 8899。
  • 图形界面显示采用 FBInk 绘图工具,在 C 程序中使用进程方式进行参数调用。
  • 触摸使用 zforce2 驱动接口时行开发,对屏幕触摸坐标进行读取。

编译依赖:pthread 多线程库,fbink 绘图工具 /usr/bin/fbink

一、 整体软件架构

|-----------|------------|----------------------------------------------------------------------------------|
| 模块 | 所属线程 | 功能职责 |
| 应用初始化 | main 主线程 | 封装在app_init() 函数;打开触屏设备、加载背景图片、初始化绘制全部 UI 界面 |
| 触屏事件处理 | 触屏独立线程 | 读取/dev/input/event0 触摸设备;识别屏幕点击;切换 LED 状态,触发网络下发控制指令;刷新屏幕 LED 状态显示 |
| 时间日期刷新 | 时间独立线程 | 1 秒周期检测系统时间;分钟变更时局部擦除墨水屏区域,更新时间、日期显示 |
| TCP 网络通信 | 网络独立线程 | TCP 连接 ESP32 服务端、断线自动重连;30 秒周期发送 GET 指令获取温湿度;接收触屏触发,下发 LED 控制命令;解析服务端应答更新传感器共享数据 |
| 传感器 UI 刷新 | main 守护主线程 | 循环调用refresh_sensor_display() ;对比新旧温湿度数值,仅数据发生变化时执行墨水屏局部刷新,减少屏幕刷新次数 |

共享变量全部使用互斥锁保护;LED 控制采用条件变量,触屏点击可立即唤醒网络线程发送指令,无需等待 30 秒查询周期。

二、 屏幕交互功能

1、UI 显示内容

  1. 顶部区域:实时时间、系统日期,分钟自动刷新。
  2. 传感器显示区:温度湿度、PM2.5、光照 Lux、烟雾数值。
  3. 状态按钮区:LED 开关状态、继电器开关状态。
复制代码
fbink -g file=/mnt/us/smarthome2.png,halign=CENTER,valign=MIDDLE //显示应用的背景图片

fbink -x 15 -y 8 "15:35"//时间坐标
fbink -x 13 -y 10 "2026/08/04"//日期坐标

fbink -x 3 -y 27 "25c/48%"
fbink -x 16 -y 27 "25ug/m"
fbink -x 28 -y 27 "450Lux"

fbink -x 5 -y 43 "850"
fbink -x 17 -y 43 "Off"
fbink -x 30 -y 43 "On"
  • 局部清屏坐标

    fbink -k top=120,left=0,width=600,height=100
    fbink -k top=405,left=45,width=120,height=50
    fbink -k top=405,left=245,width=120,height=50
    fbink -k top=405,left=445,width=120,height=50

    fbink -k top=655,left=45,width=120,height=50
    fbink -k top=655,left=245,width=120,height=50
    fbink -k top=655,left=445,width=120,height=50

2、 触屏点击交互

  1. LED 按钮区域点击
  • 点击切换 LED 状态:On / Off,屏幕状态立即更新。
  • LED 开启:网络线程发送指令 CMD1,ESP32 执行 WS2812 红色全亮,应答:OK:RED
  • LED 关闭:网络线程发送指令 CMD5,ESP32 执行 WS2812 全部熄灭,应答:OK:OFF
  1. 继电器按钮区域点击
  • 本地 UI 切换继电器 On/Off 状态;当前版本仅本地界面模拟,未实现下发网络指令,可后续扩展。
  • 触摸区域坐标

LED 灯( x 坐标:230--330,y 坐标: 500--700)

继电器( x 坐标:430--530,y 坐标: 500--700)

所有屏幕绘制均使用 fbink 局部擦除,只刷新变化区域,降低电子墨水屏损耗。

三、网络通信功能

1、TCP 通信协议

Kindle 客户端 ↔ ESP32 服务端,端口 8899。

每条指令末尾必须带换行符\n,适配 ESP32 readStringUntil('\n')读取逻辑。

|----------|-------------|------------------------|
| 发送指令 | 功能 | ESP32 返回应答示例 |
| GET | 查询温湿度 | TEMP:24.50,HUM:47.20 |
| CMD1 | WS2812 红色全亮 | OK:RED |
| CMD5 | WS2812 全部熄灭 | OK:OFF |

2、 网络线程工作逻辑

  1. 程序启动自动尝试连接 ESP32;连接失败每 3 秒自动重试。
  2. 每 30s 自动发送GET指令获取温湿度,解析返回数据更新程序内部传感器变量。
  3. 触屏点击 LED,通过条件变量立即触发网络线程发送 CMD1/CMD5 控制指令,不等待 30s 定时周期。
  4. TCP 断开、读写超时,自动关闭 socket,进入断线重连流程。

四、 关键函数说明

  1. app_init():应用初始化函数。打开触屏设备,绘制背景图,完成界面初始渲染。
  2. refresh_sensor_display():传感器 UI 刷新独立函数,main 主线程循环调用;仅温湿度数值变化才刷新屏幕。
  3. touch_thread_entry():触屏线程入口,阻塞读取输入设备,处理点击事件。
  4. time_thread_entry():时间线程入口,负责时间日期更新。
  5. net_thread_entry():网络线程入口,TCP 连接管理、定时查询、下发控制指令、应答解析。
  6. send_command():底层 TCP 发送指令,循环读取直到换行符获取完整应答。
  7. parse_get_response():解析 GET 返回的温湿度字符串,更新全局共享传感器变量。
  • 触摸区域坐标

LED 灯( x 坐标:230--330,y 坐标: 500--700)

继电器( x 坐标:430--530,y 坐标: 500--700)

五、 程序实现

程序采用 C 语言的多线程框架进行开发实现。

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include <pthread.h>

#define TOUCH_DEV       "/dev/input/event0"
#define FBINK_BIN       "/usr/bin/fbink"

#define KV_DATETIME       "top=120,left=0,width=600,height=100"
#define KV_SENSOR_TEMP    "top=405,left=45,width=120,height=50"
#define KV_SENSOR_PM25    "top=405,left=245,width=120,height=50"
#define KV_SENSOR_LUX     "top=405,left=445,width=120,height=50"
#define KV_SENSOR_SMOKE   "top=655,left=45,width=120,height=50"
#define KV_SENSOR_LED     "top=655,left=245,width=120,height=50"
#define KV_SENSOR_RELAY   "top=655,left=445,width=120,height=50"

#define LED_X_MIN 230
#define LED_X_MAX 330
#define LED_Y_MIN 500
#define LED_Y_MAX 700

#define RELAY_X_MIN 430
#define RELAY_X_MAX 530
#define RELAY_Y_MIN 500
#define RELAY_Y_MAX 700

/* TCP config */
#define SERVER_PORT  8899
#define BUF_SIZE     256
#define RECV_TIMEOUT_SEC 3
#define QUERY_INTERVAL_SEC 30

/* 全局共享状态 */
static pthread_mutex_t g_state_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  g_net_cond   = PTHREAD_COND_INITIALIZER;

static int g_led_on      = 0;
static int g_relay_on    = 0;
static int last_minute   = -1;
static int last_day      = -1;

/* 传感器数据,网络线程更新,主线程读取刷新UI */
static float g_temp    = 25.0f;
static float g_humi    = 48.0f;
static int g_pm25    = 25;
static int g_lux     = 450;
static int g_smoke   = 850;
static float g_last_temp = 0.0f;
static float g_last_humi = 0.0f;

/* 网络线程触发命令标记 */
#define NET_CMD_NONE    0
#define NET_CMD_LED_ON  1
#define NET_CMD_LED_OFF 2
static int g_net_cmd_flag = NET_CMD_NONE;

static int g_touch_fd = -1;
static int sock_fd = -1;
static char g_esp32_ip[64] = {0};

static int fbink_exec(char *const argv[])
{
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        return -1;
    }
    if (pid == 0) {
        execvp(argv[0], argv);
        perror("execvp fbink");
        _exit(127);
    }
    int status;
    waitpid(pid, &status, 0);
    if (WIFEXITED(status)) {
        return WEXITSTATUS(status);
    }
    return -1;
}

static int fbink_local_erase(const char *kv_base)
{
    char argbuf[256];
    snprintf(argbuf, sizeof(argbuf), "%s", kv_base);
    char * const argv[] = {
        FBINK_BIN,
        "-k", argbuf,
        NULL
        };
    return fbink_exec(argv);
}

static void draw_background(void)
{
    char * const argv[] = {
        FBINK_BIN,
        "-g", "file=/mnt/us/smarthome2.png,halign=CENTER,valign=MIDDLE",
        NULL
        };
    fbink_exec(argv);
}

static void draw_time(int hour, int minute)
{
    char buf[64];
    snprintf(buf, sizeof(buf), "%02d:%02d", hour, minute);
    char x_str[] = "15";
    char y_str[] = "8";
    char * const argv[] = {
        FBINK_BIN,
        "-x", x_str,
        "-y", y_str,
        buf,
        NULL
    };
    fbink_exec(argv);
}

static void draw_date(int y, int m, int d)
{
    char buf[64];
    snprintf(buf, sizeof(buf), "%04d/%02d/%02d", y, m, d);
    char x_str[] = "13";
    char y_str[] = "10";
    char * const argv[] = {
        FBINK_BIN,
        "-x", x_str,
        "-y", y_str,
        buf,
        NULL
    };
    fbink_exec(argv);
}

static void draw_temp_humi(void)
{
    char buf[128];
    pthread_mutex_lock(&g_state_mutex);
    float t = g_temp;
    float h = g_humi;
    pthread_mutex_unlock(&g_state_mutex);
	
	int it = (int)roundf(t);
	int ih = (int)roundf(h);
    snprintf(buf, sizeof(buf), "%dc/%d%%", it, ih);
    char x[]="3",y[]="27";
    char *argv[]={FBINK_BIN,"-x",x,"-y",y,buf,NULL};
    fbink_local_erase(KV_SENSOR_TEMP);
    fbink_exec(argv);
}

static void draw_pm25(void)
{
    char buf[128];
    snprintf(buf, sizeof(buf), "%dug/m", g_pm25);
    char x[]="16",y[]="27";
    char *argv[]={FBINK_BIN,"-x",x,"-y",y,buf,NULL};
    fbink_local_erase(KV_SENSOR_PM25);
    fbink_exec(argv);
}

static void draw_lux(void)
{
    char buf[128];
    snprintf(buf, sizeof(buf), "%dLux", g_lux);
    char x[]="28",y[]="27";
    char *argv[]={FBINK_BIN,"-x",x,"-y",y,buf,NULL};
    fbink_local_erase(KV_SENSOR_LUX);
    fbink_exec(argv);
}

static void draw_smoke(void)
{
    char buf[128];
    snprintf(buf, sizeof(buf), "%d", g_smoke);
    char x[]="5",y[]="43";
    char *argv[]={FBINK_BIN,"-x",x,"-y",y,buf,NULL};
    fbink_local_erase(KV_SENSOR_SMOKE);
    fbink_exec(argv);
}

static void draw_led_status(void)
{
    pthread_mutex_lock(&g_state_mutex);
    int led = g_led_on;
    pthread_mutex_unlock(&g_state_mutex);

    char led_str[8];
    strcpy(led_str, led ? "On" : "Off");
    char x[]="17",y[]="43";
    char *argv[]={FBINK_BIN,"-x",x,"-y",y,led_str,NULL};
    fbink_local_erase(KV_SENSOR_LED);
    fbink_exec(argv);
}

static void draw_relay_status(void)
{
    pthread_mutex_lock(&g_state_mutex);
    int relay = g_relay_on;
    pthread_mutex_unlock(&g_state_mutex);

    char rel_str[8];
    strcpy(rel_str, relay ? "On" : "Off");
    char x[]="30",y[]="43";
    char *argv[]={FBINK_BIN,"-x",x,"-y",y,rel_str,NULL};
    fbink_local_erase(KV_SENSOR_RELAY);
    fbink_exec(argv);
}

static void draw_all_sensor(void)
{
    draw_temp_humi();
    draw_pm25();
    draw_lux();
    draw_smoke();
    draw_led_status();
    draw_relay_status();
}

/* 独立传感器刷新函数,main守护线程调用;仅温湿度变化才刷新屏幕 */
static void refresh_sensor_display(void)
{
    pthread_mutex_lock(&g_state_mutex);
    float curr_t = g_temp;
    float curr_h = g_humi;
    float last_t = g_last_temp;
    float last_h = g_last_humi;
    pthread_mutex_unlock(&g_state_mutex);

    if(curr_t != last_t || curr_h != last_h)
    {
        draw_temp_humi();
        pthread_mutex_lock(&g_state_mutex);
        g_last_temp = curr_t;
        g_last_humi = curr_h;
        pthread_mutex_unlock(&g_state_mutex);
    }
}

static void handle_tap(int x, int y)
{
    printf("TAP event x=%d y=%d\n", x, y);

    if (x >= LED_X_MIN && x <= LED_X_MAX && y >= LED_Y_MIN && y <= LED_Y_MAX)
    {
        pthread_mutex_lock(&g_state_mutex);
        g_led_on = !g_led_on;
        if(g_led_on)
        {
            g_net_cmd_flag = NET_CMD_LED_ON;
        }
        else
        {
            g_net_cmd_flag = NET_CMD_LED_OFF;
        }
        pthread_cond_signal(&g_net_cond);
        int newval = g_led_on;
        pthread_mutex_unlock(&g_state_mutex);

        printf("TOGGLE_LED, new state=%s\n", newval?"On":"Off");
        draw_led_status();
        return;
    }

    if (x >= RELAY_X_MIN && x <= RELAY_X_MAX && y >= RELAY_Y_MIN && y <= RELAY_Y_MAX)
    {
        pthread_mutex_lock(&g_state_mutex);
        g_relay_on = !g_relay_on;
        int newval = g_relay_on;
        pthread_mutex_unlock(&g_state_mutex);

        printf("TOGGLE_RELAY, new state=%s\n", newval?"On":"Off");
        draw_relay_status();
        return;
    }
}

static void *touch_thread_entry(void *arg)
{
    (void)arg;
    struct input_event ev;
    int touch_x = -1, touch_y = -1;
    int tap_pending = 0;

    for(;;)
    {
        ssize_t n = read(g_touch_fd, &ev, sizeof(struct input_event));
        if(n != sizeof(struct input_event))
        {
            usleep(100*1000);
            continue;
        }

        switch(ev.type)
        {
        case EV_ABS:
            if(ev.code == ABS_MT_POSITION_X){
                touch_x = ev.value;
            }else if(ev.code == ABS_MT_POSITION_Y){
                touch_y = ev.value;
            }else if(ev.code == ABS_MT_TRACKING_ID){
                if(ev.value != -1){
                    tap_pending = 1;
                }else{
                    if(tap_pending && touch_x != -1 && touch_y != -1){
                        handle_tap(touch_x, touch_y);
                    }
                    tap_pending = 0;
                }
            }
            break;
        case EV_SYN:
            break;
        default:
            break;
        }
    }
    return NULL;
}

static void *time_thread_entry(void *arg)
{
    (void)arg;
    for(;;)
    {
        sleep(1);
        time_t now = time(NULL);
        struct tm *tm_now = localtime(&now);
        int curr_min  = tm_now->tm_min;
        int curr_day  = tm_now->tm_mday;

        if (curr_min != last_minute)
        {
            fbink_local_erase(KV_DATETIME);
            draw_time(tm_now->tm_hour, curr_min);
            if(curr_day != last_day){
                draw_date(tm_now->tm_year+1900, tm_now->tm_mon+1, tm_now->tm_mday);
                last_day = curr_day;
            }
			draw_date(tm_now->tm_year+1900, tm_now->tm_mon+1, tm_now->tm_mday);
            last_minute = curr_min;
        }
    }
    return NULL;
}

/* TCP 网络辅助函数 */
static void set_sock_timeout(int fd, int sec)
{
    struct timeval timeout;
    timeout.tv_sec = sec;
    timeout.tv_usec = 0;
    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
}

static int connect_server(void)
{
    struct sockaddr_in serv_addr;

    if (sock_fd > 0)
    {
        printf("已经连接,无需重连\n");
        return 0;
    }

    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0)
    {
        perror("socket create failed");
        return -1;
    }
    set_sock_timeout(sock_fd, RECV_TIMEOUT_SEC);

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERVER_PORT);

    if (inet_pton(AF_INET, g_esp32_ip, &serv_addr.sin_addr) <= 0)
    {
        printf("IP地址格式错误:%s\n",g_esp32_ip);
        close(sock_fd);
        sock_fd = -1;
        return -1;
    }

    if (connect(sock_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        perror("connect failed");
        close(sock_fd);
        sock_fd = -1;
        return -1;
    }

    printf("✅ 成功连接 ESP32 TCP Server %s:%d\n", g_esp32_ip, SERVER_PORT);
    return 0;
}

static int send_command(const char *cmd, char *resp, int resp_len)
{
    char send_buf[BUF_SIZE];
    char tmp[BUF_SIZE];
    int total = 0;
    int i = 0;
    ssize_t ret;

    memset(resp, 0, resp_len);

    if (sock_fd < 0)
    {
        printf("❌ 未连接服务器,请先连接!\n");
        return -1;
    }

    snprintf(send_buf, sizeof(send_buf), "%s\n", cmd);

    if (write(sock_fd, send_buf, strlen(send_buf)) < 0)
    {
        perror("send fail");
        close(sock_fd);
        sock_fd = -1;
        return -1;
    }

    while (1)
    {
        ret = read(sock_fd, tmp, sizeof(tmp)-1);
        if (ret <= 0)
        {
            printf("❌ 读取应答失败 / 超时 / 服务器断开\n");
            close(sock_fd);
            sock_fd = -1;
            return -1;
        }

        for (; i < ret; i++)
        {
            if (total < resp_len - 1)
            {
                resp[total++] = tmp[i];
            }
            if (tmp[i] == '\n')
            {
                resp[total] = '\0';
                return 0;
            }
        }
    }
}

/* 解析GET应答 TEMP:XX.XX,HUM:XX.XX */
static void parse_get_response(const char *rsp)
{
    float t,h;
    if(sscanf(rsp,"TEMP:%f,HUM:%f",&t,&h)==2)
    {
        pthread_mutex_lock(&g_state_mutex);
        g_temp = t;
        g_humi = h;
        pthread_mutex_unlock(&g_state_mutex);
        printf("Parsed temp=%.2f hum=%.2f\n",t,h);
    }
}

/* 网络线程入口:断线重连、30s GET查询、处理LED命令标记 */
static void *net_thread_entry(void *arg)
{
    (void)arg;
    char response[BUF_SIZE];
    struct timespec ts;

    for(;;)
    {
        /* 没有连接就尝试重连 */
        if(sock_fd < 0)
        {
            connect_server();
            sleep(3);
            continue;
        }

        pthread_mutex_lock(&g_state_mutex);
        /* 等待条件变量,最多等待30秒,到期执行GET查询 */
        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_sec += QUERY_INTERVAL_SEC;
        int wait_ret = pthread_cond_timedwait(&g_net_cond, &g_state_mutex, &ts);

        int local_cmd = g_net_cmd_flag;
        g_net_cmd_flag = NET_CMD_NONE;
        pthread_mutex_unlock(&g_state_mutex);

        /* 如果有LED命令标记,发送对应指令 */
        if(local_cmd != NET_CMD_NONE)
        {
            if(local_cmd == NET_CMD_LED_ON)
            {
                if(0 == send_command("CMD1", response, sizeof(response)))
                {
                    printf("ESP32 rsp: %s",response);
                }
            }
            else if(local_cmd == NET_CMD_LED_OFF)
            {
                if(0 == send_command("CMD5", response, sizeof(response)))
                {
                    printf("ESP32 rsp: %s",response);
                }
            }
        }

        /* 超时:执行GET查询温湿度 */
        if(wait_ret == ETIMEDOUT)
        {
            if(0 == send_command("GET", response, sizeof(response)))
            {
                printf("GET rsp: %s",response);
                parse_get_response(response);
            }
        }
    }
    return NULL;
}

static int app_init(void)
{
    g_touch_fd = open(TOUCH_DEV, O_RDONLY);
    if (g_touch_fd < 0) {
        perror("open touch dev root required");
        return -1;
    }

    draw_background();
    usleep(600*1000);

    {
        time_t now = time(NULL);
        struct tm *tm_now = localtime(&now);
        fbink_local_erase(KV_DATETIME);
        draw_time(tm_now->tm_hour, tm_now->tm_min);
        draw_date(tm_now->tm_year+1900, tm_now->tm_mon+1, tm_now->tm_mday);
        last_minute = tm_now->tm_min;
        last_day = tm_now->tm_mday;

        draw_all_sensor();
    }
    printf("SmartHome eInk UI init ok.\n");
    return 0;
}

int main(int argc, char *argv[])
{
    pthread_t th_touch, th_time, th_net;
    int ret;

    if(argc != 2)
    {
        fprintf(stderr,"Usage: %s <ESP32-IP>\nExample: %s 192.168.4.10\n",argv[0],argv[0]);
        return EXIT_FAILURE;
    }
    strncpy(g_esp32_ip, argv[1], sizeof(g_esp32_ip)-1);

    ret = app_init();
    if(ret != 0)
    {
        fprintf(stderr,"app init failed\n");
        return EXIT_FAILURE;
    }

    ret = pthread_create(&th_touch, NULL, touch_thread_entry, NULL);
    if(ret != 0){ fprintf(stderr,"touch thread fail:%s\n",strerror(ret)); return 1; }
    pthread_detach(th_touch);

    ret = pthread_create(&th_time, NULL, time_thread_entry, NULL);
    if(ret != 0){ fprintf(stderr,"time thread fail:%s\n",strerror(ret)); return 1; }
    pthread_detach(th_time);

    ret = pthread_create(&th_net, NULL, net_thread_entry, NULL);
    if(ret != 0){ fprintf(stderr,"net thread fail:%s\n",strerror(ret)); return 1; }
    pthread_detach(th_net);

    /* 主线程守护,循环调用传感器刷新函数 */
    for(;;)
    {
        refresh_sensor_display();
        sleep(1);
    }

    close(g_touch_fd);
    if(sock_fd>0) close(sock_fd);
    pthread_mutex_destroy(&g_state_mutex);
    pthread_cond_destroy(&g_net_cond);
    return 0;
}

程序编译:

复制代码
tony@tony-PC:~/work/kindle-dev$ arm-linux-gnueabi-gcc -march=armv7-a -mtune=cortex-a9 -msoft-float -mabi=aapcs-linux   -lpthread -lm smart-tcp.c -o smart-tcp
tony@tony-PC:~/work/kindle-dev$ scp ./smart-tcp root@192.168.1.4:/mnt/us/gcc-test
[root@kindle gcc-test]# ./smart-tcp 192.168.1.11

六、系统 UI 管理脚本

自定义的 Kindle 的图形界面运行后,Kindle 系统原生的 UI 应用还在后台运行,会定期刷新屏幕,例如左上角的时间。需要通过下述脚本停止系统 UI 运行。

停止系统 UI 脚本:

复制代码
#!/bin/bash
FBINK_CMD="/usr/bin/fbink"

# 暂停指定进程
stop_kindle_ui() {
    local pname="$1"
    pids=$(pidof "$pname")
    if [ -n "$pids" ];then
        echo "SIGSTOP $pname pid:$pids"
        kill -SIGSTOP $pids
    fi
}

# ============ 停止原生UI ============
stop_kindle_ui KPPMainAppV2
stop_kindle_ui awesome
stop_kindle_ui cvm
stop_kindle_ui mesquite

# 清理临时IO进程
killall gzip tar 2>/dev/null

# 清屏,消除原生界面残影
$FBINK_CMD -k -f -q
sleep 1

echo "原生UI已暂停,启动自定义应用"
# 启动你的程序
# /mnt/us/smarthome 192.168.4.10

恢复系统 UI 脚本:

恢复系统 UI 运行后,有可能屏幕还是清屏状态,主 UI 没有更新,可在屏幕上点击几个触发更新。

复制代码
#!/bin/bash
FBINK_CMD="/usr/bin/fbink"

# 恢复原生UI
resume_kindle_ui() {
    local pname="$1"
    pids=$(pidof "$pname")
    if [ -n "$pids" ];then
        echo "SIGCONT $pname pid:$pids"
        kill -SIGCONT $pids
    fi
}

# 清屏,消除原生界面残影
$FBINK_CMD -k -f -q
sleep 1

# ============ 需要恢复Kindle原生界面时调用 ============
resume_kindle_ui cvm
resume_kindle_ui awesome
resume_kindle_ui KPPMainAppV2
resume_kindle_ui mesquite
相关推荐
延凡科技7 小时前
工业锅炉智慧云管控平台解决方案|物联网远程监测、智能预警与能耗优化
物联网·远程控制·工业物联网·设备运维·智慧锅炉
fthux9 小时前
装闭 RenoPit 源码解析(04):装修图纸和合同文件上传处理流程
人工智能·ai·开源·github·open source·renopit
带娃的IT创业者11 小时前
Kimi-K3 开源背后:2.8 万亿参数的“暴力美学”与智能体的新拐点
人工智能·开源·大模型·智能体·开源模型·kimi-k3·moonshot ai
数字新视界11 小时前
IT资产管理系统推动智能化运维与资源更优化管理
物联网·机房管理·蓄电池·动力与环境监控系统·机房动力环境监控
圆奋奋12 小时前
FreeRTOS学习(三)- 任务调度模块
学习·开源·freertos
(╹◡╹)12 小时前
03.智能家居端侧 AI 架构
智能家居
LCG元13 小时前
STM32 硬件 SPI + DMA 驱动 OLED 显示屏(SSD1306):从原理到 112fps 高速刷新全流程
stm32·单片机·嵌入式硬件
9527出列13 小时前
Tursm中WebSocket 连接生命周期:建立、保活、断开与重连
开源
冬奇Lab13 小时前
开源项目第182期:Graphify — 把整个代码库变成可查询知识图谱,让 AI 编程助手真正「懂」你的项目
人工智能·开源·资讯
天吾cc13 小时前
RTT-ADC
单片机·嵌入式硬件·学习