'''2008-06-01''' [SRIV] If you're interested in programming PLC's, then you can appreciate this driver. I wrote it so I could control the AutomationDirect DL05 PLC pictured below. [http://web4.automationdirect.com/images/overviews/DL05_left_300.jpg] More details about this PLC can be found here: http://web4.automationdirect.com/adc/Overview/Catalog/PLC_Hardware/DirectLogic_05 My plans now are to control this from my web server from a tcl cron script, which when it detects that my internet connectivity is lost, will turn off the outputs on the PLC to power cycle my 2 modems routers. I'm adding a DPDT Form-C relay to physically disconnect the coaxial cables to my modems as well. ################################################################################ # package name modbus.tcl # # Abstract: serial modbus rtu plc driver # # Author: Steve Redler IV 06-1-2008 # # # Cable wiring to AutomationDirect DL05 PLC # DB9 female 6P6C 6-pin phone plug - Port 2 on plc # 2 rxd ------------- 4 txd /--///--/| # 3 txd ------------- 3 rxd /__[#]__// # 5 gnd ------------- 1 gnd |123456|/ # -------- # 7 rts --\ jumper # 8 cts --/ source crc16.tcl package require crc16 proc modbussendmsg {message responsesize device timeoutms} { set fh [open $device "RDWR"] fconfigure $fh -translation binary -mode 9600,e,8,1 -buffering none -blocking 0 flush $fh; set junk [read $fh]; # clear buffer puts -nonewline $fh $message flush $fh set timeoutctr 0 set reply "" while {[string bytelength $reply] < $responsesize && $timeoutms >= $timeoutctr } { binary scan [read $fh] H* asciihex if {$asciihex != ""} { append reply $asciihex } after 5 incr timeoutctr 5 } if {$timeoutms < $timeoutctr} {set reply "timeout"} close $fh return $reply } proc modbuscommand {args} { if {[llength $args] == 7} { foreach {slaveaddr function startreg pointcount databytes device timeoutms} $args {} } else { foreach {slaveaddr function startreg pointcount device timeoutms} $args {} } switch $pointcount { on {set pointcount 65280} off {set pointcount 0} } append message [binary format c $slaveaddr] append message [binary format c $function] if {$startreg != {}} {append message [binary format S $startreg]} switch $function { 12 {if {$pointcount != {}} {append message [binary format S $pointcount]} } 6 {append message [binary format H* $pointcount]} 15 - 16 {append message [binary format S $pointcount] append message [binary format c [expr [string length $databytes] /2]] append message [binary format H* $databytes] } 22 {#not available in DL05/06 append message [binary format H* $pointcount] append message [binary format H* $databytes] } default {if {$pointcount != {}} {append message [binary format S $pointcount]} } } set checksum [::crc::crc16 -seed 0xFFFF $message] set checksum [binary format s $checksum] append message $checksum #binary scan $message H* msg ; puts "msg=$msg" switch $function { 1 - 2 {set responsesize [expr $pointcount / 4 +5 *2]} 3 {set responsesize [expr $pointcount * 4 +5 *2]} 4 {set responsesize [expr $pointcount * 4 +5 *2]} default {set responsesize 12} } set result [modbussendmsg $message $responsesize $device $timeoutms] if {$result == "timeout"} {return $result} if {[string index $result 2] <= 7} { if {$function <= 4} { set result [string range $result 6 end-4] } else { set result "ok" } } else { set result "error" } return $result } ############################################################# # test examples # ############################################################# set port /dev/ttyUSB0 set timeout 1000 #read discrete outputs 0x ref (returns 1 byte per 8 output bits) puts [modbuscommand 5 1 2048 8 $port $timeout] #read disrcete input status 1x ref (returns 1 byte per 8 input bits) puts [modbuscommand 5 2 2048 8 $port $timeout] #read holding registers 4x ref (returns 2 bytes per 1 16bit register) puts [modbuscommand 5 3 2100 8 $port $timeout] #read input register 3x ref (returns 2 bytes per 1 16bit register) puts [modbuscommand 5 4 2048 8 $port $timeout] #force single output coil on puts [modbuscommand 5 5 2049 on $port $timeout] #preset a single register 4x ref puts [modbuscommand 5 6 1 4444 $port $timeout] puts [modbuscommand 5 3 1 1 $port $timeout] #force multiple output coils on 8 puts [modbuscommand 5 15 2049 8 ff00 $port $timeout] #force multiple output coils off 8 puts [modbuscommand 5 15 2049 8 0000 $port $timeout] #preset multiple registers addr 2048 count 2 datavalues ff00aa33 puts [modbuscommand 5 16 2048 2 ff00aa33 $port $timeout] puts [modbuscommand 5 16 2100 4 1234567812345678 $port $timeout] Output of test commands: 00 08 12345678123456780000000000000000 ff00aa33000000000000000000000000 ok ok 4444 ok ok ok ok ---- [Scott Beasley] WOW! Makes me want to run out and buy some of those modules to play with some :) I did a lot of Process control and data collection back in the 80's-90's. Nothing like thoses back then. "Look Ma! No soldering iron!!!!" ---- source crc16.tcl package require crc16 ... where can we find the crc16.tcl source file ? tcllib ---- !!!!!! %| [Category Embedded] |% !!!!!!