CanOpenDeviceMonitorThe 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