6cf4f2abde51e745b68fa59f7e61957cb5ec3642
[feed/packages.git] / utils / gl-puli-mcu / src / gl-puli-mcu.c
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
15 *
16 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
17 * Copyright (C) 2021 Nuno Goncalves <nunojpg@gmail.com>
18 */
19
20 #include <fcntl.h>
21 #include <termios.h>
22
23 #include <libubox/ulog.h>
24 #include <libubox/ustream.h>
25 #include <libubox/utils.h>
26 #include <libubox/uloop.h>
27 #include <libubus.h>
28
29 static struct ustream_fd stream;
30 static struct ubus_auto_conn conn;
31 static struct blob_buf b;
32
33 struct Battery
34 {
35 float temperature;
36 uint16_t cycles;
37 uint8_t soc;
38 bool charging;
39 bool set;
40 } battery;
41
42 static bool
43 process(char *read)
44 {
45 if (read[0] != '{' ||
46 read[1] != 'O' ||
47 read[2] != 'K' ||
48 read[3] != '}' ||
49 read[4] != ',')
50 return false;
51 const char *from = read + 5;
52 char *to;
53 battery.soc = strtoul(from, &to, 10);
54 if (from == to)
55 return false;
56 from = to + 1;
57 battery.temperature = strtoul(from, &to, 10) / 10.0f;
58 if (from == to)
59 return false;
60 if (to[0] != ',' || (to[1] != '0' && to[1] != '1') || to[2] != ',')
61 return false;
62 battery.charging = to[1] == '1';
63 from = to + 3;
64 battery.cycles = strtoul(from, &to, 10);
65 if (from == to)
66 return false;
67 return true;
68 }
69
70 static int
71 consume(struct ustream *s, char **a)
72 {
73 char *eol = strstr(*a, "\n");
74
75 if (!eol)
76 return -1;
77
78 *eol++ = '\0';
79
80 battery.set = process(*a);
81 if (!battery.set)
82 ULOG_ERR("failed to parse message from serial: %s", *a);
83
84 ustream_consume(s, eol - *a);
85 *a = eol;
86
87 return 0;
88 }
89
90 static void
91 msg_cb(struct ustream *s, int bytes)
92 {
93 int len;
94 char *a = ustream_get_read_buf(s, &len);
95
96 while (!consume(s, &a))
97 ;
98 }
99
100 static void
101 notify_cb(struct ustream *s)
102 {
103 if (!s->eof)
104 return;
105
106 ULOG_ERR("tty error, shutting down\n");
107 exit(-1);
108 }
109
110 static int
111 serial_open(char *dev)
112 {
113 const int tty = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
114 if (tty < 0)
115 {
116 ULOG_ERR("%s: device open failed: %s\n", dev, strerror(errno));
117 return -1;
118 }
119
120 struct termios config;
121 tcgetattr(tty, &config);
122 cfmakeraw(&config);
123 cfsetispeed(&config, B9600);
124 cfsetospeed(&config, B9600);
125 tcsetattr(tty, TCSANOW, &config);
126
127 stream.stream.string_data = true;
128 stream.stream.notify_read = msg_cb;
129 stream.stream.notify_state = notify_cb;
130
131 ustream_fd_init(&stream, tty);
132
133 tcflush(tty, TCIFLUSH);
134
135 return 0;
136 }
137
138 static struct uloop_timeout serial_query_timer;
139 static void
140 serial_query_handler(struct uloop_timeout *timeout)
141 {
142 const char cmd[] = "{ \"mcu_status\": \"1\" }\n";
143 const unsigned cmd_len = sizeof(cmd) - 1;
144 ustream_write(&stream.stream, cmd, cmd_len, false);
145 uloop_timeout_set(&serial_query_timer, 3000); // timeout in 3 sec
146 uloop_timeout_add(timeout);
147 }
148
149 static int
150 battery_info(struct ubus_context *ctx, struct ubus_object *obj,
151 struct ubus_request_data *req, const char *method,
152 struct blob_attr *msg)
153 {
154 blob_buf_init(&b, 0);
155
156 if (!battery.set)
157 {
158 blobmsg_add_u8(&b, "error", 1);
159 }
160 else
161 {
162 blobmsg_add_u16(&b, "soc", battery.soc);
163 blobmsg_add_u8(&b, "charging", battery.charging);
164 blobmsg_add_double(&b, "temperature", battery.temperature);
165 blobmsg_add_u16(&b, "cycles", battery.cycles);
166 }
167 ubus_send_reply(ctx, req, b.head);
168
169 return UBUS_STATUS_OK;
170 }
171
172 static const struct ubus_method battery_methods[] = {
173 UBUS_METHOD_NOARG("info", battery_info),
174 };
175
176 static struct ubus_object_type battery_object_type =
177 UBUS_OBJECT_TYPE("battery", battery_methods);
178
179 static struct ubus_object battery_object = {
180 .name = "battery",
181 .type = &battery_object_type,
182 .methods = battery_methods,
183 .n_methods = ARRAY_SIZE(battery_methods),
184 };
185
186 static void
187 ubus_connect_handler(struct ubus_context *ctx)
188 {
189 int ret;
190
191 ret = ubus_add_object(ctx, &battery_object);
192 if (ret)
193 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
194 }
195
196 int
197 main(int argc, char **argv)
198 {
199
200 uloop_init();
201 conn.path = NULL;
202 conn.cb = ubus_connect_handler;
203 ubus_auto_connect(&conn);
204
205 if (serial_open("/dev/ttyUSB0") < 0)
206 return -1;
207
208 serial_query_timer.cb = serial_query_handler;
209 serial_query_handler(&serial_query_timer);
210 uloop_run();
211 uloop_done();
212
213 return 0;
214 }