first commit

This commit is contained in:
cyy_mac
2026-06-16 08:44:45 +08:00
commit c9d2b90a0b
37 changed files with 2396 additions and 0 deletions

468
ir_app/ir_app.c Normal file
View File

@@ -0,0 +1,468 @@
#include "ir_app.h"
#include "../common/device.h"
#include "../button/button.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#define IR_HEAD1 0xA1
#define IR_HEAD2 0xF1
/* ── Apple-style dark palette ── */
#define CLR_BG RGB(28, 28, 30) /* system background */
#define CLR_SURFACE RGB(44, 44, 46) /* card / surface */
#define CLR_BLUE RGB(0, 122, 255) /* accent blue */
#define CLR_GREEN RGB(52, 199, 89) /* system green */
#define CLR_RED RGB(255, 59, 48) /* system red */
#define CLR_GRAY RGB(142, 142, 147) /* secondary text */
#define CLR_SEP RGB(56, 56, 58) /* separator */
#define CLR_DOT RGB(48, 209, 88) /* learned dot */
#define TOP_H 44 /* nav bar height */
/* ── Static data ── */
static int ir_fd = -1;
static button_t g_btns[5]; /* up/down/left/right/center */
static button_t g_menu[4]; /* Send Recv Learn Back */
static button_t g_mode_btn; /* learn-page SEND↔RECV */
static unsigned char g_send_cmds[5][3];
static unsigned char g_recv_cmds[5][3];
static int g_send_learned[5];
static int g_recv_learned[5];
/* Pre-rendered Apple-style assets */
static unsigned int *g_bmp_page_bg = NULL;
static int g_bmp_page_bg_w, g_bmp_page_bg_h;
static unsigned int *g_bmp_bar = NULL;
static int g_bmp_bar_w, g_bmp_bar_h;
/* ── Serial ── */
static int ir_serial_open(void)
{
int fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0) { perror("ttySAC1"); return -1; }
struct termios opt;
tcgetattr(fd, &opt);
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
opt.c_cflag |= (CLOCAL | CREAD);
opt.c_cflag &= ~PARENB; opt.c_cflag &= ~CSTOPB; opt.c_cflag &= ~CSIZE;
opt.c_cflag |= CS8;
opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
opt.c_iflag &= ~(IXON | IXOFF | IXANY);
opt.c_oflag &= ~OPOST;
opt.c_cc[VMIN] = 0; opt.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, &opt);
return fd;
}
static void ir_save_cmd(int idx, const unsigned char *data, int is_recv)
{
char name[32];
snprintf(name, sizeof(name), is_recv ? "ir_recv_%d.txt" : "ir_send_%d.txt", idx);
FILE *f = fopen(name, "w");
if (f == NULL) return;
fprintf(f, "%02X %02X %02X\n", data[0], data[1], data[2]);
fclose(f);
if (is_recv)
{
g_recv_cmds[idx][0] = data[0]; g_recv_cmds[idx][1] = data[1]; g_recv_cmds[idx][2] = data[2];
g_recv_learned[idx] = 1;
}
else
{
g_send_cmds[idx][0] = data[0]; g_send_cmds[idx][1] = data[1]; g_send_cmds[idx][2] = data[2];
g_send_learned[idx] = 1;
}
}
static int ir_load_cmd(int idx, unsigned char *data, int is_recv)
{
char name[32];
snprintf(name, sizeof(name), is_recv ? "ir_recv_%d.txt" : "ir_send_%d.txt", idx);
FILE *f = fopen(name, "r");
if (f == NULL) return 0;
unsigned int v[3];
int n = fscanf(f, "%02X %02X %02X", &v[0], &v[1], &v[2]);
fclose(f);
if (n == 3) { data[0] = v[0]; data[1] = v[1]; data[2] = v[2]; return 1; }
return 0;
}
static void ir_send(unsigned char d0, unsigned char d1, unsigned char d2)
{
unsigned char pkt[5] = {IR_HEAD1, IR_HEAD2, d0, d1, d2};
write(ir_fd, pkt, 5);
}
static unsigned char ir_buf[3];
static int ir_buf_idx = 0;
static int ir_poll_3bytes(unsigned char *out)
{
if (ir_fd < 0) return 0;
int n = read(ir_fd, ir_buf + ir_buf_idx, 3 - ir_buf_idx);
if (n < 0 && errno != EAGAIN)
printf(" ir: read error %d\n", errno);
else if (n > 0)
{
printf(" ir: got %d byte(s)\n", n);
ir_buf_idx += n;
if (ir_buf_idx == 3)
{
memcpy(out, ir_buf, 3);
ir_buf_idx = 0;
return 1;
}
}
return 0;
}
static int recv_match(const unsigned char *code)
{
int i;
for (i = 0; i < 5; i++)
{
if (!g_recv_learned[i]) continue;
if (code[0] == g_recv_cmds[i][0] &&
code[1] == g_recv_cmds[i][1] &&
code[2] == g_recv_cmds[i][2])
return i;
}
return -1;
}
/* ── Drawing helpers ── */
static void draw_top_bar(const char *title, unsigned int color)
{
if (g_bmp_bar)
show_bmp(g_bmp_bar, g_bmp_bar_w, g_bmp_bar_h, 0, 0);
else
fb_fill(0, 0, 800, TOP_H, color);
printf("=== %s ===\n", title);
}
static void draw_page_bg(void)
{
if (g_bmp_page_bg)
show_bmp(g_bmp_page_bg, g_bmp_page_bg_w, g_bmp_page_bg_h, 0, 0);
else
fb_fill(0, 0, 800, 480, CLR_BG);
}
static void draw_dir_buttons(int *learned)
{
int i;
for (i = 0; i < 5; i++)
button_set_learned(&g_btns[i], learned[i]);
for (i = 0; i < 5; i++)
button_draw(&g_btns[i]);
}
/* ── Pages ── */
static void page_send(void)
{
draw_page_bg();
draw_top_bar("Send", CLR_BLUE);
draw_dir_buttons(g_send_learned);
int sx, sy, t, btn_ok = 0;
while (1)
{
while (ts_read(&sx, &sy, &t) > 0)
{
if (t && sy < TOP_H) return;
if (!t) { btn_ok = 0; continue; }
if (btn_ok) continue;
int i;
for (i = 0; i < 5; i++)
{
if (button_hit(&g_btns[i], sx, sy) && g_send_learned[i])
{
ir_send(g_send_cmds[i][0], g_send_cmds[i][1], g_send_cmds[i][2]);
printf("IR TX: %02X %02X %02X\n",
g_send_cmds[i][0], g_send_cmds[i][1], g_send_cmds[i][2]);
beep_on(); usleep(50000); beep_off();
btn_ok = 1;
}
}
}
}
}
static void page_learn(void)
{
static int is_recv = 0;
draw_top_bar(is_recv ? "Learn · Receive" : "Learn · Send", CLR_GREEN);
draw_page_bg();
button_draw(&g_mode_btn);
int i;
int *learned = is_recv ? g_recv_learned : g_send_learned;
draw_dir_buttons(learned);
int learn_idx = -1, btn_handled = 0;
int sx, sy, t;
ir_buf_idx = 0;
int lfd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NONBLOCK);
{ struct termios o; tcgetattr(lfd, &o);
cfsetispeed(&o, B9600); cfsetospeed(&o, B9600);
o.c_cflag |= (CLOCAL | CREAD);
o.c_cflag &= ~PARENB; o.c_cflag &= ~CSTOPB; o.c_cflag &= ~CSIZE;
o.c_cflag |= CS8;
o.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
o.c_iflag &= ~(IXON | IXOFF | IXANY);
o.c_oflag &= ~OPOST;
o.c_cc[VMIN] = 0; o.c_cc[VTIME] = 0;
tcsetattr(lfd, TCSANOW, &o); }
while (1)
{
while (ts_read(&sx, &sy, &t) > 0)
{
if (t && sy < TOP_H) { close(lfd); return; }
if (!t) { btn_handled = 0; continue; }
if (btn_handled) continue;
if (button_hit(&g_mode_btn, sx, sy))
{
btn_handled = 1;
is_recv = !is_recv;
g_mode_btn.label = is_recv ? "RECV" : "SEND";
draw_top_bar(is_recv ? "Learn · Receive" : "Learn · Send", CLR_GREEN);
button_draw(&g_mode_btn);
learned = is_recv ? g_recv_learned : g_send_learned;
draw_dir_buttons(learned);
printf("Learn mode: %s\n", is_recv ? "RECV" : "SEND");
continue;
}
for (i = 0; i < 5; i++)
{
if (button_hit(&g_btns[i], sx, sy))
{
learn_idx = i;
ir_buf_idx = 0;
btn_handled = 1;
printf("Learn(%s): waiting IR for btn %d...\n",
is_recv ? "RECV" : "SEND", i);
break;
}
}
}
if (learn_idx >= 0)
{
int n = read(lfd, ir_buf + ir_buf_idx, 3 - ir_buf_idx);
if (n > 0)
{
printf(" ir: got %d byte(s)\n", n);
ir_buf_idx += n;
if (ir_buf_idx == 3)
{
ir_save_cmd(learn_idx, ir_buf, is_recv);
printf(" Learned %s btn %d: %02X %02X %02X\n",
is_recv ? "RECV" : "SEND", learn_idx,
ir_buf[0], ir_buf[1], ir_buf[2]);
learned = is_recv ? g_recv_learned : g_send_learned;
button_set_learned(&g_btns[learn_idx], learned[learn_idx]);
button_draw(&g_btns[learn_idx]);
beep_on(); usleep(100000); beep_off();
learn_idx = -1;
ir_buf_idx = 0;
}
}
}
}
}
static void page_recv(void)
{
draw_page_bg();
draw_top_bar("Receive", CLR_RED);
/* Draw 4 rounded indicator boxes on the left */
int i, y0;
for (i = 0; i < 5; i++)
{
y0 = 70 + i * 90;
unsigned int c = g_recv_learned[i] ? CLR_GREEN : CLR_SEP;
fb_fill_rounded(10, y0, 80, 60, 8, c);
/* Subtle border */
fb_fill(10, y0, 80, 1, CLR_SURFACE);
fb_fill(10, y0 + 59, 80, 1, CLR_SURFACE);
}
int sx, sy, t;
while (read(ir_fd, ir_buf, 3) > 0) {}
ir_buf_idx = 0;
while (1)
{
while (ts_read(&sx, &sy, &t) > 0)
{
if (t && sy < TOP_H) return;
}
unsigned char buf[3];
if (ir_poll_3bytes(buf))
{
printf("IR RX: %02X %02X %02X\n", buf[0], buf[1], buf[2]);
beep_on(); usleep(80000); beep_off(); usleep(40000);
beep_on(); usleep(80000); beep_off();
int m = recv_match(buf);
if (m >= 0)
{
printf(" → matched recv[%d]\n", m);
y0 = 70 + m * 90;
fb_fill_rounded(10, y0, 80, 60, 8, RGB(0, 255, 0));
beep_on(); usleep(200000); beep_off();
fb_fill_rounded(10, y0, 80, 60, 8, CLR_GREEN);
switch (m)
{
case 0: led_on(1); break;
case 1: led_on(2); break;
case 2: led_on(0); break;
case 3: led_off(0); break;
}
}
}
}
}
static void show_menu(void)
{
draw_page_bg();
draw_top_bar("Infrared", CLR_BG);
int i;
for (i = 0; i < 4; i++)
button_draw(&g_menu[i]);
printf("[Send] [Recv] [Learn] [Back]\n");
}
/* ── Public ── */
int ir_poll(int *action)
{
unsigned char buf[3];
if (!ir_poll_3bytes(buf)) return 0;
int m = recv_match(buf);
if (m < 0) return 0;
if (action) *action = m;
printf("IR remote: btn[%d] %02X%02X%02X\n", m, buf[0], buf[1], buf[2]);
return 1;
}
void ir_flush(void)
{
unsigned char drain[64];
while (read(ir_fd, drain, sizeof(drain)) > 0) {}
ir_buf_idx = 0;
}
void ir_app_init(void)
{
ir_fd = ir_serial_open();
int i;
for (i = 0; i < 5; i++)
{
unsigned char cmd[3];
if (ir_load_cmd(i, cmd, 0))
{
g_send_cmds[i][0] = cmd[0]; g_send_cmds[i][1] = cmd[1]; g_send_cmds[i][2] = cmd[2];
g_send_learned[i] = 1;
}
else g_send_learned[i] = 0;
if (ir_load_cmd(i, cmd, 1))
{
g_recv_cmds[i][0] = cmd[0]; g_recv_cmds[i][1] = cmd[1]; g_recv_cmds[i][2] = cmd[2];
g_recv_learned[i] = 1;
}
else g_recv_learned[i] = 0;
}
/* Load Apple-style BMP assets */
g_bmp_page_bg = bmp_load("icon/bg_page.bmp", &g_bmp_page_bg_w, &g_bmp_page_bg_h);
g_bmp_bar = bmp_load("icon/bar_top.bmp", &g_bmp_bar_w, &g_bmp_bar_h);
/* Direction buttons — cross layout, rounded */
button_init_rounded(&g_btns[0], 400, 150, 90, 70, CLR_SURFACE, CLR_SEP, 12);
g_btns[0].label = "UP"; g_btns[0].label_color = RGB(255, 255, 255);
button_init_rounded(&g_btns[1], 400, 330, 90, 70, CLR_SURFACE, CLR_SEP, 12);
g_btns[1].label = "DN"; g_btns[1].label_color = RGB(255, 255, 255);
button_init_rounded(&g_btns[2], 300, 240, 90, 70, CLR_SURFACE, CLR_SEP, 12);
g_btns[2].label = "LT"; g_btns[2].label_color = RGB(255, 255, 255);
button_init_rounded(&g_btns[3], 500, 240, 90, 70, CLR_SURFACE, CLR_SEP, 12);
g_btns[3].label = "RT"; g_btns[3].label_color = RGB(255, 255, 255);
/* Center button — circle */
button_init_rounded(&g_btns[4], 400, 240, 60, 60, CLR_SURFACE, CLR_SEP, 30);
g_btns[4].label = "OK"; g_btns[4].label_color = RGB(255, 255, 255);
/* Mode toggle — pill shape at bottom */
button_init_rounded(&g_mode_btn, 400, 430, 160, 38, CLR_SEP, CLR_GRAY, 19);
g_mode_btn.label = "SEND"; g_mode_btn.label_color = RGB(255, 255, 255);
/* Menu buttons — rounded, accent colors, evenly spaced */
button_init_rounded(&g_menu[0], 160, 260, 130, 90, CLR_BLUE, CLR_BLUE, 14);
g_menu[0].label = "SEND"; g_menu[0].label_color = RGB(255, 255, 255);
button_init_rounded(&g_menu[1], 320, 260, 130, 90, CLR_RED, CLR_RED, 14);
g_menu[1].label = "RECV"; g_menu[1].label_color = RGB(255, 255, 255);
button_init_rounded(&g_menu[2], 480, 260, 130, 90, CLR_GREEN, CLR_GREEN, 14);
g_menu[2].label = "LEARN"; g_menu[2].label_color = RGB(255, 255, 255);
button_init_rounded(&g_menu[3], 640, 260, 130, 90, CLR_GRAY, CLR_GRAY, 14);
g_menu[3].label = "BACK"; g_menu[3].label_color = RGB(255, 255, 255);
printf("IR app init OK\n");
}
void ir_app_run(void)
{
int sx, sy, t;
/* Flush any residual touch before showing menu */
{
int _t = 1;
while (_t)
{
while (ts_read(&sx, &sy, &_t) > 0) {}
if (_t) usleep(20000);
}
usleep(50000);
while (ts_read(&sx, &sy, &_t) > 0) {}
}
while (1)
{
show_menu();
while (1)
{
while (ts_read(&sx, &sy, &t) > 0)
{
if (!t) continue;
printf(" touch at (%d,%d)\n", sx, sy);
if (button_hit(&g_menu[0], sx, sy))
{ page_send(); show_menu(); break; }
if (button_hit(&g_menu[1], sx, sy))
{ page_recv(); show_menu(); break; }
if (button_hit(&g_menu[2], sx, sy))
{ page_learn(); show_menu(); break; }
if (button_hit(&g_menu[3], sx, sy))
{ printf("IR exit\n"); return; }
}
}
}
}