/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * Copyright (C) 2014 John Crispin * Copyright (C) 2021 Nuno Goncalves */ #include #include #include #include #include #include #include static struct ustream_fd stream; static struct ubus_auto_conn conn; static struct blob_buf b; struct Battery { float temperature; uint16_t cycles; uint8_t soc; bool charging; bool set; } battery; static bool process(char *read) { if (read[0] != '{' || read[1] != 'O' || read[2] != 'K' || read[3] != '}' || read[4] != ',') return false; const char *from = read + 5; char *to; battery.soc = strtoul(from, &to, 10); if (from == to) return false; from = to + 1; battery.temperature = strtoul(from, &to, 10) / 10.0f; if (from == to) return false; if (to[0] != ',' || (to[1] != '0' && to[1] != '1') || to[2] != ',') return false; battery.charging = to[1] == '1'; from = to + 3; battery.cycles = strtoul(from, &to, 10); if (from == to) return false; return true; } static int consume(struct ustream *s, char **a) { char *eol = strstr(*a, "\n"); if (!eol) return -1; *eol++ = '\0'; battery.set = process(*a); if (!battery.set) ULOG_ERR("failed to parse message from serial: %s", a); ustream_consume(s, eol - *a); *a = eol; return 0; } static void msg_cb(struct ustream *s, int bytes) { int len; char *a = ustream_get_read_buf(s, &len); while (!consume(s, &a)) ; } static void notify_cb(struct ustream *s) { if (!s->eof) return; ULOG_ERR("tty error, shutting down\n"); exit(-1); } static int serial_open(char *dev) { const int tty = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK); if (tty < 0) { ULOG_ERR("%s: device open failed: %s\n", dev, strerror(errno)); return -1; } struct termios config; tcgetattr(tty, &config); cfmakeraw(&config); cfsetispeed(&config, B9600); cfsetospeed(&config, B9600); tcsetattr(tty, TCSANOW, &config); stream.stream.string_data = true; stream.stream.notify_read = msg_cb; stream.stream.notify_state = notify_cb; ustream_fd_init(&stream, tty); tcflush(tty, TCIFLUSH); return 0; } static struct uloop_timeout serial_query_timer; static void serial_query_handler(struct uloop_timeout *timeout) { const char cmd[] = "{ \"mcu_status\": \"1\" }\n"; const unsigned cmd_len = sizeof(cmd) - 1; ustream_write(&stream.stream, cmd, cmd_len, false); uloop_timeout_set(&serial_query_timer, 3000); // timeout in 3 sec uloop_timeout_add(timeout); } static int battery_info(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { blob_buf_init(&b, 0); if (!battery.set) { blobmsg_add_u8(&b, "error", 1); } else { blobmsg_add_u16(&b, "soc", battery.soc); blobmsg_add_u8(&b, "charging", battery.charging); blobmsg_add_double(&b, "temperature", battery.temperature); blobmsg_add_u16(&b, "cycles", battery.cycles); } ubus_send_reply(ctx, req, b.head); return UBUS_STATUS_OK; } static const struct ubus_method battery_methods[] = { UBUS_METHOD_NOARG("info", battery_info), }; static struct ubus_object_type battery_object_type = UBUS_OBJECT_TYPE("battery", battery_methods); static struct ubus_object battery_object = { .name = "battery", .type = &battery_object_type, .methods = battery_methods, .n_methods = ARRAY_SIZE(battery_methods), }; static void ubus_connect_handler(struct ubus_context *ctx) { int ret; ret = ubus_add_object(ctx, &battery_object); if (ret) fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); } int main(int argc, char **argv) { uloop_init(); conn.path = NULL; conn.cb = ubus_connect_handler; ubus_auto_connect(&conn); if (serial_open("/dev/ttyUSB0") < 0) return -1; serial_query_timer.cb = serial_query_handler; serial_query_handler(&serial_query_timer); uloop_run(); uloop_done(); return 0; }