The CANopen Device Monitor (CDM) is a powerful CanOpen tool.
It provides a high-level scripting API using the language TclTk 8.4. To get started with CDM programming, you should be familiar with TclTk.
Scripts can be loaded and executed opening the Console (Menue: View->Console), enter:
(cdm) 1 % source script.tcl
global frame set frame [::cdm::addTab Heartbeat] label $frame.label -text "Heartbeat interval (ms):" entry $frame.entry -width 10 button $frame.okButton -text "Change heartbeat interval" -command changeHB grid $frame.label -row 0 -column 0 grid $frame.entry -row 0 -column 1 grid $frame.okButton -row 2 -column 0 -columnspan 2 proc changeHB { } { global frame set val [$frame.entry get] w 0x1017 0 u16 $val } ## the CDM 2.x command for ::cdm::addTab is cdm_addTab? ## the w command in CDM 2.x does not require the 0x prefix for hex values
toplevel .mytoplevel wm title .mytoplevel "NMT Control Desk" wm geometry .mytoplevel "300x200" button .mytoplevel.b1 -text "Start" -command "start 0" button .mytoplevel.b2 -text "Enter Preop" -command "preop 0" button .mytoplevel.b3 -text "Stop" -command "stop 0" pack .mytoplevel.b1 .mytoplevel.b2 .mytoplevel.b3
global myVal set myVal 0 proc doIncrementJob { } { global myVal w 0x6200 1 u8 $myVal incr myVal if { $myVal >= 255 } { set myVal 0 } } # run doIncrementJob every 500 ms ::common::every doIncrementJob 500 ## common::every is not available in CDM 2.x
# at first the PDO must be configured # as it is a RPDO of the device, you have to use the command # set_rpdo set cobId 0x333 set transmissionType "event" set_rpdo local 1 $cobId $transmissionType 0x6200 1 0x6200 2 # now the PDO can be sent by wpdo set num 1 set length 2 set value1 0x00 set value2 0xaa wpdo $num $length $value1 $value2 # or wpdo 1 2 0x00 0x55 # to send this PDO every second # use the function ::common::every ::common::every "wpdo 1 2 0x00 0x55" 1000 ## common::every is not available in CDM 2.x ## the syntax of wpdo and set_rpdo is different from CDM 2.x
# the following script provides the every command for CDM 2.x users proc every { script interval } { eval $script after $interval [info level 0] }
::common::every use the after command. You can disable all after events (not only ::common::every events) by using:
foreach id [after info] { after cancel $id }
# Check the execution time of a script with time # Send a PDO 1000-times as fast as possible and see how long it takes time { ::pdo::wpdo 1 } 1000
# configure a PDO and show the received PDO # at first the PDO must be configured # As the device sends the PDO, it's a TPDO of the device # and must be configured with ::pdo::set_tpo? # ::pdo::set_tpdo <type> <number> <cobId> <transmissionType> # <1st mapped index> <sub> .. <nth mapped index> <sub> ::pdo::set_tpdo local 1 0x1a0 event 0x2010 0x0 # # After that, you need a Callback-Function that is called # every time the PDOs is received. # The following is a little example: proc myCallback { num length dataList } { # just put the data to the console puts "PDO $num received: $dataList" } # Now the Callback function has to be registered. #::pdo::setPDOIndication <num> <callback function> ::pdo::setPDOIndication 1 myCallback # Now myCallback is called on reception of the PDO 1 # Don't forget to start the network start 0 # If the PDO can be requested by RTR, just use ::pdo::rpdo 1 # to request it. # To change a configured PDO, it must be reset by disabling the first # bit of the PDO ::pdo::set_tpdo local 1 0x800001a0 event # now PDO 1 can be reconfigured
Some CDM commands require strings formated as base64. E.g. to send octet strings or domain data from a script. Convertion to base64 can easyly be done by the built-in tcl commands.
# convert a string to base64 set x [::base64::encode "I love CDM"] # even with non-printable characters set x [::base64::encode "\x01\x02\x03\x04"] # decode base64 ::base64::decode "d3d3LmNhbmV4cGVydHMuZGU="
The good news at first. The tcl commands haven't changed between these two CDM versions. But the following CDM commands have changed:
cdm_set_remote_id? -> ::cdm::setRemoteID cdm_load_eds? -> ::cdm::loadEds cdm_profilescan? -> ::cdm::profileScan cdm_banner? -> ::cdm::banner cdm_get_remote_id? -> ::cdm::getRemoteID cdm_userdialog? -> ::cdm::userDialog cdm_string_center? -> ::cdm::stringCenter cdm_addTab? -> ::cdm::addTab cdm_deleteTab? -> ::cdm::deleteTab cdm_string_fill? -> ::cdm::stringFill cdm_addTestTab? -> ::cdm::addTestTab cdm_puts_datetime? -> ::cdm::putsDateTime cdm_comment_input? -> ::cdm::commentInput cdm_procedure? -> ::cdm::setAction # the first :: can be omitedThat are not only the names that have changed, but the number and kind of the arguments, too.
Further the index argument of all SDO commands like r, w etc. have to be changed. Look at the following example:
# CDM 2.x syntax w 2000 0 u16 10 # CDM 3.x syntax w 0x2000 0 u16 10
Additionally a specific node ID can be addressed now by putting the node ID before the w or r command.
# read from node 10 10 r 0x1000 0 u32 # read from node 22 22 r 0x1000 0 u32
Reception of PDO:
# register your old cdm_pdo_ind? procedure # do this for all your pdo ::pdo::setPDOIndication 1 cdm_pdo_ind?
# using a scale-slider to visualize a PDO # # arrange a PDO, sent by a 401 device from 0x6401:1 # assume a node-id is selected already # the value of aa scale can be either set by # a procedure # scalePathName set value # or # assigning a variable to it and change the value of this variable # scale .scale1 -variable value set 401node 65 set scalevalue 0 ;# used to store the value to be displayed with the ;# scale widget set pdoUsed 3 # the PDO indication function proc gotValue {pdo count values} { global scalevalue #puts "got PDO $pdo, $count values, [lindex $values 0]" set scalevalue [lindex $values 0] # set secondvalue [lindex $values 1] } # open separate Tab set tab [::cdm::addTab "PDO input" end] # define a button to close the tab button $tab.close -text close -command { ::cdm::deleteTab $tab } pack $tab.close -side right # define a vertically scale scale $tab.scale -label "Analog Value 6400:1" -variable scalevalue \ -length 10c \ -from 32000 \ -to 0 pack $tab.scale # define the PDO to be received by the CANopen Server # ::pdo::set_tpdo <scope> <pdo_nr?> <cob> <trans> <index1> <sub1> <indexN> <subN> ::pdo::set_tpdo local $pdoUsed [expr 0x280 + $401node] event 0x6401 1 0x6401 2 # assign the PDO Indication function ::pdo::setPDOIndication $pdoUsed gotValue
tkcon buffer 100000