CanOpenDeviceMonitor


The CANopen Device Monitor (CDM) is a powerful CanOpen tool.

Introduction

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.

Sample scripts provided by CDM users

Scripts can be loaded and executed opening the Console (Menue: View->Console), enter:

 
 (cdm) 1 % source script.tcl

Simple mask to change the heartbeat rate of a CanOpen device (CDM 3.x)

 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

my litte own main window (toplevel) (CDM 2.x, CDM 3.x)

 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

increment a value in the object dictionary every 500 ms by SDO (CDM 3.x)

 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

send a PDO to a device every 1000 ms (CDM 3.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

Doing something every n ms in 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]
 }

Disable after events

::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
 }

How much time do I need for ... ?

 # 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

Configuration and Reception of PDOs

 # 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
 

base64 strings

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="

porting CDM 2.x scripts to CDM 3.x

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 omited
That 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 PDOs, display the value in a scale widget

 # 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   

more buffered lines within the console

 tkcon buffer 100000

Edit CanOpenDeviceMonitor FrontPage PageList RecentChanges PageHistory