]> sigrok.org Git - libsigrok.git/blame - src/hardware/mooshimeter-dmm/api.c
Add support for the Mooshimeter DMM
[libsigrok.git] / src / hardware / mooshimeter-dmm / api.c
CommitLineData
ebcd1aba
DH
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2019 Derek Hageman <hageman@inthat.cloud>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <config.h>
21#include "protocol.h"
22
23static struct sr_dev_driver mooshimeter_dmm_driver_info;
24
25static const uint32_t scanopts[] = {
26 SR_CONF_CONN,
27};
28
29static const uint32_t drvopts[] = {
30 SR_CONF_MULTIMETER,
31};
32
33static const uint32_t devopts[] = {
34 SR_CONF_CONTINUOUS,
35 SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_LIST,
36 SR_CONF_LIMIT_MSEC | SR_CONF_SET | SR_CONF_LIST,
37 SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
38 SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
39 SR_CONF_CHANNEL_CONFIG | SR_CONF_SET,
40};
41
42static void init_dev(struct sr_dev_inst *sdi)
43{
44 struct dev_context *devc;
45 struct sr_channel *chan;
46
47 devc = g_new0(struct dev_context, 1);
48 sdi->priv = devc;
49 sdi->status = SR_ST_INITIALIZING;
50 sdi->vendor = g_strdup("Mooshim Engineering");
51 sdi->model = g_strdup("Mooshimeter");
52
53 sr_sw_limits_init(&devc->limits);
54
55 chan = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
56 devc->channel_meaning[0].mq = SR_MQ_CURRENT;
57 devc->channel_meaning[0].unit = SR_UNIT_AMPERE;
58 devc->channel_meaning[0].mqflags = SR_MQFLAG_DC;
59 devc->channel_meaning[0].channels = g_slist_prepend(NULL, chan);
60
61 chan = sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "CH2");
62 devc->channel_meaning[1].mq = SR_MQ_VOLTAGE;
63 devc->channel_meaning[1].unit = SR_UNIT_VOLT;
64 devc->channel_meaning[1].mqflags = SR_MQFLAG_DC;
65 devc->channel_meaning[1].channels = g_slist_prepend(NULL, chan);
66
67 chan = sr_channel_new(sdi, 2, SR_CHANNEL_ANALOG, FALSE, "P");
68 devc->channel_meaning[2].mq = SR_MQ_POWER;
69 devc->channel_meaning[2].unit = SR_UNIT_WATT;
70 devc->channel_meaning[2].mqflags = SR_MQFLAG_RMS;
71 devc->channel_meaning[2].channels = g_slist_prepend(NULL, chan);
72}
73
74static GSList *scan(struct sr_dev_driver *di, GSList *options)
75{
76 struct sr_bt_desc *desc;
77 const char *conn;
78 struct sr_config *src;
79 GSList *l;
80 int ret;
81
82 conn = NULL;
83 for (l = options; l; l = l->next) {
84 src = l->data;
85 switch (src->key) {
86 case SR_CONF_CONN:
87 conn = g_variant_get_string(src->data, NULL);
88 break;
89 }
90 }
91
92 if (!conn)
93 return NULL;
94
95 desc = sr_bt_desc_new();
96 if (!desc)
97 return NULL;
98
99 ret = sr_bt_config_addr_remote(desc, conn);
100 if (ret < 0)
101 goto err;
102
103 /*
104 * These handles where queried with btgatt-client, since the
105 * documentation specifies them in terms of UUIDs.
106 *
107 * service - start: 0x0010, end: 0xffff, type: primary, uuid: 1bc5ffa0-0200-62ab-e411-f254e005dbd4
108 * charac - start: 0x0011, value: 0x0012, props: 0x08, ext_props: 0x0000, uuid: 1bc5ffa1-0200-62ab-e411-f254e005dbd4
109 * descr - handle: 0x0013, uuid: 00002901-0000-1000-8000-00805f9b34fb
110 * charac - start: 0x0014, value: 0x0015, props: 0x10, ext_props: 0x0000, uuid: 1bc5ffa2-0200-62ab-e411-f254e005dbd4
111 * descr - handle: 0x0016, uuid: 00002902-0000-1000-8000-00805f9b34fb
112 * descr - handle: 0x0017, uuid: 00002901-0000-1000-8000-00805f9b34fb
113 */
114 ret = sr_bt_config_notify(desc, 0x0015, 0x0012, 0x0016, 0x0001);
115 if (ret < 0)
116 goto err;
117
118 struct sr_dev_inst *sdi = g_malloc0(sizeof(struct sr_dev_inst));
119 struct dev_context *devc = g_malloc0(sizeof(struct dev_context));
120
121 sdi->priv = devc;
122 sdi->inst_type = SR_INST_USER;
123 sdi->connection_id = g_strdup(conn);
124 sdi->conn = desc;
125
126 init_dev(sdi);
127
128 return std_scan_complete(di, g_slist_prepend(NULL, sdi));
129
130err:
131 sr_bt_desc_free(desc);
132 return NULL;
133}
134
135static int dev_clear(const struct sr_dev_driver *di)
136{
137 struct drv_context *drvc = di->context;
138 struct sr_dev_inst *sdi;
139 GSList *l;
140
141 if (drvc) {
142 for (l = drvc->instances; l; l = l->next) {
143 sdi = l->data;
144 struct sr_bt_desc *desc = sdi->conn;
145 if (desc)
146 sr_bt_desc_free(desc);
147 sdi->conn = NULL;
148 }
149 }
150
151 return std_dev_clear(di);
152}
153
154static int set_channel1_mean(const struct sr_dev_inst *sdi)
155{
156 struct dev_context *devc = sdi->priv;
157 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_RMS;
158 devc->channel_meaning[0].mqflags |= SR_MQFLAG_DC;
159
160 return mooshimeter_dmm_set_chooser(sdi, "CH1:ANALYSIS",
161 "CH1:ANALYSIS:MEAN");
162}
163
164static int set_channel1_rms(const struct sr_dev_inst *sdi)
165{
166 struct dev_context *devc = sdi->priv;
167 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DC;
168 devc->channel_meaning[0].mqflags |= SR_MQFLAG_RMS;
169
170 return mooshimeter_dmm_set_chooser(sdi, "CH1:ANALYSIS",
171 "CH1:ANALYSIS:RMS");
172}
173
174static int set_channel1_buffer(const struct sr_dev_inst *sdi)
175{
176 struct dev_context *devc = sdi->priv;
177 devc->channel_meaning[0].mqflags &= ~(SR_MQFLAG_DC | SR_MQFLAG_RMS);
178
179 return mooshimeter_dmm_set_chooser(sdi, "CH1:ANALYSIS",
180 "CH1:ANALYSIS:BUFFER");
181}
182
183static int set_channel2_mean(const struct sr_dev_inst *sdi)
184{
185 struct dev_context *devc = sdi->priv;
186 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_RMS;
187 devc->channel_meaning[1].mqflags |= SR_MQFLAG_DC;
188
189 return mooshimeter_dmm_set_chooser(sdi, "CH2:ANALYSIS",
190 "CH2:ANALYSIS:MEAN");
191}
192
193static int set_channel2_rms(const struct sr_dev_inst *sdi)
194{
195 struct dev_context *devc = sdi->priv;
196 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DC;
197 devc->channel_meaning[1].mqflags |= SR_MQFLAG_RMS;
198
199 return mooshimeter_dmm_set_chooser(sdi, "CH2:ANALYSIS",
200 "CH2:ANALYSIS:RMS");
201}
202
203static int set_channel2_buffer(const struct sr_dev_inst *sdi)
204{
205 struct dev_context *devc = sdi->priv;
206
207 devc->channel_meaning[1].mqflags &= ~(SR_MQFLAG_DC | SR_MQFLAG_RMS);
208
209 return mooshimeter_dmm_set_chooser(sdi, "CH2:ANALYSIS",
210 "CH2:ANALYSIS:BUFFER");
211}
212
213static void autorange_channel1_current(const struct sr_dev_inst *sdi,
214 float value)
215{
216 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
217 "CH1:MAPPING:CURRENT", value);
218}
219
220static int configure_channel1_current(const struct sr_dev_inst *sdi,
221 float range)
222{
223 struct dev_context *devc = sdi->priv;
224 int ret;
225
226 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
227 "CH1:MAPPING:CURRENT");
228 if (ret != SR_OK)
229 return ret;
230
231 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
232 "CH1:MAPPING:CURRENT", range);
233 if (ret != SR_OK)
234 return ret;
235
236 if (range <= 0) {
237 devc->channel_autorange[0] = autorange_channel1_current;
238 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
239 } else {
240 devc->channel_autorange[0] = NULL;
241 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
242 }
243
244 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE;
245 devc->channel_meaning[0].mq = SR_MQ_CURRENT;
246 devc->channel_meaning[0].unit = SR_UNIT_AMPERE;
247
248 return SR_OK;
249}
250
251static void autorange_channel1_temperature(const struct sr_dev_inst *sdi,
252 float value)
253{
254 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
255 "CH1:MAPPING:TEMP", value);
256}
257
258static int configure_channel1_temperature(const struct sr_dev_inst *sdi,
259 float range)
260{
261 struct dev_context *devc = sdi->priv;
262 int ret;
263
264 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
265 "CH1:MAPPING:TEMP");
266 if (ret != SR_OK)
267 return ret;
268
269 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
270 "CH1:MAPPING:TEMP", range);
271 if (ret != SR_OK)
272 return ret;
273
274 if (range <= 0) {
275 devc->channel_autorange[0] = autorange_channel1_temperature;
276 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
277 } else {
278 devc->channel_autorange[0] = NULL;
279 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
280 }
281
282 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE;
283 devc->channel_meaning[0].mq = SR_MQ_TEMPERATURE;
284 devc->channel_meaning[0].unit = SR_UNIT_KELVIN;
285
286 return SR_OK;
287}
288
289static void autorange_channel1_auxv(const struct sr_dev_inst *sdi,
290 float value)
291{
292 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
293 "SHARED:AUX_V", value);
294}
295
296static int configure_channel1_auxv(const struct sr_dev_inst *sdi,
297 float range)
298{
299 struct dev_context *devc = sdi->priv;
300 int ret;
301
302 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:AUX_V");
303 if (ret != SR_OK)
304 return ret;
305
306 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
307 "CH1:MAPPING:SHARED");
308 if (ret != SR_OK)
309 return ret;
310
311 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
312 "SHARED:AUX_V", range);
313 if (ret != SR_OK)
314 return ret;
315
316 if (range <= 0) {
317 devc->channel_autorange[0] = autorange_channel1_auxv;
318 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
319 } else {
320 devc->channel_autorange[0] = NULL;
321 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
322 }
323
324 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE;
325 devc->channel_meaning[0].mq = SR_MQ_VOLTAGE;
326 devc->channel_meaning[0].unit = SR_UNIT_VOLT;
327
328 return SR_OK;
329}
330
331static void autorange_channel1_resistance(const struct sr_dev_inst *sdi,
332 float value)
333{
334 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
335 "SHARED:RESISTANCE", value);
336}
337
338static int configure_channel1_resistance(const struct sr_dev_inst *sdi,
339 float range)
340{
341 struct dev_context *devc = sdi->priv;
342 int ret;
343
344 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:RESISTANCE");
345 if (ret != SR_OK)
346 return ret;
347
348 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
349 "CH1:MAPPING:SHARED");
350 if (ret != SR_OK)
351 return ret;
352
353 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
354 "SHARED:RESISTANCE", range);
355 if (ret != SR_OK)
356 return ret;
357
358 if (range <= 0) {
359 devc->channel_autorange[0] = autorange_channel1_resistance;
360 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
361 } else {
362 devc->channel_autorange[0] = NULL;
363 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
364 }
365
366 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE;
367 devc->channel_meaning[0].mq = SR_MQ_RESISTANCE;
368 devc->channel_meaning[0].unit = SR_UNIT_OHM;
369
370 return SR_OK;
371}
372
373static void autorange_channel1_diode(const struct sr_dev_inst *sdi,
374 float value)
375{
376 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
377 "SHARED:DIODE", value);
378}
379
380static int configure_channel1_diode(const struct sr_dev_inst *sdi,
381 float range)
382{
383 struct dev_context *devc = sdi->priv;
384 int ret;
385
386 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:DIODE");
387 if (ret != SR_OK)
388 return ret;
389
390 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
391 "CH1:MAPPING:SHARED");
392 if (ret != SR_OK)
393 return ret;
394
395 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
396 "SHARED:DIODE", range);
397 if (ret != SR_OK)
398 return ret;
399
400 if (range <= 0) {
401 devc->channel_autorange[0] = autorange_channel1_diode;
402 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
403 } else {
404 devc->channel_autorange[0] = NULL;
405 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
406 }
407
408 devc->channel_meaning[0].mqflags |= SR_MQFLAG_DIODE;
409 devc->channel_meaning[0].mq = SR_MQ_VOLTAGE;
410 devc->channel_meaning[0].unit = SR_UNIT_VOLT;
411
412 return SR_OK;
413}
414
415static void autorange_channel2_voltage(const struct sr_dev_inst *sdi,
416 float value)
417{
418 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
419 "CH2:MAPPING:VOLTAGE", value);
420}
421
422static int configure_channel2_voltage(const struct sr_dev_inst *sdi,
423 float range)
424{
425 struct dev_context *devc = sdi->priv;
426 int ret;
427
428 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
429 "CH2:MAPPING:VOLTAGE");
430 if (ret != SR_OK)
431 return ret;
432
433 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
434 "CH2:MAPPING:VOLTAGE", range);
435 if (ret != SR_OK)
436 return ret;
437
438 if (range <= 0) {
439 devc->channel_autorange[1] = autorange_channel2_voltage;
440 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
441 } else {
442 devc->channel_autorange[1] = NULL;
443 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
444 }
445
446 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE;
447 devc->channel_meaning[1].mq = SR_MQ_VOLTAGE;
448 devc->channel_meaning[1].unit = SR_UNIT_VOLT;
449
450 return SR_OK;
451}
452
453static void autorange_channel2_temperature(const struct sr_dev_inst *sdi,
454 float value)
455{
456 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
457 "CH2:MAPPING:TEMP", value);
458}
459
460static int configure_channel2_temperature(const struct sr_dev_inst *sdi,
461 float range)
462{
463 struct dev_context *devc = sdi->priv;
464 int ret;
465
466 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
467 "CH2:MAPPING:TEMP");
468 if (ret != SR_OK)
469 return ret;
470
471 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
472 "CH2:MAPPING:TEMP", range);
473 if (ret != SR_OK)
474 return ret;
475
476 if (range <= 0) {
477 devc->channel_autorange[1] = autorange_channel2_temperature;
478 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
479 } else {
480 devc->channel_autorange[1] = NULL;
481 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
482 }
483
484 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE;
485 devc->channel_meaning[1].mq = SR_MQ_TEMPERATURE;
486 devc->channel_meaning[1].unit = SR_UNIT_CELSIUS;
487
488 return SR_OK;
489}
490
491static void autorange_channel2_auxv(const struct sr_dev_inst *sdi,
492 float value)
493{
494 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
495 "SHARED:AUX_V", value);
496}
497
498static int configure_channel2_auxv(const struct sr_dev_inst *sdi,
499 float range)
500{
501 struct dev_context *devc = sdi->priv;
502 int ret;
503
504 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:AUX_V");
505 if (ret != SR_OK)
506 return ret;
507
508 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
509 "CH2:MAPPING:SHARED");
510 if (ret != SR_OK)
511 return ret;
512
513 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
514 "SHARED:AUX_V", range);
515 if (ret != SR_OK)
516 return ret;
517
518 if (range <= 0) {
519 devc->channel_autorange[1] = autorange_channel2_auxv;
520 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
521 } else {
522 devc->channel_autorange[1] = NULL;
523 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
524 }
525
526 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE;
527 devc->channel_meaning[1].mq = SR_MQ_VOLTAGE;
528 devc->channel_meaning[1].unit = SR_UNIT_VOLT;
529
530 return SR_OK;
531}
532
533static void autorange_channel2_resistance(const struct sr_dev_inst *sdi,
534 float value)
535{
536 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
537 "SHARED:RESISTANCE", value);
538}
539
540static int configure_channel2_resistance(const struct sr_dev_inst *sdi,
541 float range)
542{
543 struct dev_context *devc = sdi->priv;
544 int ret;
545
546 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:RESISTANCE");
547 if (ret != SR_OK)
548 return ret;
549
550 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
551 "CH2:MAPPING:SHARED");
552 if (ret != SR_OK)
553 return ret;
554
555 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
556 "SHARED:RESISTANCE", range);
557 if (ret != SR_OK)
558 return ret;
559
560 if (range <= 0) {
561 devc->channel_autorange[1] = autorange_channel2_resistance;
562 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
563 } else {
564 devc->channel_autorange[1] = NULL;
565 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
566 }
567
568 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE;
569 devc->channel_meaning[1].mq = SR_MQ_RESISTANCE;
570 devc->channel_meaning[1].unit = SR_UNIT_OHM;
571
572 return SR_OK;
573}
574
575static void autorange_channel2_diode(const struct sr_dev_inst *sdi,
576 float value)
577{
578 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
579 "SHARED:DIODE", value);
580}
581
582static int configure_channel2_diode(const struct sr_dev_inst *sdi,
583 float range)
584{
585 struct dev_context *devc = sdi->priv;
586 int ret;
587
588 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:DIODE");
589 if (ret != SR_OK)
590 return ret;
591
592 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
593 "CH2:MAPPING:SHARED");
594 if (ret != SR_OK)
595 return ret;
596
597 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
598 "SHARED:DIODE", range);
599 if (ret != SR_OK)
600 return ret;
601
602 if (range <= 0) {
603 devc->channel_autorange[1] = autorange_channel2_diode;
604 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
605 } else {
606 devc->channel_autorange[1] = NULL;
607 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
608 }
609
610 devc->channel_meaning[1].mqflags |= SR_MQFLAG_DIODE;
611 devc->channel_meaning[1].mq = SR_MQ_VOLTAGE;
612 devc->channel_meaning[1].unit = SR_UNIT_VOLT;
613
614 return SR_OK;
615}
616
617/*
618 * Full string: CH1,CH2
619 * Each channel: MODE[:RANGE[:ANALYSIS]]
620 * Channel 1 mode:
621 * Current, A
622 * Temperature, T, K
623 * Resistance, Ohm, W
624 * Diode, D
625 * Aux, LV
626 * Channel 2 mode:
627 * Voltage, V
628 * Temperature, T, K
629 * Resistance, Ohm, W
630 * Diode, D
631 * Aux, LV
632 * Range is the upper bound of the range (e.g. 60 for 0-60 V or 600 for 0-600),
633 * zero or absent for autoranging
634 * Analysis:
635 * Mean, DC
636 * RMS, AC
637 * Buffer, Samples
638 */
639static int apply_channel_config(const struct sr_dev_inst *sdi,
640 const char *config)
641{
642 gchar **channel_config;
643 gchar **parameters;
644 const gchar *param;
645 int ret = SR_ERR;
646 float range;
647 gboolean shared_in_use = FALSE;
648
649 channel_config = g_strsplit_set(config, ",/", -1);
650 if (!channel_config[0])
651 goto err_free_channel_config;
652
653 parameters = g_strsplit_set(channel_config[0], ":;", -1);
654 if (parameters[0] && parameters[0][0]) {
655 range = 0;
656 if (parameters[1])
657 range = g_ascii_strtod(parameters[1], NULL);
658
659 param = parameters[0];
660 if (!g_ascii_strncasecmp(param, "Resistance", 10) ||
661 !g_ascii_strncasecmp(param, "Ohm", 3) ||
662 !g_ascii_strncasecmp(param, "W", 1) ||
663 !g_ascii_strncasecmp(param, "R", 1)) {
664 ret = configure_channel1_resistance(sdi, range);
665 if (ret != SR_OK)
666 goto err_free_parameters;
667 shared_in_use = TRUE;
668 } else if (!g_ascii_strncasecmp(param, "Diode", 5) ||
669 !g_ascii_strncasecmp(param, "D", 1)) {
670 ret = configure_channel1_diode(sdi, range);
671 if (ret != SR_OK)
672 goto err_free_parameters;
673 shared_in_use = TRUE;
674 } else if (!g_ascii_strncasecmp(param, "Aux", 3) ||
675 !g_ascii_strncasecmp(param, "LV", 2)) {
676 ret = configure_channel1_auxv(sdi, range);
677 if (ret != SR_OK)
678 goto err_free_parameters;
679 shared_in_use = TRUE;
680 } else if (!g_ascii_strncasecmp(param, "T", 1) ||
681 !g_ascii_strncasecmp(param, "K", 1)) {
682 ret = configure_channel1_temperature(sdi, range);
683 if (ret != SR_OK)
684 goto err_free_parameters;
685 } else if (!g_ascii_strncasecmp(param, "Current", 7) ||
686 !g_ascii_strncasecmp(param, "A", 1) ||
687 *parameters[0]) {
688 ret = configure_channel1_current(sdi, range);
689 if (ret != SR_OK)
690 goto err_free_parameters;
691 } else {
692 sr_info("Unrecognized mode for CH1: %s.", param);
693 ret = configure_channel1_current(sdi, range);
694 if (ret != SR_OK)
695 goto err_free_parameters;
696 }
697
698 if (parameters[1] && parameters[2]) {
699 param = parameters[2];
700 if (!g_ascii_strcasecmp(param, "RMS") ||
701 !g_ascii_strcasecmp(param, "AC")) {
702 ret = set_channel1_rms(sdi);
703 if (ret != SR_OK)
704 goto err_free_parameters;
705 } else if (!g_ascii_strcasecmp(param, "Buffer") ||
706 !g_ascii_strcasecmp(param, "Samples")) {
707 ret = set_channel1_buffer(sdi);
708 if (ret != SR_OK)
709 goto err_free_parameters;
710 } else {
711 ret = set_channel1_mean(sdi);
712 if (ret != SR_OK)
713 goto err_free_parameters;
714 }
715 }
716 }
717 g_strfreev(parameters);
718
719 if (!channel_config[1]) {
720 g_strfreev(channel_config);
721 return SR_OK;
722 }
723
724 parameters = g_strsplit_set(channel_config[1], ":;", -1);
725 if (parameters[0] && parameters[0][0]) {
726 range = 0;
727 if (parameters[1])
728 range = g_ascii_strtod(parameters[1], NULL);
729
730 param = parameters[0];
731 if (!g_ascii_strncasecmp(param, "Resistance", 10) ||
732 !g_ascii_strncasecmp(param, "Ohm", 3) ||
733 !g_ascii_strncasecmp(param, "W", 1) ||
734 !g_ascii_strncasecmp(param, "R", 1)) {
735 if (shared_in_use) {
736 ret = SR_ERR;
737 goto err_free_parameters;
738 }
739 ret = configure_channel2_resistance(sdi, range);
740 if (ret != SR_OK)
741 goto err_free_parameters;
742 } else if (!g_ascii_strncasecmp(param, "Diode", 5) ||
743 !g_ascii_strncasecmp(param, "D", 1)) {
744 if (shared_in_use) {
745 ret = SR_ERR;
746 goto err_free_parameters;
747 }
748 ret = configure_channel2_diode(sdi, range);
749 if (ret != SR_OK)
750 goto err_free_parameters;
751 } else if (!g_ascii_strncasecmp(param, "Aux", 3) ||
752 !g_ascii_strncasecmp(param, "LV", 2)) {
753 if (shared_in_use) {
754 ret = SR_ERR;
755 goto err_free_parameters;
756 }
757 ret = configure_channel2_auxv(sdi, range);
758 if (ret != SR_OK)
759 goto err_free_parameters;
760 } else if (!g_ascii_strncasecmp(param, "T", 1) ||
761 !g_ascii_strncasecmp(param, "K", 1)) {
762 ret = configure_channel2_temperature(sdi, range);
763 if (ret != SR_OK)
764 goto err_free_parameters;
765 } else if (!g_ascii_strncasecmp(param, "V", 1) ||
766 !param[0]) {
767 ret = configure_channel2_voltage(sdi, range);
768 if (ret != SR_OK)
769 goto err_free_parameters;
770 } else {
771 sr_info("Unrecognized mode for CH2: %s.", param);
772 ret = configure_channel2_voltage(sdi, range);
773 if (ret != SR_OK)
774 goto err_free_parameters;
775 }
776
777 if (parameters[1] && parameters[2]) {
778 param = parameters[2];
779 if (!g_ascii_strcasecmp(param, "RMS") ||
780 !g_ascii_strcasecmp(param, "AC")) {
781 ret = set_channel2_rms(sdi);
782 if (ret != SR_OK)
783 goto err_free_parameters;
784 } else if (!g_ascii_strcasecmp(param, "Buffer") ||
785 !g_ascii_strcasecmp(param, "Samples")) {
786 ret = set_channel2_buffer(sdi);
787 if (ret != SR_OK)
788 goto err_free_parameters;
789 } else {
790 ret = set_channel2_mean(sdi);
791 if (ret != SR_OK)
792 goto err_free_parameters;
793 }
794 }
795 }
796 g_strfreev(parameters);
797
798 g_strfreev(channel_config);
799 return SR_OK;
800
801err_free_parameters:
802 g_strfreev(parameters);
803err_free_channel_config:
804 g_strfreev(channel_config);
805 return ret;
806}
807
808static int dev_open(struct sr_dev_inst *sdi)
809{
810 int ret;
811
812 ret = mooshimeter_dmm_open(sdi);
813 if (ret != SR_OK)
814 return ret;
815
816 sdi->status = SR_ST_INACTIVE;
817
818 ret = mooshimeter_dmm_set_chooser(sdi, "SAMPLING:TRIGGER",
819 "SAMPLING:TRIGGER:OFF");
820 if (ret != SR_OK)
821 return ret;
822
823 ret = mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:RATE",
824 "SAMPLING:RATE", 125);
825 if (ret != SR_OK)
826 return ret;
827
828 ret = mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:DEPTH",
829 "SAMPLING:DEPTH", 64);
830 if (ret != SR_OK)
831 return ret;
832
833 /* Looks like these sometimes get set to 8, somehow? */
834 ret = mooshimeter_dmm_set_integer(sdi, "CH1:BUF_BPS", 24);
835 if (ret != SR_OK)
836 return ret;
837
838 ret = mooshimeter_dmm_set_integer(sdi, "CH2:BUF_BPS", 24);
839 if (ret != SR_OK)
840 return ret;
841
842 ret = configure_channel1_current(sdi, 0);
843 if (ret != SR_OK)
844 return ret;
845
846 ret = set_channel1_mean(sdi);
847 if (ret != SR_OK)
848 return ret;
849
850 ret = configure_channel2_voltage(sdi, 0);
851 if (ret != SR_OK)
852 return ret;
853
854 ret = set_channel2_mean(sdi);
855 if (ret != SR_OK)
856 return ret;
857
858 sdi->status = SR_ST_ACTIVE;
859
860 return SR_OK;
861}
862
863static int dev_close(struct sr_dev_inst *sdi)
864{
865 struct dev_context *devc = sdi->priv;
866
867 sdi->status = SR_ST_INACTIVE;
868
869 g_slist_free(devc->channel_meaning[0].channels);
870 devc->channel_meaning[0].channels = NULL;
871
872 g_slist_free(devc->channel_meaning[1].channels);
873 devc->channel_meaning[1].channels = NULL;
874
875 g_slist_free(devc->channel_meaning[2].channels);
876 devc->channel_meaning[2].channels = NULL;
877
878 return mooshimeter_dmm_close(sdi);
879}
880
881static int config_get(uint32_t key, GVariant **data,
882 const struct sr_dev_inst *sdi,
883 const struct sr_channel_group *cg)
884{
885 struct dev_context *devc = sdi->priv;
886 int ret;
887 float value;
888
889 (void)cg;
890
891 switch (key) {
892 case SR_CONF_SAMPLERATE:
893 ret = mooshimeter_dmm_get_chosen_number(sdi, "SAMPLING:RATE",
894 "SAMPLING:RATE", &value);
895 if (ret != SR_OK)
896 return ret;
897 *data = g_variant_new_uint64((guint64)value);
898 return SR_OK;
899 case SR_CONF_AVG_SAMPLES:
900 ret = mooshimeter_dmm_get_chosen_number(sdi, "SAMPLING:DEPTH",
901 "SAMPLING:DEPTH", &value);
902 if (ret != SR_OK)
903 return ret;
904 *data = g_variant_new_uint64((guint64)value);
905 return SR_OK;
906 case SR_CONF_CHANNEL_CONFIG:
907 return SR_ERR_NA;
908 default:
909 break;
910 }
911
912 return sr_sw_limits_config_get(&devc->limits, key, data);
913}
914
915static int config_set(uint32_t key, GVariant *data,
916 const struct sr_dev_inst *sdi,
917 const struct sr_channel_group *cg)
918{
919 struct dev_context *devc = sdi->priv;
920
921 (void)cg;
922
923 switch (key) {
924 case SR_CONF_SAMPLERATE:
925 return mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:RATE",
926 "SAMPLING:RATE", g_variant_get_uint64(data));
927 case SR_CONF_AVG_SAMPLES:
928 return mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:DEPTH",
929 "SAMPLING:DEPTH", g_variant_get_uint64(data));
930 case SR_CONF_CHANNEL_CONFIG:
931 return apply_channel_config(sdi, g_variant_get_string(data, NULL));
932 default:
933 break;
934 }
935
936 return sr_sw_limits_config_set(&devc->limits, key, data);
937}
938
939static int config_list(uint32_t key, GVariant **data,
940 const struct sr_dev_inst *sdi,
941 const struct sr_channel_group *cg)
942{
943 int ret;
944 float *values;
945 size_t count;
946 uint64_t *integers;
947
948 switch (key) {
949 case SR_CONF_SAMPLERATE:
950 ret = mooshimeter_dmm_get_available_number_choices(sdi,
951 "SAMPLING:RATE", &values, &count);
952 if (ret != SR_OK)
953 return ret;
954 integers = g_malloc(sizeof(uint64_t) * count);
955 for (size_t i = 0; i < count; i++)
956 integers[i] = (uint64_t)values[i];
957 g_free(values);
958 *data = std_gvar_samplerates(integers, count);
959 g_free(integers);
960 return SR_OK;
961 case SR_CONF_AVG_SAMPLES:
962 ret = mooshimeter_dmm_get_available_number_choices(sdi,
963 "SAMPLING:DEPTH", &values, &count);
964 if (ret != SR_OK)
965 return ret;
966 integers = g_malloc(sizeof(uint64_t) * count);
967 for (size_t i = 0; i < count; i++)
968 integers[i] = (uint64_t)values[i];
969 g_free(values);
970 *data = std_gvar_array_u64(integers, count);
971 g_free(integers);
972 return SR_OK;
973 case SR_CONF_CHANNEL_CONFIG:
974 return SR_ERR_NA;
975 default:
976 break;
977 }
978
979 return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
980}
981
982static int dev_acquisition_start(const struct sr_dev_inst *sdi)
983{
984 struct dev_context *devc = sdi->priv;
985 int ret;
986
987 ret = mooshimeter_dmm_set_chooser(sdi, "SAMPLING:TRIGGER",
988 "SAMPLING:TRIGGER:CONTINUOUS");
989 if (ret)
990 return ret;
991
992 sr_sw_limits_acquisition_start(&devc->limits);
993 std_session_send_df_header(sdi);
994
995 sr_session_source_add(sdi->session, -1, 0, 10000,
996 mooshimeter_dmm_heartbeat, (void *)sdi);
997
998 /* The Bluetooth socket isn't exposed, so just poll for data. */
999 sr_session_source_add(sdi->session, -2, 0, 50,
1000 mooshimeter_dmm_poll, (void *)sdi);
1001
1002 devc->enable_value_stream = TRUE;
1003
1004 return SR_OK;
1005}
1006
1007static int dev_acquisition_stop(struct sr_dev_inst *sdi)
1008{
1009 struct dev_context *devc = sdi->priv;
1010
1011 sr_session_source_remove(sdi->session, -1);
1012 sr_session_source_remove(sdi->session, -2);
1013 devc->enable_value_stream = FALSE;
1014
1015 mooshimeter_dmm_set_chooser(sdi, "SAMPLING:TRIGGER",
1016 "SAMPLING:TRIGGER:OFF");
1017
1018 return SR_OK;
1019}
1020
1021static struct sr_dev_driver mooshimeter_dmm_driver_info = {
1022 .name = "mooshimeter-dmm",
1023 .longname = "Mooshimeter DMM",
1024 .api_version = 1,
1025 .init = std_init,
1026 .cleanup = std_cleanup,
1027 .scan = scan,
1028 .dev_list = std_dev_list,
1029 .dev_clear = dev_clear,
1030 .config_get = config_get,
1031 .config_set = config_set,
1032 .config_list = config_list,
1033 .dev_open = dev_open,
1034 .dev_close = dev_close,
1035 .dev_acquisition_start = dev_acquisition_start,
1036 .dev_acquisition_stop = dev_acquisition_stop,
1037 .context = NULL,
1038};
1039SR_REGISTER_DEV_DRIVER(mooshimeter_dmm_driver_info);