/
mosquitto_ar844.c
370 lines (326 loc) · 10.8 KB
/
mosquitto_ar844.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
// MQTT publisher of AR844 smart sensor sound level meter
// Uses mosquitto mqtt library, and libusb1.0
//
// Configuration values are in the code.
//
// published format tele/*hostname*/ar844/data
// { "time": "2019-12-29T13:45:00Z",
// "min": 13.2,
// "max": 72.4,
// "avg": 50.0,
// "weight": "A"
// }
//
// Based on...
// libusb test code - https://www.microchip.com/forums/m340898.aspx
// meter reverse engineering - http://www.brainworks.it/rpi-environmental-monitoring/reveng-the-usb-data
//
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <libusb-1.0/libusb.h>
#include <mosquitto.h>
// Config
const int meter_poll_period = 500; // ms - poll meter every 500ms
const int meter_accumulation_period = 1*60; // s - accumulate readings and publish every 60 seconds
const char *mqtt_broker_hostname = "server"; // broker host name
const int mqtt_broker_port = 1883; // broker port
const char *mqtt_topic = "tele/%s/ar844/data"; // publish topic - %s will contain hostname
#define MQQT_KEEPALIVE 90 // connection keep alive time (seconds)
#define WEIGHT_C (1<<4)
#define WEIGHT_A (0)
uint8_t ConfigWeight = WEIGHT_C;
#define SPEED_FAST (1<<6)
#define SPEED_SLOW (0)
uint8_t ConfigSpeed = SPEED_FAST;
#define RANGE_30_130 (0)
#define RANGE_30_80 (1)
#define RANGE_50_100 (2)
#define RANGE_60_110 (3)
#define RANGE_80_130 (4)
uint8_t ConfigRange = RANGE_30_130;
// AR844 VID and PID (dodgey?)
#define VENDOR_ID 0x1234
#define PRODUCT_ID 0x5678
// endpoint information (we could query this)
#define PACKET_INT_OUT_LEN 8
#define PACKET_INT_IN_LEN 8
const static int ENDPOINT_INT_IN=0x81; /* endpoint 0x81 address for IN */
const static int ENDPOINT_INT_OUT=0x02; /* endpoint 1 address for OUT */
const static int TIMEOUT=1000; /* timeout in ms */
static libusb_context *usbCtx;
static struct libusb_device_handle *devh = NULL;
static struct mosquitto *mqtt_client = NULL;
volatile int doExit = 0;
static char hostname[256];
void signal_handler(int n)
{
fprintf(stderr, "SIGINT received\n");
doExit = 1;
}
int timespec_subtract (struct timespec *x, struct timespec *y)
{
int sec_diff = (x->tv_sec - y->tv_sec) * 1000; // s to ms
int ns_diff = (x->tv_nsec - y->tv_nsec) / 1000000; // us to ms
return sec_diff + ns_diff;
}
static int init_mqtt()
{
int r;
r = mosquitto_lib_init();
mqtt_client = mosquitto_new(NULL, true, NULL );
if ( mqtt_client == NULL )
{
fprintf(stderr, "Failed to create mosquitto client %d", errno );
return -errno;
}
r = mosquitto_connect( mqtt_client, mqtt_broker_hostname, mqtt_broker_port, MQQT_KEEPALIVE );
if ( r != MOSQ_ERR_SUCCESS )
{
fprintf(stderr, "Failed to connect to host %s:%d %d", mqtt_broker_hostname, mqtt_broker_port, r );
return -r;
}
return 0;
}
static void publish_sample(const char * msg )
{
int r;
char topic[200];
snprintf( topic, sizeof(topic), mqtt_topic, hostname );
r = mosquitto_publish( mqtt_client, NULL, topic, strlen(msg), msg, 0, false );
if ( r == MOSQ_ERR_SUCCESS )
return;
fprintf(stderr,"mosquitto_publish returned %d\n", r );
fprintf(stderr,"Errno=%d\n", errno);
if ( r != 0 )
{
fprintf(stderr, "Attempting reconnect\n");
r = mosquitto_reconnect( mqtt_client );
fprintf(stderr,"mosquitto_reconnect returned %d\n", r );
fprintf(stderr,"Errno=%d\n", errno);
}
}
uint32_t dBSum = 0;
uint16_t dBMin = 0;
uint16_t dBMax = 0;
int sampleCount = 0;
time_t next_period;
static void get_next_period()
{
next_period = ((time(NULL) + meter_accumulation_period) / meter_accumulation_period ) * meter_accumulation_period;
//fprintf(stderr, "Next period = %d\n", next_period );
}
static void process_sample( uint16_t dB, bool fast, char weight, int range )
{
if ( sampleCount == 0 )
{
dBMin = dBMax = dBSum = dB;
}
else
{
dBSum += dB;
if ( dB < dBMin )
dBMin = dB;
else if ( dB > dBMax )
dBMax = dB;
}
sampleCount++;
if ( time(NULL) >= next_period )
{
uint32_t dBAvg = dBSum / sampleCount;
char timebuf[30], buf[200];
strftime(timebuf, sizeof(timebuf), "%FT%TZ", localtime(&next_period) );
snprintf(buf, sizeof(buf),
"{\"time\": \"%s\","
"\"avg\": %d.%d,"
"\"min\": %d.%d,"
"\"max\": %d.%d,"
"\"weight\": \"%c\""
"}",
timebuf,
dBAvg/10,dBAvg%10,
dBMin/10,dBMin%10,
dBMax/10,dBMax%10,
weight );
fprintf(stderr, "%s\n", buf);
publish_sample(buf);
get_next_period();
sampleCount = 0;
}
}
static int main_loop(void)
{
int r,i;
int transferred;
uint8_t answer[PACKET_INT_IN_LEN] = {0};
uint8_t question[PACKET_INT_OUT_LEN] = {0xB3,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // I don't think the content of the poll packet matters.
uint8_t program_meter[PACKET_INT_OUT_LEN] = {0x56,ConfigWeight | ConfigSpeed | ConfigRange,0x00,0x00,0x00,0x00,0x00,0x00}; // I don't think the content of the poll packet matters.
// set up a send and receive async transfer to run simultaneously.
struct libusb_transfer *send = libusb_alloc_transfer(0);
struct libusb_transfer *recv = libusb_alloc_transfer(0);
libusb_fill_interrupt_transfer( send, devh, ENDPOINT_INT_OUT, program_meter, PACKET_INT_OUT_LEN, NULL, NULL, 1000);
libusb_fill_interrupt_transfer( recv, devh, ENDPOINT_INT_IN, answer, PACKET_INT_IN_LEN, NULL, NULL, 1000);
recv->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
recv->status = -1;
r = libusb_submit_transfer( recv );
//fprintf(stderr,"recv status=%d\n", recv->status );
if ( r < 0 )
{
fprintf(stderr, "Failed to submit recv transfer %d\n", r );
return 0;
}
send->status = -1;
r = libusb_submit_transfer( send );
//fprintf(stderr, "send status=%d\n", send->status );
if ( r < 0 )
{
fprintf(stderr, "Failed to submit send transfer %d\n", r );
return 0;
}
struct timespec last;
clock_gettime( CLOCK_REALTIME, &last );
int count = 0;
while ( !doExit )
{
// fprintf(stderr, "handle events\n");
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
int completed = 0;
libusb_handle_events_timeout_completed( usbCtx, &tv, &completed );
// fprintf(stderr,"completed=%d\n", completed );
// fprintf(stderr,"recv status=%d\n", recv->status );
if ( recv->status >= 0 )
{
if ( recv->status == LIBUSB_TRANSFER_COMPLETED )
{
//if ( recv->actual_length > 0 )
//{
// for(i = 0;i < recv->actual_length; i++) {
// if(i%8 == 0)
// printf("\n");
// printf("%02x ",(uint8_t)answer[i]);
// }
// printf("\n");
//}
if ( recv->actual_length == PACKET_INT_IN_LEN &&
answer[0] < 10 ) // 200dB is x07D0 - if [0] is greater ignore (ack for config?)
{
uint16_t dB = (((uint16_t)answer[0]) << 8) | answer[1];
float soundLeveldB = (float)dB/10.0;
int measureSpeed = answer[2] >> 6;
int measureCurveType = (answer[2] >> 4) & 0x01;
int measureRange = answer[2] & 0x07;
//printf("%f %s %s %d\n", soundLeveldB, measureSpeed==1?"FAST":"SLOW",measureCurveType==0?"A":"C",measureRange);
process_sample( dB, measureSpeed, measureCurveType==0?'A':'C', measureRange );
}
}
else
{
//fprintf(stderr, "recv status error %d\n", recv->status );
}
recv->status = -1;
r = libusb_submit_transfer( recv );
//fprintf(stderr,"recv status=%d\n", recv->status );
}
//fprintf(stderr, "send status=%d\n", send->status );
if ( send->status >= 0 )
{
struct timespec now;
clock_gettime( CLOCK_REALTIME, &now );
int diff = timespec_subtract(&now, &last );
if ( diff >= 500 )
{
last = now;
send->status = -1;
send->buffer = question;
r = libusb_submit_transfer( send );
//fprintf(stderr, "send status=%d\n", send->status );
}
}
}
return 0;
}
static int init_usb()
{
int r;
devh = NULL;
r = libusb_init(&usbCtx);
if (r < 0)
{
fprintf(stderr, "Failed to initialise libusb\n");
return r;
}
//libusb_set_option(usbCtx, LIBUSB_OPTION_LOG_LEVEL , LIBUSB_LOG_LEVEL_DEBUG );
devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if ( devh == NULL )
{
r = -EIO;
fprintf(stderr, "Could not find/open Smart Sensor AR844\n");
goto out;
}
//printf("Successfully find the LVR Generic HID device\n");
libusb_set_auto_detach_kernel_driver(devh, 1);
#ifdef LINUX
libusb_detach_kernel_driver(devh, 0);
#endif
r = libusb_set_configuration(devh, 1);
if (r < 0)
{
fprintf(stderr, "libusb_set_configuration error %d\n", r);
//goto out;
}
//printf("Successfully set usb configuration 1\n");
//
r = libusb_claim_interface(devh, 0);
if (r < 0)
{
fprintf(stderr, "libusb_claim_interface error %d\n", r);
goto out;
}
//printf("Successfully claimed interface\n");
return 0;
out:
if ( devh != NULL )
{
libusb_release_interface(devh, 0);
libusb_reset_device(devh);
libusb_close(devh);
}
libusb_exit(NULL);
return r;
}
int main(void)
{
int r = 1;
fprintf(stderr, "mosquitto_ar844 starting\n");
signal(SIGINT, signal_handler);
r = init_usb();
if ( r < 0 )
{
fprintf(stderr, "Failed to initialise usb\n");
exit(1);
}
r = init_mqtt();
if ( r < 0 )
{
fprintf(stderr, "Failed to initialise mqtt\n");
goto out;
}
get_next_period();
gethostname( hostname, sizeof(hostname) );
main_loop();
out:
libusb_release_interface(devh, 0);
libusb_reset_device(devh);
libusb_close(devh);
libusb_exit(NULL);
mosquitto_disconnect(mqtt_client);
mosquitto_destroy(mqtt_client);
mosquitto_lib_cleanup();
return r >= 0 ? r : -r;
}