15 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
17 static int separate_entries(char *buf, char *entries[], int max_entries)
24 while (*buf && i< max_entries) {
39 static void init_data(struct charger_data *data)
43 bzero(data, sizeof(*data));
44 for (i = 0; i < MAX_CELLS; i++)
45 data->cell_voltage[i] = NAN;
47 data->timestamp = NAN;
48 data->input_voltage = NAN;
49 data->charging_voltage = NAN;
50 data->charging_current = NAN;
51 data->total_charge = NAN;
57 * Convert one log line into charger_data structure. Supports at least
58 * iCharger 6 and 10 cell chargers. Others are not tested.
60 * Returns negative on incorrect data, zero on success
62 static int parse_logline(const char *buf, struct charger_data *data)
64 int i, j, entry_count;
68 char *str = strdup(buf);
71 entry_count = separate_entries(str, entries, ARRAY_SIZE(entries));
73 pr_debug("Entry count: %d\n", entry_count);
77 if (entries[0][0] != '$') {
78 pr_debug("Discarding malformed data entry\n");
82 for (i = 0; i < entry_count; i++)
83 pr_debug("Entry %d: data: %s\n", i, entries[i]);
86 entries[i]++; /* discard the dollar sign */
87 data->channel = atoi(entries[i++]);
89 data->state = atoi(entries[i++]);
91 /* Timestamp is optional */
92 if (strlen(entries[1]) > 0)
93 data->timestamp = atof(entries[i++]);
95 data->input_voltage = atof(entries[i++]) / 1000.0;
96 data->charging_voltage = atof(entries[i++]) / 1000.0;
97 data->charging_current = atof(entries[i++]) / 100.0;
99 #define ASSIGN_OR_NAN(data, val) \
107 max_cells = entry_count - 10;
108 pr_debug("max_cells: %d\n", max_cells);
110 for (j = 0; j < max_cells; j++, i++) {
111 d = atoi(entries[i]);
112 ASSIGN_OR_NAN(data->cell_voltage[j], d / 1000.0);
115 d = atoi(entries[i++]);
116 ASSIGN_OR_NAN(data->int_temp, d / 10.0);
118 d = atoi(entries[i++]);
119 ASSIGN_OR_NAN(data->ext_temp, d / 10.0);
121 data->total_charge = atof(entries[i++]);
129 static void dump_data(struct charger_data *data)
133 pr_debug("channel %d\n", data->channel);
134 pr_debug("state %d\n", data->state);
135 pr_debug("timestamp %.1f\n", data->timestamp);
136 pr_debug("input_voltage %.3f\n", data->input_voltage);
137 pr_debug("charging_voltage %.3f\n", data->charging_voltage);
138 pr_debug("charging_current %.3f\n", data->charging_current);
140 for (i = 0; i < MAX_CELLS; i++) {
141 if (isnan(data->cell_voltage[i]))
144 pr_debug("cell_voltage[%d] %f\n", i,
145 data->cell_voltage[i]);
148 pr_debug("total_charge %.0f\n", data->total_charge);
149 pr_debug("int_temp %.1f\n", data->int_temp);
150 pr_debug("ext_temp %.1f\n", data->ext_temp);
153 static void print_status_line(struct charger_data *data)
155 int i, active_cells = 0;
159 if (data->timestamp > 3600) {
160 snprintf(time_str, sizeof(time_str), "%d:%02d:%02d",
161 (int)(data->timestamp / 3600),
162 (int)((int)data->timestamp % 3600) / 60,
163 (int)data->timestamp % 60);
165 snprintf(time_str, sizeof(time_str), "%2d:%02d",
166 ((int)data->timestamp % 3600) / 60,
167 (int)data->timestamp % 60);
170 for (i = 0; i < MAX_CELLS; i++) {
171 if (!isnan(data->cell_voltage[i])) {
172 cell_avg += data->cell_voltage[i];
176 cell_avg /= (double)active_cells;
178 pr_info("\r\033[K%8s Ubat: %.3fV Ucell avg: %.3fV "
179 "Current: %.2fA Charge %.0fmAh ",
180 time_str, data->charging_voltage, cell_avg,
181 data->charging_current, data->total_charge);
188 * Read data from a slow device
190 * return 1 when a complete NULL terminated line has been read
192 * return 0 when a partial line has been read and appended to the
195 * return negative on error
197 static int read_log_line(int infd, char *buf, size_t bufsize, int *offset)
202 ret = read(infd, buf + *offset, bufsize - *offset - 1);
204 pr_err("read: %m\n");
209 pr_err("Read EOF, stopping\n");
212 buf[*offset + ret] = 0;
214 for (i = 0; i < ret; i++) {
215 if (buf[i + *offset] == '\n' ||
216 buf[i + *offset] == '\r') {
218 * Got a complete line when there is a newline
219 * at the end. Remove the newline and possible
220 * other junk, such as '\r'
222 buf[i + *offset] = 0;
229 * Fixme! Nothing guarantees that there isn't actually
230 * more data (a part of a new log entry perhaps) after
231 * the newline. So in rare cases (we are prevented
232 * from reading the serial line in very long time) we
233 * might lose data from the stream..
241 int read_data(int infd, int outfd)
243 struct epoll_event ev;
244 time_t start_time = 0, cur_time;
250 epoll_fd = epoll_create(1);
252 pr_err("Failed to create epoll socket: %m\n");
258 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, infd, &ev) == -1) {
259 pr_err("epoll_ctl: %m\n");
264 struct charger_data data;
268 ret = epoll_wait(epoll_fd, &ev, 1, -1);
273 pr_err("epoll: %m\n");
277 ret = read_log_line(infd, buf, sizeof(buf), &offset);
284 if (strlen(buf) < 5) {
285 pr_debug("discarding truncated log entry\n");
291 start_time = time(NULL);
293 cur_time = time(NULL);
295 parse_logline(buf, &data);
297 /* Fill in possibly missing timestamp */
298 if (isnan(data.timestamp) || data.timestamp == 0);
299 data.timestamp = cur_time - start_time;
301 print_status_line(&data);
308 len = snprintf(str, sizeof(str),
311 "%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;"
312 "%.f;%.1f;%.1f\n", /* mAh, and temp */
318 data.charging_voltage,
319 data.charging_current,
321 data.cell_voltage[0],
322 data.cell_voltage[1],
323 data.cell_voltage[2],
324 data.cell_voltage[3],
325 data.cell_voltage[4],
326 data.cell_voltage[5],
327 data.cell_voltage[6],
328 data.cell_voltage[7],
329 data.cell_voltage[8],
330 data.cell_voltage[9],
335 ret = write(outfd, str, len);
337 pr_err("write: %m\n");