#include #include #include #include #include #include #include #include #include #include #include #include #include #include const int digit_bitmaps[10][5][3] = { {{1,1,1},{1,0,1},{1,0,1},{1,0,1},{1,1,1}}, // 0 {{0,1,0},{1,1,0},{0,1,0},{0,1,0},{1,1,1}}, // 1 {{1,1,1},{0,0,1},{1,1,1},{1,0,0},{1,1,1}}, // 2 {{1,1,1},{0,0,1},{1,1,1},{0,0,1},{1,1,1}}, // 3 {{1,0,1},{1,0,1},{1,1,1},{0,0,1},{0,0,1}}, // 4 {{1,1,1},{1,0,0},{1,1,1},{0,0,1},{1,1,1}}, // 5 {{1,1,1},{1,0,0},{1,1,1},{1,0,1},{1,1,1}}, // 6 {{1,1,1},{0,0,1},{0,1,0},{0,1,0},{0,1,0}}, // 7 {{1,1,1},{1,0,1},{1,1,1},{1,0,1},{1,1,1}}, // 8 {{1,1,1},{1,0,1},{1,1,1},{0,0,1},{1,1,1}} // 9 }; typedef struct{ int data; int ldata; int hdata; int digits; bool high; int height; int width; int starty; int startx; WINDOW *lwin; char title[30]; } StInt; typedef struct{ float data; float ldata; float hdata; int digits; bool high; int height; int width; int starty; int startx; WINDOW *lwin; char title[30]; } StFlt; typedef struct{ int height; int width; int starty; int startx; WINDOW *lwin; char title[30]; } StStr; typedef struct { char linebuf[128]; int linepos; } SerialParser; typedef struct { StInt speed; StInt rpm; StInt power; StFlt eff; StInt bat; StFlt tens; StFlt amp; StStr message; } Tel; void win_init_int(StInt *st) { st->lwin = newwin(st->height, st->width, st->starty, st->startx); box(st->lwin, 0, 0); mvwprintw(st->lwin, 0, 2, "%s", st->title); wrefresh(st->lwin); }; void win_init_flt(StFlt *st) { st->lwin = newwin(st->height, st->width, st->starty, st->startx); box(st->lwin, 0, 0); mvwprintw(st->lwin, 0, 2, "%s", st->title); wrefresh(st->lwin); }; void win_init_str(StStr *st) { st->lwin = newwin(st->height, st->width, st->starty, st->startx); box(st->lwin, 0, 0); mvwprintw(st->lwin, 0, 2, "%s", st->title); wrefresh(st->lwin); }; void get_fake_data(Tel *t) { t->speed.data = t->speed.ldata + rand() % (t->speed.hdata - t->speed.ldata + 1); t->power.data = t->power.ldata + rand() % (t->power.hdata - t->power.ldata + 1); t->bat.data = t->bat.ldata + rand() % (t->bat.hdata - t->bat.ldata + 1); t->rpm.data = t->rpm.ldata + rand() % (t->rpm.hdata - t->rpm.ldata + 1); t->tens.data = t->tens.ldata + ((float)rand() / (float)RAND_MAX) * (float)(t->tens.hdata - t->tens.ldata); t->amp.data = t->amp.ldata + ((float)rand() / (float)RAND_MAX) * (float)(t->amp.hdata - t->amp.ldata); t->eff.data = t->eff.ldata + ((float)rand() / (float)RAND_MAX) * (float)(t->eff.hdata - t->eff.ldata); } void read_can(Tel *t, int soc) { struct can_frame frame; int nbytes = read(soc, &frame, sizeof(struct can_frame)); if (nbytes > 0) { switch (frame.can_id) { case 0x382: if (frame.can_dlc >= 2) { t->rpm.data = frame.data[0] | (frame.data[1] << 8); } break; default: break; } } } void read_uart(Tel *t, int fd, SerialParser *uart_str) { char buf[128]; int n = read(fd, buf, sizeof(buf)); if (n > 0) { for (int i = 0; i < n; i++) { char c = buf[i]; if (c == '\n') { uart_str->linebuf[uart_str->linepos] = '\0'; float tmp_v, tmp_a; if (sscanf(uart_str->linebuf, "%f,%f", &tmp_v, &tmp_a) == 2) { t->tens.data = tmp_v; t->amp.data = tmp_a; } uart_str->linepos = 0; } if (uart_str->linepos < (int)sizeof(uart_str->linebuf) - 1) { uart_str->linebuf[uart_str->linepos++] = c; } else { uart_str->linepos = 0; // drop line to prevent overflow } } } } void calc_data(Tel *t) { t->power.data = (int)(t->tens.data * t->amp.data); t->speed.data = (int)(t->rpm.data * 0.017f); if (t->speed.data > 0) { t->eff.data = (float)t->power.data / (float)t->speed.data; } else { t->eff.data = 0.0f; } } long now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; } void win_clear(WINDOW *lwin) { int y, x; getmaxyx(lwin, y, x); for (int i = 1; i < y - 1; i++) { mvwhline(lwin, i, 1, ' ', x - 2); } } int smaller_of(int a, int b) { return (a < b) ? a : b; } int color_high(float data, float ldata, float hdata) { int percent = (int)((100.0f * (data - ldata)) / (hdata - ldata) + 0.5f); if (percent >= 90) return 1; else if (percent >= 75) return 2; else return 3; } int color_low(float data, float ldata, float hdata) { int percent = 100 - (int)((100.0f * (data - ldata)) / (hdata - ldata) + 0.5f); if (percent >= 90) return 1; else if (percent >= 75) return 2; else return 3; } void bar_mark(WINDOW *lwin) { int lwiny, lwinx; int first_mark, last_mark; getmaxyx(lwin, lwiny, lwinx); first_mark = (75 * (lwinx - 4)) / 100; last_mark = (90 * (lwinx - 4)) / 100; mvwaddch(lwin, 0, first_mark + 1, '|'); mvwaddch(lwin, lwiny - 1, first_mark + 1, '|'); mvwaddch(lwin, 0, last_mark + 1, '|'); mvwaddch(lwin, lwiny - 1, last_mark + 1, '|'); } void win_int(StInt *st, bool use_color) { int color; if (st->high) { color = color_high(st->data, st->ldata, st->hdata); } else { color = color_low(st->data, st->ldata, st->hdata); }; int lwiny, lwinx, len; char buf[16]; getmaxyx(st->lwin, lwiny, lwinx); snprintf(buf , sizeof(buf), "%d", st->data); len = strlen(buf); int bh = 5; // bitmap height int bw = 3; // bitmap width int size = smaller_of((lwiny - 2) / bh, (lwinx - 2 - (st->digits - 1)) / (bw * st->digits)); int total_width = st->digits * (bw * size) + (st->digits - 1); int startx = (lwinx - total_width) / 2; int starty = (lwiny - bh * size) / 2; int offset = (bw * size + 1) * (st->digits - len); win_clear(st->lwin); if (use_color) wattron(st->lwin, COLOR_PAIR(color)); for (int d = 0; d < len; d++) { int digit = buf[d] - '0'; int dx = startx + d * (bw * size + 1) + offset; for (int y = 0; y < bh; y++) { for (int x = 0; x < bw; x++) { if (digit_bitmaps[digit][y][x]) { for (int yy = 0; yy < size; yy++) for (int xx = 0; xx < size; xx++) mvwaddch(st->lwin, starty + y * size + yy, dx + x * size + xx, ACS_CKBOARD); } } } } if (use_color) wattroff(st->lwin, COLOR_PAIR(color)); wrefresh(st->lwin); } void win_float(StFlt *st, bool use_color) { int color; if (st->high) { color = color_high(st->data, st->ldata, st->hdata); } else { color = color_low(st->data, st->ldata, st->hdata); }; int lwiny, lwinx, len; char buf[16]; getmaxyx(st->lwin, lwiny, lwinx); snprintf(buf , sizeof(buf), "%.1f", st->data); len = strlen(buf); int bh = 5; // bitmap height int bw = 3; // bitmap width int size = smaller_of((lwiny - 2) / bh, (lwinx - 2 - (st->digits - 1)) / (bw * st->digits)); int total_width = st->digits * (bw * size) + (st->digits - 1); int startx = (lwinx - total_width) / 2; int starty = (lwiny - bh * size) / 2; int offset = (bw * size + 1) * (st->digits - len); win_clear(st->lwin); if (use_color) wattron(st->lwin, COLOR_PAIR(color)); for (int d = 0; d < len; d++) { int dx = startx + d * (bw * size + 1) + offset; if (buf[d] == '.') { for (int yy = 0; yy < size; yy++) for (int xx = 0; xx < size; xx++) mvwaddch( st->lwin, starty + (bh - 1) * size + yy, dx + xx, ACS_CKBOARD ); continue; } int digit = buf[d] - '0'; for (int y = 0; y < bh; y++) { for (int x = 0; x < bw; x++) { if (digit_bitmaps[digit][y][x]) { for (int yy = 0; yy < size; yy++) for (int xx = 0; xx < size; xx++) mvwaddch(st->lwin, starty + y * size + yy, dx + x * size + xx, ACS_CKBOARD); } } } } if (use_color) wattroff(st->lwin, COLOR_PAIR(color)); wrefresh(st->lwin); } void win_bar(StInt *st, bool use_color) { int lwiny, lwinx; getmaxyx(st->lwin, lwiny, lwinx); win_clear(st->lwin); int bar_width = lwinx - 4; int bar_height = lwiny - 2; int filled = (st->data * bar_width) / st->hdata; int yellow = (75 * bar_width) / 100; int red = (90 * bar_width) / 100; for (int y = 1; y <= bar_height; y++) { for (int x = 0; x < filled; x++) { if (use_color) { if (x < yellow) wattron(st->lwin, COLOR_PAIR(3)); else if (x < red) wattron(st->lwin, COLOR_PAIR(2)); else wattron(st->lwin, COLOR_PAIR(1)); } mvwaddch(st->lwin, y, 2 + x, ACS_CKBOARD); } } if (use_color) { wattroff(st->lwin, COLOR_PAIR(3)); wattroff(st->lwin, COLOR_PAIR(2)); wattroff(st->lwin, COLOR_PAIR(1)); } char buf[16]; int len; snprintf(buf , sizeof(buf), "%d", st->data); len = strlen(buf); int bh = 5; // bitmap height int bw = 3; // bitmap width int size = smaller_of((lwiny - 2) / bh, (lwinx - 2 - (st->digits - 1)) / (bw * st->digits)); int total_width = st->digits * (bw * size) + (st->digits - 1); int startx = (lwinx - total_width) / 3; int starty = (lwiny - bh * size) / 2; int offset = (bw * size + 1) * (st->digits - len); for (int d = 0; d < len; d++) { int digit = buf[d] - '0'; int dx = startx + d * (bw * size + 1) + offset; for (int y = 0; y < bh; y++) { for (int x = 0; x < bw; x++) { if (digit_bitmaps[digit][y][x]) { for (int yy = 0; yy < size; yy++) for (int xx = 0; xx < size; xx++) mvwaddch(st->lwin, starty + y * size + yy, dx + x * size + xx, ACS_CKBOARD); } } } } wrefresh(st->lwin); if (use_color) { wattroff(st->lwin, COLOR_PAIR(0)); } } int main(int argc, char **argv) { int option; int delay = 1; int fake_data = 0; int use_can = 0; int soc = 0; int use_uart = 0; int uart_fd = 0; struct sockaddr_can addr; struct ifreq ifr; struct termios uart_str; SerialParser uart_parser = {0}; while ((option = getopt(argc, argv, "d:hfcu")) !=-1) { switch (option) { case 'd' : delay = atoi(optarg); if (delay < 0) { delay = 0; } break; case 'f' : fake_data = 1; break; case 'c' : use_can = 1; break; case 'u' : use_uart = 1; break; case 'h' : printf("Usage: ./cocomobile-tui [-d int ] [-h ] [-f ] [-c ] [-u ]\n"); return(0); } } if (use_can) { soc = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (soc < 0) { perror("socket error"); return 1; } fcntl(soc, F_SETFL, O_NONBLOCK); strcpy(ifr.ifr_name, "can0"); if (ioctl(soc, SIOCGIFINDEX, &ifr) < 0) { perror("ioctl error"); close(soc); return 1; } addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; if (bind(soc, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind error"); close(soc); return 1; } } if (use_uart) { uart_fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY | O_NONBLOCK); if (uart_fd < 0) { perror("uart open error"); return 1; } tcgetattr(uart_fd, &uart_str); uart_str.c_cflag &= ~CSIZE; // clear bit size uart_str.c_cflag |= (CLOCAL | CREAD | CS8 | B115200); uart_str.c_lflag = 0; // raw mode uart_str.c_iflag = 0; uart_str.c_oflag = 0; tcsetattr(uart_fd, TCSAFLUSH, &uart_str); // aplly settings } initscr(); noecho(); cbreak(); curs_set(0); nodelay(stdscr, TRUE); refresh(); int y,x; getmaxyx(stdscr, y, x); if (y < 42 || x < 100) { endwin(); perror("terminal window too small"); return 1; } bool use_color = 0; if (has_colors()) { use_color = 1; start_color(); use_default_colors(); init_pair(1, COLOR_RED, -1); init_pair(2, COLOR_YELLOW, -1); init_pair(3, COLOR_GREEN, -1); } int x3 = x / 3; int x3r = x - 2 * x3; int y2 = y / 2; int y2r = y - y2; int y4 = y / 4; int y4r = y2 - y4; int y2r4 = y2r / 2; int y2r4r = y2r - y2r4; int y6 = y2r / 3; int y6r = y - (y2 + 2* y6); Tel tel; tel.speed = (StInt){0 , 0 , 200 , 3, 1, y2 , x3 , 0 , 0 , NULL, "speed (km/h)" }; tel.rpm = (StInt){0 , 0 , 6000 , 4, 1, y2 , x3 , 0 , x3 , NULL, "rpm (tr/min)" }; tel.power = (StInt){0 , 0 , 1300 , 4, 1, 2*y6 , 2*x3, y2 , 0 , NULL, "power (W)" }; tel.bat = (StInt){0 , 0 , 100 , 3, 0, y4 , x3r , 0 , x-x3r , NULL, "batteries (%)" }; tel.eff = (StFlt){0.0f, 0.0f, 300.0f, 5, 0, y4r , x3r , y4 , x-x3r , NULL, "efficiency (Wh/Km)" }; tel.tens = (StFlt){0.0f, 0.0f, 55.0f , 4, 1, y2r4 , x3r , y2 , x-x3r , NULL, "tension (V)" }; tel.amp = (StFlt){0.0f, 0.0f, 55.0f , 4, 1, y2r4r, x3r , y-y2r4r, x-x3r , NULL, "intensity (A)" }; tel.message = (StStr){ y6r , 2*x3, y-y6r , 0 , NULL, "warnings" }; win_init_int(&tel.speed); win_init_int(&tel.rpm); win_init_int(&tel.power); win_init_flt(&tel.eff); win_init_int(&tel.bat); win_init_flt(&tel.tens); win_init_flt(&tel.amp); win_init_str(&tel.message); bar_mark(tel.power.lwin); long t100 = 0; int ch = ERR; while(1) { ch = tolower(getch()); switch (ch) { case 'q' : goto end; case ERR : default : if (fake_data) { get_fake_data(&tel); } else { if (use_can) read_can(&tel, soc); if (use_uart) read_uart(&tel, uart_fd, &uart_parser); calc_data(&tel); } break; } long now = now_ms(); if (now - t100 >= 100 * delay) { win_int(&tel.speed, use_color); win_int(&tel.rpm, use_color); win_bar(&tel.power, use_color); win_float(&tel.eff, use_color); win_float(&tel.tens, use_color); win_float(&tel.amp, use_color); win_int(&tel.bat, use_color); t100 = now; } //message; napms(10); }; end: delwin(tel.speed.lwin); delwin(tel.rpm.lwin); delwin(tel.power.lwin); delwin(tel.eff.lwin); delwin(tel.bat.lwin); delwin(tel.tens.lwin); delwin(tel.amp.lwin); delwin(tel.message.lwin); endwin(); if (use_can) close(soc); return 0; }