1 #!/usr/bin/perl |
1 #!/usr/bin/perl |
2 #====================================================================== |
2 #====================================================================== |
3 # B B A B B L E |
3 # B B A B B L E |
4 # doc: Thu Mar 11 01:00:51 2004 |
4 # doc: Thu Mar 11 01:00:51 2004 |
5 # dlm: Wed Dec 1 14:31:14 2010 |
5 # dlm: Fri Jul 3 10:12:51 2020 |
6 # (c) 2004 A.M. Thurnherr |
6 # (c) 2004 A.M. Thurnherr |
7 # uE-Info: 49 73 NIL 0 0 72 10 2 8 NIL ofnI |
7 # uE-Info: 58 0 NIL 0 0 72 10 2 8 NIL ofnI |
8 #====================================================================== |
8 #====================================================================== |
9 |
9 |
10 # Broad Band Babble --- talk to 1--2 RDI ADCPs |
10 # Broad Band Babble --- talk to 1--2 RDI ADCPs or other serial instruments |
11 |
11 |
12 # HISTORY: |
12 # HISTORY: |
13 # Mar 5, 2004: - written first, one-page-long proof-of-concept version |
13 # Mar 5, 2004: - written first, one-page-long proof-of-concept version |
14 # Mar 6, 2004: - added downloading |
14 # Mar 6, 2004: - added downloading |
15 # Mar 7, 2004: - made it portable (linux & MacOSX) |
15 # Mar 7, 2004: - made it portable (linux & MacOSX) |
45 # - changes to task syncronization |
45 # - changes to task syncronization |
46 # - replace unprintable chars by ? while in ECHO mode (only!) |
46 # - replace unprintable chars by ? while in ECHO mode (only!) |
47 # Aug 26, 2010: - added -y)modem receive |
47 # Aug 26, 2010: - added -y)modem receive |
48 # Oct 18, 2010: - added -u option to default ymodem receive call |
48 # Oct 18, 2010: - added -u option to default ymodem receive call |
49 # Dec 1, 2010: - BUG: -y with empty-string option argument did not work |
49 # Dec 1, 2010: - BUG: -y with empty-string option argument did not work |
|
50 # Jun 18, 2018: - explicitly implemented code to disable download on -y "" |
|
51 # - BUG: one debug message had -s wrong way round |
|
52 # - added state-change test required when Arduino closes USB connection |
|
53 # - BUG: non-existing ttys had stupid error messages |
|
54 # - made -s default and added -a to show async output, which is for debugging only |
|
55 # - changed default to use -m unless 2 devices are chosen |
|
56 # - begin implementing non-R)DI mode (currently, upload only) |
|
57 # Jul 3, 2020: - expunged master/slave terminology |
50 |
58 |
51 #---------------------------------------------------------------------- |
59 #---------------------------------------------------------------------- |
52 # USAGE |
60 # USAGE |
53 #---------------------------------------------------------------------- |
61 #---------------------------------------------------------------------- |
54 |
62 |
55 use Getopt::Std; |
63 use Getopt::Std; |
56 |
64 |
57 $USAGE = "Usage: $0 [-m)onochrome] [-s)uppress async output] " . |
65 $USAGE = "Usage: $0 [-m)onochrome] [-s)uppress async output(default)] [produce -a)sync ouput]" . |
58 "[-y)modem receive <cmd>] " . |
66 "[-y)modem receive <cmd[\"\" for none]>] [non-R)DI mode]" . |
59 "<tty0_device> [tty1_device]\n"; |
67 "<tty0_device> [tty1_device]\n"; |
60 |
68 |
61 die($USAGE) unless (getopts("msy:")); |
69 die($USAGE) unless (getopts("amRsy:")); |
|
70 $opt_s = 1 unless ($opt_a); |
|
71 $opt_m = 1 unless (@ARGV > 1); |
|
72 |
|
73 #============================== USER MANUAL ============================== |
62 |
74 |
63 # bbabble is started with 1 or 2 arguments, which are tty special files. |
75 # bbabble is started with 1 or 2 arguments, which are tty special files. |
64 # On LINUX, /dev/ttyS0 is com1: /dev/ttyS1 is com2: /dev/ttyUSB0 is the |
76 # On LINUX, /dev/ttyS0 is com1: /dev/ttyS1 is com2: /dev/ttyUSB0 is the |
65 # first USB tty port, /dev/ttyUSB1 is the 2nd, &c. If two ttys are |
77 # first USB tty port, /dev/ttyUSB1 is the 2nd, &c. If two ttys are |
66 # specified bbabble can talk to two instruments in parallel. Communication |
78 # specified bbabble can talk to two instruments in parallel. Communication |
67 # with The first (second) port is shown in red (blue). For consistency |
79 # with The first (second) port is shown in red (blue). For consistency |
68 # with the color scheme used by the LDEO expect scripts, the master |
80 # with the color scheme used by the LDEO expect scripts, the instrument |
69 # (downlooker) should be connected to the first tty given on the command |
81 # sending the sync pulses (downlooker) should be connected to the first |
70 # line. |
82 # tty given on the command line. When only one port is specified, the |
71 |
83 # -m)onochrome option is assumed. |
72 # On some (especially BSD-based) systems there are separate tty |
|
73 # device files for dialin and dialout operations. Only the latter work |
|
74 # with bbabble. They traditionally have names matching /dev/cu* (e.g. |
|
75 # /dev/cuad0 for the first serial # port in FreeBSD). |
|
76 |
|
77 # In order to have read/write access to the device files, the user |
|
78 # that is to run bbabble should be added to the group that owns |
|
79 # the tty device files (e.g. dialer on FreeBSD). |
|
80 |
|
81 # The -m option suppresses color ouput and -s suppresses the asynchronous |
|
82 # messages generated by bbabble and printed in curly braces. Both |
|
83 # options should be used if bbabble is run within expect(1). |
|
84 |
84 |
85 # Upon startup, bbabble prints a help message showing the current |
85 # Upon startup, bbabble prints a help message showing the current |
86 # "foreground" instrument as well as a list of legal keyboard commands. |
86 # "foreground" instrument as well as a list of legal keyboard commands. |
87 # These should be largely self explanatory. Initially, bbabble |
87 # These should be largely self explanatory. Initially, bbabble |
88 # is set up to talk to the instrument connected to the first tty. When |
88 # is set up to talk to the instrument connected to the first tty. When |
112 # asynchronous messages). |
112 # asynchronous messages). |
113 |
113 |
114 # On ^U the user is asked for a command-file-name to be uploaded to the |
114 # On ^U the user is asked for a command-file-name to be uploaded to the |
115 # instrument. The command file may contain any valid RDI command, empty |
115 # instrument. The command file may contain any valid RDI command, empty |
116 # lines, as well as comments beginning with a semicolon (;). |
116 # lines, as well as comments beginning with a semicolon (;). |
|
117 |
|
118 #====================== USAGE NOTES ============================== |
|
119 |
|
120 # On some (especially BSD-based) systems there are separate tty |
|
121 # device files for dialin and dialout operations. Only the latter work |
|
122 # with bbabble. They traditionally have names matching /dev/cu* (e.g. |
|
123 # /dev/cuad0 for the first serial # port in FreeBSD). |
|
124 |
|
125 # In order to have read/write access to the device files, the user |
|
126 # that is to run bbabble should be added to the group that owns |
|
127 # the tty device files (e.g. dialer on FreeBSD). |
117 |
128 |
118 # Upon startup, bbabble expects to communicate with the ADCPs at 9600bps |
129 # Upon startup, bbabble expects to communicate with the ADCPs at 9600bps |
119 # (baud). If the user sends a BREAK (^C) and garbage is produced the |
130 # (baud). If the user sends a BREAK (^C) and garbage is produced the |
120 # instrument's default baud rate is probably set to a different value. |
131 # instrument's default baud rate is probably set to a different value. |
121 # ^B cycles through all available instrument baud rates by first sending |
132 # ^B cycles through all available instrument baud rates by first sending |
245 # the prgram is always called `rb', even on systems where the executable |
256 # the prgram is always called `rb', even on systems where the executable |
246 # is `lrb'. |
257 # is `lrb'. |
247 # If -y is not given, bbabble trys to find one of the standard ymodem |
258 # If -y is not given, bbabble trys to find one of the standard ymodem |
248 # executables. Using -y allows options to be set. |
259 # executables. Using -y allows options to be set. |
249 |
260 |
250 if (length($opt_y) > 0) { |
261 if (defined($opt_y)) { |
251 $receive_ymodem = $opt_y; |
262 if (length($opt_y) > 0) { |
|
263 $receive_ymodem = $opt_y; |
|
264 } |
252 } else { |
265 } else { |
253 chomp($receive_ymodem = `which lrb 2>/dev/null`); |
266 chomp($receive_ymodem = `which lrb 2>/dev/null`); |
254 chomp($receive_ymodem = `which rb 2>/dev/null`) |
267 chomp($receive_ymodem = `which rb 2>/dev/null`) |
255 if ($receive_ymodem eq ''); |
268 if ($receive_ymodem eq ''); |
256 die("$0: cannot find rb or lrb\n") |
269 die("$0: cannot find rb or lrb\n") |
257 if ($receive_ymodem eq ''); |
270 if ($receive_ymodem eq ''); |
258 $receive_ymodem .= ' -u'; # keep upper-case filenames |
271 $receive_ymodem .= ' -u'; # keep upper-case filenames |
259 } |
272 } |
260 |
273 |
261 # When uploading command files, each command is sent after a prompt |
274 # When uploading command files, each command is sent after a prompt |
262 # is received from the instrument. The following variable defines the |
275 # is received from the instrument. The following variable defines the |
263 # prompt (as a perl regexpr). ANCHOR AT END ONLY!!! |
276 # prompt (as a perl regexpr). ANCHOR AT END ONLY!!! |
328 @COLOR = (`tput setaf $COLOR_TTY0` , |
341 @COLOR = (`tput setaf $COLOR_TTY0` , |
329 `tput setaf $COLOR_TTY1`); |
342 `tput setaf $COLOR_TTY1`); |
330 } |
343 } |
331 |
344 |
332 my(@sfd); # TTYs |
345 my(@sfd); # TTYs |
|
346 -c $TTY0 || die("$TTY0: no such file or directory\n"); |
333 open(TTY0,'+>',$TTY0) || die("$TTY0: $!\n"); |
347 open(TTY0,'+>',$TTY0) || die("$TTY0: $!\n"); |
334 $sfd[0] = fileno(TTY0); |
348 $sfd[0] = fileno(TTY0); |
335 if (defined($TTY1)) { |
349 if (defined($TTY1)) { |
336 select(undef,undef,undef,$naptime); # KEYSPAN 49W requires this |
350 select(undef,undef,undef,$naptime); # KEYSPAN 49W requires this |
|
351 -c $TTY1 || die("$TTY1: no such file or directory\n"); |
337 open(TTY1,'+>',$TTY1) || die("$TTY1: $!\n"); |
352 open(TTY1,'+>',$TTY1) || die("$TTY1: $!\n"); |
338 $sfd[1] = fileno(TTY1); |
353 $sfd[1] = fileno(TTY1); |
339 } |
354 } |
340 |
355 |
341 STDOUT->autoflush(1); # flushing |
356 STDOUT->autoflush(1); # flushing |
400 return unless ($rcv_state[$id]); # ... or state change |
415 return unless ($rcv_state[$id]); # ... or state change |
401 last unless ($rcv_state[$id] == $ECHO || |
416 last unless ($rcv_state[$id] == $ECHO || |
402 $rcv_state[$id] == $BUFFER); |
417 $rcv_state[$id] == $BUFFER); |
403 vec($rin,$sfd[$id],1) = 1; |
418 vec($rin,$sfd[$id],1) = 1; |
404 } |
419 } |
405 |
420 last unless ($rcv_state[$id] == $ECHO || # needed when Arduino USB interface is closed at other end |
|
421 $rcv_state[$id] == $BUFFER); |
|
422 |
406 #------------------------------ |
423 #------------------------------ |
407 # DOWNLOAD DATA FROM INSTRUMENT |
424 # DOWNLOAD DATA FROM INSTRUMENT |
408 #------------------------------ |
425 #------------------------------ |
409 |
426 |
410 if ($rcv_state[$id] == $DOWNLOAD) { # initiate download |
427 if ($rcv_state[$id] == $DOWNLOAD) { # initiate download |
805 |
822 |
806 elsif ($char == 21) { |
823 elsif ($char == 21) { |
807 if ($rcv_state[$cid]&$DOWNLOAD){ |
824 if ($rcv_state[$cid]&$DOWNLOAD){ |
808 print(STDERR "$COLOR[$cid]\{DOWNLOAD IN PROGRESS --- ^C TO ABORT}\n"); |
825 print(STDERR "$COLOR[$cid]\{DOWNLOAD IN PROGRESS --- ^C TO ABORT}\n"); |
809 } else { |
826 } else { |
810 $tmp = $rcv_state[$cid]&~$ECHO; $rcv_state[$cid] = $tmp|$BUFFER; |
827 unless ($opt_R) { |
|
828 $tmp = $rcv_state[$cid]&~$ECHO; $rcv_state[$cid] = $tmp|$BUFFER; |
|
829 } |
811 print($RESET); |
830 print($RESET); |
812 cookedmode(); |
831 cookedmode(); |
813 do { |
832 do { |
814 print("\nCommand File: "); chomp($_ = <STDIN>); |
833 print("\nCommand File: "); chomp($_ = <STDIN>); |
815 if ($_ eq '') { # no file name given |
834 if ($_ eq '') { # no file name given |
816 print("{upload canceled}\n"); |
835 print("{upload canceled}\n"); |
817 rawmode(); |
836 rawmode(); |
818 $tmp = $rcv_state[$cid]&~$BUFFER; $rcv_state[$cid] = $tmp|$FLUSH; |
837 unless ($opt_R) { |
819 wait_for_bit_cleared($cid,$FLUSH); |
838 $tmp = $rcv_state[$cid]&~$BUFFER; $rcv_state[$cid] = $tmp|$FLUSH; |
|
839 wait_for_bit_cleared($cid,$FLUSH); |
|
840 } |
820 next KEYSTROKE; |
841 next KEYSTROKE; |
821 } |
842 } |
822 unless (open(CF,$_)) { |
843 unless (open(CF,$_)) { |
823 print("$_: $!"); |
844 print("$_: $!"); |
824 redo; |
845 redo; |
825 } |
846 } |
826 } while (0); |
847 } while (0); |
827 rawmode(); |
848 rawmode(); |
828 $tmp = $rcv_state[$cid]&~$BUFFER; $rcv_state[$cid] = $tmp|$FLUSH; |
849 |
829 wait_for_bit_cleared($cid,$FLUSH); |
850 if ($opt_R) { # non-RDI mode: dump entire file contents raw |
830 |
851 my($buf,$nread); |
831 my($cmd) = next_cmd(CF); # read ahead (last cmd may ... |
852 while (($nread = read(CF,$buf,1)) > 0) { # nice small chunks |
832 my($next_cmd); # ... not generate prompt) |
853 POSIX::write($sfd[$cid],$buf,$nread); |
833 while (defined($next_cmd = next_cmd(CF))) { |
854 } |
834 $rcv_state[$cid] = $UPLOAD; |
855 } else { # RDI mode: parse command file and send command by command |
835 if ($cmd eq '<BREAK>') { |
856 $tmp = $rcv_state[$cid]&~$BUFFER; $rcv_state[$cid] = $tmp|$FLUSH; |
836 send_BREAK($cid); |
857 wait_for_bit_cleared($cid,$FLUSH); |
837 } else { |
858 my($cmd) = next_cmd(CF); # read ahead (last cmd may ... |
838 POSIX::write($sfd[$cid],"$cmd\r",length($cmd)+1); |
859 my($next_cmd); # ... not generate prompt) |
|
860 while (defined($next_cmd = next_cmd(CF))) { |
|
861 $rcv_state[$cid] = $UPLOAD; |
|
862 if ($cmd eq '<BREAK>') { |
|
863 send_BREAK($cid); |
|
864 } else { |
|
865 POSIX::write($sfd[$cid],"$cmd\r",length($cmd)+1); |
|
866 } |
|
867 $cmd = $next_cmd; |
|
868 wait_for_bit_set($cid,$ECHO); # NOT SURE!!! |
|
869 $tmp = $rcv_state[$cid]&~$ECHO; $rcv_state[$cid] = $tmp|$BUFFER; |
|
870 print("\n${RESET}{upload finished}\n"); |
|
871 $tmp = $rcv_state[$cid]&~$BUFFER; $rcv_state[$cid] = $tmp|$FLUSH; |
|
872 wait_for_bit_cleared($cid,$FLUSH); |
839 } |
873 } |
840 $cmd = $next_cmd; |
|
841 wait_for_bit_set($cid,$ECHO); # NOT SURE!!! |
|
842 } |
874 } |
843 close(CF); |
875 close(CF); |
844 POSIX::write($sfd[$cid],"$cmd\r",length($cmd)+1); # last cmd |
|
845 |
|
846 $tmp = $rcv_state[$cid]&~$ECHO; $rcv_state[$cid] = $tmp|$BUFFER; |
|
847 print("\n${RESET}{upload finished}\n"); |
|
848 $tmp = $rcv_state[$cid]&~$BUFFER; $rcv_state[$cid] = $tmp|$FLUSH; |
|
849 wait_for_bit_cleared($cid,$FLUSH); |
|
850 } |
876 } |
851 } |
877 } |
852 |
878 |
853 #-------------------------- |
879 #-------------------------- |
854 # HANDLE ^X (DOWNLOAD DATA) |
880 # HANDLE ^X (DOWNLOAD DATA) |
855 #-------------------------- |
881 #-------------------------- |
856 |
882 |
857 elsif ($char == 24) { |
883 elsif ($char == 24) { |
858 if ($rcv_state[$cid]&$DOWNLOAD){ |
884 if (defined($receive_ymodem)) { |
859 print(STDERR "$COLOR[$cid]\{DOWNLOAD IN PROGRESS --- ^C TO ABORT}\n"); |
885 if ($rcv_state[$cid]&$DOWNLOAD){ |
|
886 print(STDERR "$COLOR[$cid]\{DOWNLOAD IN PROGRESS --- ^C TO ABORT}\n"); |
|
887 } else { |
|
888 $rcv_state[$cid] = $SET_DOWNLOAD_SPEED; |
|
889 wait_for_bit_set($cid,$ECHO); |
|
890 $rcv_state[$cid] = $DOWNLOAD; # start waiting for instr. ready |
|
891 POSIX::write($sfd[$cid],"$START_DOWNLOAD_RDI_COMMAND\r", |
|
892 length($START_DOWNLOAD_RDI_COMMAND)+1); |
|
893 wait_for_bit_set($cid,$ECHO); |
|
894 } |
860 } else { |
895 } else { |
861 $rcv_state[$cid] = $SET_DOWNLOAD_SPEED; |
896 print(STDERR "$COLOR[$cid]\{NO YMODEM RECEIVER PROGRAM SPECIFIED}\n"); |
862 wait_for_bit_set($cid,$ECHO); |
|
863 $rcv_state[$cid] = $DOWNLOAD; # start waiting for instr. ready |
|
864 POSIX::write($sfd[$cid],"$START_DOWNLOAD_RDI_COMMAND\r", |
|
865 length($START_DOWNLOAD_RDI_COMMAND)+1); |
|
866 wait_for_bit_set($cid,$ECHO); |
|
867 } |
897 } |
868 } |
898 } |
869 |
899 |
870 #------------------------------------------------------------------ |
900 #------------------------------------------------------------------ |
871 # (FINALLY) HANDLE DEFAULT CASE: ECHO KEYBOARD INPUT TO SERIAL PORT |
901 # (FINALLY) HANDLE DEFAULT CASE: ECHO KEYBOARD INPUT TO SERIAL PORT |