bbabble
changeset 12 5e67754f6457
parent 5 f41d45fe7ae9
equal deleted inserted replaced
11:c39e37d59638 12:5e67754f6457
     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
   384   				  POSIX::IEXTEN()));
   399   				  POSIX::IEXTEN()));
   385   $t->setcflag($t->getcflag() & ~(POSIX::CSIZE()|POSIX::PARENB())
   400   $t->setcflag($t->getcflag() & ~(POSIX::CSIZE()|POSIX::PARENB())
   386  			      | POSIX::CS8());
   401  			      | POSIX::CS8());
   387   $t->setcc(POSIX::VMIN,1); $t->setcc(POSIX::VTIME,0);
   402   $t->setcc(POSIX::VMIN,1); $t->setcc(POSIX::VTIME,0);
   388   set_speed($t,$id,$DEFAULT_SPEED[$id]);
   403   set_speed($t,$id,$DEFAULT_SPEED[$id]);
   389   if ($opt_s) {
   404   unless ($opt_s) {
   390     print($COLOR[$id]) unless ($opt_m);
   405     print($COLOR[$id]) unless ($opt_m);
   391     print("{TTY $id READY}");
   406     print("{TTY $id READY}");
   392   }
   407   }
   393 
   408 
   394   my(@buf,$rin);
   409   my(@buf,$rin);
   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