initial version of BlinkenDeviceOutput
Stefan Schuermans

Stefan Schuermans commited on 2016-12-29 21:34:48
Showing 4 changed files, with 576 additions and 2 deletions.

... ...
@@ -1,6 +1,7 @@
1 1
 *.o
2 2
 .*.swp
3 3
 BlinkenConv
4
+BlinkenDeviceOutput
4 5
 BlinkenOutput
5 6
 BlinkenRecv
6 7
 BlinkenSend
... ...
@@ -0,0 +1,567 @@
1
+/* BlinkenLib
2
+   Copyright 2004-2014 Stefan Schuermans <stefan@schuermans.info>
3
+   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
4
+   a blinkenarea.org project */
5
+/* OS X support by Manfred Wuits
6
+   <manfred@werkzeugH.at> */
7
+
8
+#include <errno.h>
9
+#include <fcntl.h>
10
+#include <stdlib.h>
11
+#include <stdio.h>
12
+#include <stdlib.h>
13
+#include <string.h>
14
+#include <termios.h>
15
+#include <time.h>
16
+#include <unistd.h>
17
+#include <sys/time.h>
18
+#include <sys/types.h>
19
+
20
+#include <BlinkenLib/BlinkenLib.h>
21
+
22
+// get serial settings from text
23
+static int serial_settings_parse(char *str, int *settings, int *speed)
24
+{
25
+  int baud, data, stop;
26
+  char parity;
27
+  int set = 0, sp = 0;
28
+
29
+  // split and parse settings string
30
+  if (sscanf(str, "%i,%c,%i,%i", &baud, &parity, &data, &stop) != 4)
31
+    return 0;
32
+
33
+  // baud rate
34
+#define BAUD(rate) else if (baud == rate) sp = B ## rate;
35
+  if (0) {}
36
+  BAUD(300)
37
+  BAUD(600)
38
+  BAUD(1200)
39
+  BAUD(2400)
40
+  BAUD(4800)
41
+  BAUD(9600)
42
+  BAUD(19200)
43
+  BAUD(38400)
44
+  BAUD(57600)
45
+  BAUD(115200)
46
+#ifdef B230400
47
+  BAUD(230400)
48
+#endif
49
+#ifdef B460800
50
+  BAUD(460800)
51
+#endif
52
+#ifdef B576000
53
+  BAUD(576000)
54
+#endif
55
+#ifdef B921600
56
+  BAUD(921600)
57
+#endif
58
+#ifdef B1000000
59
+  BAUD(1000000)
60
+#endif
61
+#ifdef B1152000
62
+  BAUD(1152000)
63
+#endif
64
+#ifdef B1500000
65
+  BAUD(1500000)
66
+#endif
67
+#ifdef B2000000
68
+  BAUD(2000000)
69
+#endif
70
+#ifdef B2500000
71
+  BAUD(2500000)
72
+#endif
73
+#ifdef B3000000
74
+  BAUD(3000000)
75
+#endif
76
+#ifdef B3500000
77
+  BAUD(3500000)
78
+#endif
79
+#ifdef B4000000
80
+  BAUD(4000000)
81
+#endif
82
+  else {
83
+    printf("illegal baudrate: %d\n", baud);
84
+    return 0;
85
+  }
86
+
87
+  // parity
88
+  if (parity == 'n' || parity == 'N')
89
+    set |= 0;
90
+  else if (parity == 'e' || parity == 'E')
91
+    set |= PARENB;
92
+  else if (parity == 'o' || parity == 'O')
93
+    set |= PARENB | PARODD;
94
+  else {
95
+    printf("invalid parity: %c\n", parity);
96
+    return 0;
97
+  }
98
+
99
+  // data bits
100
+  if (data == 5)
101
+    set |= CS5;
102
+  else if (data == 6)
103
+    set |= CS6;
104
+  else if (data == 7)
105
+    set |= CS7;
106
+  else if (data == 8)
107
+    set |= CS8;
108
+  else {
109
+    printf("illegal number of data bits: %d\n", data);
110
+    return 0;
111
+  }
112
+
113
+  // stop bits
114
+  if (stop == 1)
115
+    set |= 0;
116
+  else if (stop == 2)
117
+    set |= CSTOPB;
118
+  else {
119
+    printf("illegal number of stop bits: %d\n", stop);
120
+    return 0;
121
+  }
122
+
123
+  // success
124
+  *settings = set;
125
+  *speed = sp;
126
+  return 1;
127
+}
128
+
129
+// convert serial settings to text
130
+static void serial_settings_to_str(int settings, char *buf,
131
+                                   unsigned int maxlen)
132
+{
133
+  int baud, data, stop;
134
+  char parity;
135
+
136
+  // baud rate
137
+  if (settings & B300)
138
+    baud = 300;
139
+  else if (settings & B600)
140
+    baud = 600;
141
+  else if (settings & B1200)
142
+    baud = 1200;
143
+  else if (settings & B2400)
144
+    baud = 2400;
145
+  else if (settings & B4800)
146
+    baud = 4800;
147
+  else if (settings & B9600)
148
+    baud = 9600;
149
+  else if (settings & B19200)
150
+    baud = 19200;
151
+  else if (settings & B38400)
152
+    baud = 38400;
153
+  else if (settings & B57600)
154
+    baud = 57600;
155
+  else if (settings & B115200)
156
+    baud = 115200;
157
+  else
158
+    baud = 0;
159
+
160
+  // parity
161
+  if (settings & PARENB)
162
+    if (settings & PARODD)
163
+      parity = 'O';
164
+    else
165
+      parity = 'E';
166
+  else
167
+    parity = 'N';
168
+
169
+  // data bits
170
+  if (settings & CS5)
171
+    data = 5;
172
+  else if (settings & CS6)
173
+    data = 6;
174
+  else if (settings & CS7)
175
+    data = 7;
176
+  else if (settings & CS8)
177
+    data = 8;
178
+  else
179
+    data = 0;
180
+
181
+  // stop bits
182
+  if (settings & CSTOPB)
183
+    stop = 2;
184
+  else
185
+    stop = 1;
186
+
187
+  snprintf(buf, maxlen, "%d,%c,%d,%d", baud, parity, data, stop);
188
+}
189
+
190
+// set serial settings for fd
191
+static int serial_settings_set(int fd, int settings, int speed)
192
+{
193
+  struct termios tio;
194
+
195
+  // set port settings
196
+  bzero(&tio, sizeof(tio));
197
+  tio.c_cflag = CLOCAL | HUPCL | CREAD | settings;
198
+  tio.c_iflag = IGNBRK | IGNPAR;
199
+  tio.c_oflag = 0;
200
+  tio.c_lflag = 0;
201
+  tio.c_cc[VTIME] = 10; // 1 sec timeout
202
+  tio.c_cc[VMIN] = 0;   // return on single char read
203
+
204
+#ifdef BLINKENLIB_CFG_OSX
205
+  if (cfsetspeed(&tio, speed) == -1) {
206
+    printf("cfsetspeed: error: %s\n", strerror(errno));
207
+    return 0;
208
+  }
209
+#else // #ifdef BLINKENLIB_CFG_OSX
210
+  tio.c_cflag |= speed;
211
+#endif // #ifdef BLINKENLIB_CFG_OSX
212
+
213
+  if (tcsetattr(fd, TCSANOW, &tio) == -1) {
214
+    printf("tcsetattr: error: %s\n", strerror(errno));
215
+    return 0;
216
+  }
217
+  // success
218
+  return 1;
219
+}
220
+
221
+// output movie
222
+// dev_fd may be -1 for not doing anything with device
223
+// returns error code (not for device-errors, 0 for success)
224
+static int out_movie(stBlinkenMovie *pMovie, int dev_fd,
225
+                     unsigned int format_change,
226
+                     unsigned int format_height, unsigned int format_width,
227
+                     unsigned int format_channels,
228
+                     unsigned int format_colors, etBlinkenProto proto)
229
+{
230
+  int frame_cnt, frame;
231
+  stBlinkenFrame *pFrame;
232
+  fd_set readFds, errFds;
233
+  char buffer[65536]; // 64kB is more than maximum UDP size
234
+  int maxFd, len, dev_eof, duration, borrow, ret;
235
+  struct timeval next, cur, timeout;
236
+
237
+  dev_eof = 0;
238
+  gettimeofday(&next, NULL);
239
+
240
+  frame_cnt = BlinkenMovieGetFrameCnt(pMovie);
241
+  for (frame = 0; frame < frame_cnt; ++frame) {
242
+    pFrame = BlinkenMovieGetFrame(pMovie, frame);
243
+    if (pFrame == NULL)
244
+      continue;
245
+    pFrame = BlinkenFrameClone(pFrame); // clone frame, will be changed
246
+    if (pFrame == NULL)
247
+      continue;
248
+    duration = BlinkenFrameGetDuration(pFrame);
249
+    next.tv_usec += (duration % 1000) * 1000;
250
+    next.tv_sec += duration / 1000;
251
+    if (next.tv_usec >= 1000000) {
252
+      next.tv_usec -= 1000000;
253
+      next.tv_sec += 1;
254
+    }
255
+
256
+    // change format
257
+    if (format_change)
258
+      BlinkenFrameResize(pFrame, format_height, format_width,
259
+                         format_channels, format_colors - 1);
260
+
261
+    // create output data from frame
262
+    len = BlinkenFrameToNetwork(pFrame, proto, buffer, sizeof(buffer));
263
+
264
+    // free frame
265
+    BlinkenFrameFree(pFrame);
266
+
267
+    // output data to device
268
+    if (dev_fd != -1 && len > 0) {
269
+      if (write(dev_fd, buffer, len) != len) {
270
+        printf("error writing to device\n");
271
+        break;
272
+      }
273
+    }
274
+
275
+    // check for data from device or error on device
276
+    do {
277
+
278
+      FD_ZERO(&readFds);
279
+      if (dev_fd != -1 && !dev_eof)
280
+        FD_SET(dev_fd, &readFds);
281
+      FD_ZERO(&errFds);
282
+      if (dev_fd != -1)
283
+        FD_SET(dev_fd, &errFds);
284
+      maxFd = 0;
285
+      if (dev_fd != -1 && dev_fd > maxFd)
286
+        maxFd = dev_fd;
287
+      gettimeofday(&cur, NULL);
288
+      if (next.tv_usec >= cur.tv_usec) {
289
+        timeout.tv_usec = next.tv_usec - cur.tv_usec;
290
+        borrow = 0;
291
+      } else {
292
+        borrow = 1;
293
+        timeout.tv_usec = 1000000 + next.tv_usec - cur.tv_usec;
294
+      }
295
+      if (next.tv_sec >= cur.tv_sec + borrow) {
296
+        timeout.tv_sec = next.tv_sec - (cur.tv_sec + borrow);
297
+      } else {
298
+        timeout.tv_usec = 0;
299
+        timeout.tv_sec = 0;
300
+      }
301
+      ret = select(maxFd + 1, &readFds, NULL, &errFds, &timeout);
302
+      if (ret < 0) { // error
303
+        printf("error during select: %s\n", strerror(errno));
304
+        BlinkenFrameFree(pFrame);
305
+        break;
306
+      }
307
+      // error on device
308
+      if (dev_fd != -1 && FD_ISSET(dev_fd, &errFds)) {
309
+        printf("error on device\n");
310
+        BlinkenFrameFree(pFrame);
311
+        break;
312
+      }
313
+
314
+      // received data from device
315
+      if (dev_fd != -1 && FD_ISSET(dev_fd, &readFds)) {
316
+        // read data
317
+        len = read(dev_fd, buffer, sizeof(buffer));
318
+        if (len < 0) {
319
+          printf("error reading from device\n");
320
+          break;
321
+        }
322
+        if (len == 0)
323
+          dev_eof = 1;
324
+      }
325
+
326
+    } while (ret != 0);
327
+
328
+  } // for frame
329
+
330
+  return 0;
331
+}
332
+
333
+// open device and output movie
334
+// returns error code (not for device-errors, 0 for success)
335
+static int open_and_output(stBlinkenMovie *pMovie, char *device,
336
+                           int serial_settings_change,
337
+                           int serial_settings, int serial_speed,
338
+                           unsigned int format_change,
339
+                           unsigned int format_height,
340
+                           unsigned int format_width,
341
+                           unsigned int format_channels,
342
+                           unsigned int format_colors, etBlinkenProto proto)
343
+{
344
+  int dev_fd, err;
345
+  char txt[64];
346
+
347
+  printf("outputting movie to \"%s\"...\n", device);
348
+
349
+  // open device
350
+  dev_fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
351
+  if (dev_fd == -1) {
352
+    printf("could not open \"%s\": error: %s\n", device, strerror(errno));
353
+    return 1;
354
+  }
355
+  // setup serial port
356
+  if (serial_settings_change) {
357
+    if (!serial_settings_set(dev_fd, serial_settings, serial_speed)) {
358
+      serial_settings_to_str(serial_settings, txt, sizeof(txt));
359
+      printf("could not set serial port to \"%s\"\n", txt);
360
+      close(dev_fd);
361
+      return 1;
362
+    }
363
+  }
364
+  // output movie to device
365
+  err = out_movie(pMovie, dev_fd,
366
+                  format_change, format_height, format_width,
367
+                  format_channels, format_colors, proto);
368
+
369
+  // close device
370
+  close(dev_fd);
371
+
372
+  printf("output to device finished...\n");
373
+
374
+  return err;
375
+}
376
+
377
+int main(int argCnt, char **args)
378
+{
379
+  int i, serial_settings, serial_speed;
380
+  char *device;
381
+  etBlinkenProto proto;
382
+  unsigned int format_change, format_height, format_width, format_channels,
383
+      format_colors;
384
+  unsigned int height, width, channels, colors;
385
+  unsigned int send_cnt, loop_cnt, loop, ui;
386
+  int serial_settings_change;
387
+  stBlinkenMovie *pMovie;
388
+
389
+  // print info
390
+  printf("BlinkenLib - BlinkenDeviceOutput\n"
391
+         "version %d.%d.%d\n"
392
+         "config " BLINKENLIB_CONFIG "\n"
393
+         "Copyright 2004-2014 Stefan Schuermans <stefan@schuermans.info>\n"
394
+         "Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html\n"
395
+         "a blinkenarea.org project\n\n",
396
+         BLINKENLIB_VERSION_MAJOR, BLINKENLIB_VERSION_MINOR,
397
+         BLINKENLIB_VERSION_REVISION);
398
+
399
+  // print syntax
400
+  if (argCnt <= 1) {
401
+#ifdef BLINKENLIB_CFG_MNG
402
+# define MNGEXT ", *.mng"
403
+#else
404
+# define MNGEXT
405
+#endif
406
+#ifdef BLINKENLIB_CFG_GIF
407
+# define GIFEXT ", *.gif"
408
+#else
409
+# define GIFEXT
410
+#endif
411
+    printf("syntax: %s <parameter> [...]\n\n"
412
+           "parameters:\n"
413
+           "  -p [BLP|EBLP|MCUF]\n"
414
+           "     protocol to output frames in (defaults to MCUF)\n"
415
+           "  -f <width>x<height>-<channels>/<colors>\n"
416
+           "     format to output frames in (defaults to no change)\n"
417
+           "  -d <device>\n"
418
+           "     device to output frames to (defaults to \"/dev/null\")\n"
419
+           "  -s <baud-rate>,<parity>,<data-bits>,<stop-bits>\n"
420
+           "     settings to use for serial devices (defaults to no change)\n"
421
+           "  -n <number>\n"
422
+           "     set number of times to send movies (defaults to 1)\n"
423
+           "  -i <file>\n"
424
+           "     read movie from file (*.blm, *.bmm, *.bml, *.bbm" MNGEXT GIFEXT ") and send it\n"
425
+           "  -l <number>\n"
426
+           "     loop number of times (defaults to 1, use 0 for forever)\n\n",
427
+           args[0]);
428
+    return 0;
429
+  }
430
+
431
+  // loop
432
+  loop_cnt = 1;
433
+  for (loop = 0; loop < loop_cnt || loop_cnt == 0; loop++) {
434
+
435
+    // print loop message
436
+    if (loop_cnt > 1)
437
+      printf("--- loop %u/%u ---\n", loop + 1, loop_cnt);
438
+    if (loop_cnt == 0)
439
+      printf("--- loop %u ---\n", loop + 1);
440
+
441
+    // process parameters
442
+    proto = BlinkenProtoMcuf;
443
+    format_change = 0;
444
+    format_height = 0;
445
+    format_width = 0;
446
+    format_channels = 0;
447
+    format_colors = 0;
448
+    device = "/dev/null";
449
+    serial_settings = 0;
450
+    serial_speed = 0;
451
+    serial_settings_change = 0;
452
+    send_cnt = 1;
453
+    for (i = 1; i < argCnt; i++) {
454
+
455
+      // protocol to output frames in
456
+      if (strcmp(args[i], "-p") == 0) {
457
+        if (i + 1 < argCnt) {
458
+          i++;
459
+          if (strcasecmp(args[i], "BLP") == 0)
460
+            proto = BlinkenProtoBlp;
461
+          else if (strcasecmp(args[i], "EBLP") == 0)
462
+            proto = BlinkenProtoEblp;
463
+          else if (strcasecmp(args[i], "MCUF") == 0)
464
+            proto = BlinkenProtoMcuf;
465
+          else
466
+            printf("unknown protocol \"%s\"\n", args[i]);
467
+        } else
468
+          printf("missing protocol for \"-p\"\n");
469
+      }
470
+      // format to output frames in
471
+      else if (strcmp(args[i], "-f") == 0) {
472
+        if (i + 1 < argCnt) {
473
+          i++;
474
+          if (sscanf
475
+              (args[i], "%ux%u-%u/%u", &width, &height, &channels, &colors) == 4
476
+              && width > 0 && width < 1000 && height > 0 && height < 1000
477
+              && channels > 0 && channels < 20 && colors > 1 && colors <= 256) {
478
+            format_change = 1;
479
+            format_height = height;
480
+            format_width = width;
481
+            format_channels = channels;
482
+            format_colors = colors;
483
+          } else
484
+            printf("invalid frame format \"%s\"\n", args[i]);
485
+        } else
486
+          printf("missing frame format for \"-r\"\n");
487
+      }
488
+      // device to output frames to
489
+      else if (strcmp(args[i], "-d") == 0) {
490
+        if (i + 1 < argCnt) {
491
+          i++;
492
+          device = args[i];
493
+        } else
494
+          printf("missing device name for \"-d\"\n");
495
+      }
496
+      // settings for serial output devices
497
+      else if (strcmp(args[i], "-s") == 0) {
498
+        if (i + 1 < argCnt) {
499
+          i++;
500
+          if (serial_settings_parse(args[i], &serial_settings, &serial_speed)) {
501
+            serial_settings_change = 1;
502
+          } else
503
+            printf("invalid serial settings \"%s\"\n", args[i]);
504
+        } else
505
+          printf("missing serial settings for \"-s\"\n");
506
+      }
507
+      // number of times to send movies
508
+      else if (strcmp(args[i], "-n") == 0) {
509
+        if (i + 1 < argCnt) {
510
+          i++;
511
+          if (sscanf(args[i], "%u", &ui) == 1 && ui > 0)
512
+            send_cnt = ui;
513
+          else
514
+            printf("invalid number \"%s\"\n", args[i]);
515
+        } else
516
+          printf("missing number for \"-n\"\n");
517
+      }
518
+      // read movie and send it
519
+      else if (strcmp(args[i], "-i") == 0) {
520
+        if (i + 1 < argCnt) {
521
+          i++;
522
+          pMovie = BlinkenMovieLoad(args[i]);
523
+          if (pMovie == NULL)
524
+            printf("could not read movie \"%s\"\n", args[i]);
525
+          else {
526
+            printf("movie \"%s\" read\n", args[i]);
527
+            for (ui = 0; ui < send_cnt; ui++) {
528
+              printf("outputting movie \"%s\" (%u/%u)...\n", args[i], ui + 1,
529
+                     send_cnt);
530
+              if (open_and_output(pMovie, device, serial_settings_change,
531
+                                  serial_settings, serial_speed,
532
+                                  format_change, format_height, format_width,
533
+                                  format_channels, format_colors, proto) == 0) {
534
+                printf("movie \"%s\" output successful\n", args[i]);
535
+              } else {
536
+                printf("movie \"%s\" output failed\n", args[i]);
537
+                break;
538
+              }
539
+            }
540
+            BlinkenMovieFree(pMovie);
541
+          }
542
+        } else
543
+          printf("missing input filename for \"-i\"\n");
544
+      }
545
+      // number of times to loop
546
+      else if (strcmp(args[i], "-l") == 0) {
547
+        if (i + 1 < argCnt) {
548
+          i++;
549
+          if (sscanf(args[i], "%u", &ui) == 1)
550
+            loop_cnt = ui;
551
+          else
552
+            printf("invalid number \"%s\"\n", args[i]);
553
+        } else
554
+          printf("missing number for \"-l\"\n");
555
+      }
556
+      // unknown parameter
557
+      else
558
+        printf
559
+            ("unknown parameter \"%s\", call without parameters to get help\n",
560
+             args[i]);
561
+
562
+    }   // for( i ...
563
+
564
+  }     // for( loop ...
565
+
566
+  return 0;
567
+}
... ...
@@ -41,7 +41,7 @@ LIB_OBJS:=BlinkenColorizer.o BlinkenFrame.o BlinkenMovie.o \
41 41
 
42 42
 .phony: all clean
43 43
 
44
-all: libBlinkenLib.a libBlinkenLib.$(SHLIBEXT).$(VERSION_MAJOR) libBlinkenLib.$(SHLIBEXT).$(VERSION) libBlinkenLib.$(SHLIBEXT) BlinkenConv BlinkenSend BlinkenRecv BlinkenOutput
44
+all: libBlinkenLib.a libBlinkenLib.$(SHLIBEXT).$(VERSION_MAJOR) libBlinkenLib.$(SHLIBEXT).$(VERSION) libBlinkenLib.$(SHLIBEXT) BlinkenConv BlinkenSend BlinkenRecv BlinkenOutput BlinkenDeviceOutput
45 45
 
46 46
 config.h: ../config/config.h
47 47
 	cp $< $@
... ...
@@ -106,6 +106,12 @@ BlinkenOutput.o: BlinkenOutput.c BlinkenLib.h config.h BlinkenColorizer.h Blinke
106 106
 BlinkenOutput: BlinkenOutput.o libBlinkenLib.$(SHLIBEXT)
107 107
 	$(CC) $(BIN_LFLAGS) -o $@ $< -lBlinkenLib
108 108
 
109
+BlinkenDeviceOutput.o: BlinkenDeviceOutput.c BlinkenLib.h config.h BlinkenColorizer.h BlinkenProto.h BlinkenFrame.h BlinkenMovie.h
110
+	$(CC) $(CFLAGS) -c -o $@ $<
111
+
112
+BlinkenDeviceOutput: BlinkenDeviceOutput.o libBlinkenLib.$(SHLIBEXT)
113
+	$(CC) $(BIN_LFLAGS) -o $@ $< -lBlinkenLib
114
+
109 115
 clean:
110 116
 	rm -f BlinkenConv BlinkenSend BlinkenRecv BlinkenOutput libBlinkenLib.$(SHLIBEXT)* libBlinkenLib.a *.o config.h
111 117
 
... ...
@@ -1,5 +1,5 @@
1 1
 VERSION_MAJOR:=0
2 2
 VERSION_MINOR:=7
3
-VERSION_REVISION:=8
3
+VERSION_REVISION:=9
4 4
 VERSION:=$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_REVISION)
5 5
 
6 6