Jump to content

SENSBLUE ATLAS/Datasheet: Difference between revisions

No edit summary
Tags: Manual revert Visual edit
 
(37 intermediate revisions by the same user not shown)
Line 1: Line 1:
[[File:SENSBLUE-ATLAS-Gateway.png|center|frameless|360x360px]]
{{DISPLAYTITLE:Datasheet}}
 


<div style="margin: 15px 0;">
<div class="buy-now-container">
<span class="atlas-btn">[[SENSBLUE ATLAS/Technical Resources|Technical Resources]]</span>
<span class="button-buy-now">[https://atronia.eu/product/sensblue-atlas/ Buy Now]</span>
</div>
</div>
[[File:SENSBLUE-ATLAS-Gateway.png|center|frameless|360x360px]]


== '''Datasheet''' ==
== '''Datasheet''' ==
Line 9: Line 12:
=== 1. Table of SENSBLUE ATLAS Reference ID Model Number ===
=== 1. Table of SENSBLUE ATLAS Reference ID Model Number ===
{| class="wikitable"
{| class="wikitable"
|
|'''Reference Number'''
|
|'''Name'''
|
|'''Version'''
|
|'''RAM'''
|
|'''Modem'''
|
|-
|PA000000102
|ATLAS IoT SBC V1 CM4. 2GB M2.0 M1 PB0
|V1
|2GB
|Yes
|-
|-
|Paxxxxxxxx
|PA000000103
|
|ATLAS IoT SBC V1 CM4. 2GB M2.0 M0 PB0
|
|V1
|
|2GB
|
|Yes
|
|-
|-
|PAxxxxxxxx
|PA000000104
|
|ATLAS IoT SBC V1 CM4. 4GB M2.0 M1 PB0
|
|V1
|
|4GB
|
|No
|
|}
|}


Line 82: Line 88:
|-
|-
|RAM
|RAM
|4GB  LPDDR4
|2GB, 4GB  LPDDR4
|-
|-
|Storage
|Storage
Line 670: Line 676:
   "task": {
   "task": {
     "taskParams": {
     "taskParams": {
       "D01": {
       "DO1": {
         "state": "open"
         "state": "open"
       },
       },
       "D02": {
       "DO2": {
         "state": "open"
         "state": "open"
       }
       }
Line 686: Line 692:
   "task": {
   "task": {
     "taskResult": {
     "taskResult": {
       "D01": {
       "DO1": {
         "success": true,
         "success": true,
         "state": "open"
         "state": "open"
       },
       },
       "D02": {
       "DO2": {
         "success": true,
         "success": true,
         "state": "open"
         "state": "open"
Line 700: Line 706:
====== b) Get all relays state ======
====== b) Get all relays state ======


Req-Topic: relayOutputs/runtime/in<syntaxhighlight lang="json" line="1">
Req-Topic: relayOutputs/runtime/in<syntaxhighlight lang="json" line="1">{
{
   "id": "<TIMESTAMP>",
   "id": "<TIMESTAMP>",
   "origin": "APP",
   "origin": "APP",
Line 707: Line 712:
     "action": "READ"
     "action": "READ"
   }
   }
}
}</syntaxhighlight>Reply-Topic: relayOutputs/runtime/out <syntaxhighlight lang="json" line="1">
</syntaxhighlight>Reply-Topic: relayOutputs/runtime/out <syntaxhighlight lang="json" line="1">
{
{
   "id": "<TIMESTAMP>",
   "id": "<TIMESTAMP>",
Line 714: Line 718:
   "task": {
   "task": {
     "taskResult": {
     "taskResult": {
       "D01": {
       "DO1": {
         "success": true,
         "success": true,
         "state": "open"
         "state": "open"
       },
       },
       "D02": {
       "DO2": {
         "success": true,
         "success": true,
         "state": "close"
         "state": "close"
Line 736: Line 740:
     "action": "SET",
     "action": "SET",
     "taskParams": {
     "taskParams": {
       "D01": {
       "DO1": {
         "state": "open"
         "state": "open"
       },
       },
       "D02": {
       "DO2": {
         "state": "close"
         "state": "close"
       }
       }
Line 751: Line 755:
   "task": {
   "task": {
     "taskResult": {
     "taskResult": {
       "D01": {
       "DO1": {
         "success": true,
         "success": true,
         "state": "open"
         "state": "open"
       },
       },
       "D02": {
       "DO2": {
         "success": true,
         "success": true,
         "state": "close"
         "state": "close"
Line 3,772: Line 3,776:
   "task": {
   "task": {
     "taskResult": {
     "taskResult": {
       "D01": {
       "DO1": {
         "success": true,
         "success": true,
         "state": "low"
         "state": "low"
       },
       },
       "D02": {
       "DO2": {
         "success": true,
         "success": true,
         "state": "high"
         "state": "high"
       },
       },
       "D03": {
       "DO3": {
         "success": true,
         "success": true,
         "state": "high"
         "state": "high"
       },
       },
       "D04": {
       "DO4": {
         "success": true,
         "success": true,
         "state": "high"
         "state": "high"
       },
       },
       "D05": {
       "DO5": {
         "success": true,
         "success": true,
         "state": "high"
         "state": "high"
       },
       },
       "D06": {
       "DO6": {
         "success": true,
         "success": true,
         "state": "low"
         "state": "low"
       },
       },
       "D07": {
       "DO7": {
         "success": true,
         "success": true,
         "state": "low"
         "state": "low"
       },
       },
       "D08": {
       "DO8": {
         "success": true,
         "success": true,
         "state": "low"
         "state": "low"
Line 4,198: Line 4,202:
}
}
</syntaxhighlight>'''Buzzer State'''
</syntaxhighlight>'''Buzzer State'''
[[File:5 beeps.svg|left|frameless|700x700px]]
[[File:5_beeps.svg|frameless|700x700px]]
 
 
 
 
 
duration_s = 1.0s
duration_s = 1.0s


Line 4,227: Line 4,226:
</syntaxhighlight>'''Buzzer State'''
</syntaxhighlight>'''Buzzer State'''
[[File:Infinite_Beeps.svg|frameless|606x606px]]
[[File:Infinite_Beeps.svg|frameless|606x606px]]
duration_s = 1.0
duration_s = 1.0
gap_s = 0.5
gap_s = 0.5
Line 5,040: Line 5,040:
'''Req-Topic''': modem/runtime/in<syntaxhighlight lang="json" line="1">
'''Req-Topic''': modem/runtime/in<syntaxhighlight lang="json" line="1">
{
{
  "id": "<TIMESTAMP>",
"id": "<TIMESTAMP>",
  "origin": "APP",
"origin": "APP",
  "task": {
"task": {
    "action": "TEST"
  "action": "TEST"
   }
   }
}
}
Line 5,556: Line 5,556:
===== 24.13.16 Notes: =====
===== 24.13.16 Notes: =====


* If no modem is detected:
* If no modem is detected: '''Reply-Topic''': modem/runtime/out
 
<syntaxhighlight lang="json" line="1">
'''Reply-Topic''': modem/runtime/out<syntaxhighlight lang="json" line="1">{
{
   "id": "<TIMESTAMP>",
   "id": "<TIMESTAMP>",
   "origin": "APP",
   "origin": "APP",
Line 5,568: Line 5,568:
     }
     }
   }
   }
}</syntaxhighlight>
}
</syntaxhighlight>
 
* If SIM is locked or missing: '''Reply-Topic''': modem/runtime/out
* If SIM is locked or missing: '''Reply-Topic''': modem/runtime/out
<syntaxhighlight lang="json" line="1">
<syntaxhighlight lang="json" line="1">
Line 5,595: Line 5,597:
* Signal strength is reported as percentage and may be cached by ModemActor.
* Signal strength is reported as percentage and may be cached by ModemActor.


=== 25. Included Software Stack ===
==== <big>24.14 CANopen Actor</big> ====
 
===== 24.14.1 App Interfacing =====
User App interface and functionalities exposed to the open part of the MQTT broker.
 
The CANopen Actor provides a generic protocol interface to CANopen devices.
 
The actor does NOT interpret device-specific meaning (Object Dictionary semantics).
 
It only handles communication (HOW to talk).
 
===== 24.14.2 Configuration =====


====== '''Configure CANopen Nodes''' ======
'''Req-Topic''': canopenRM8007/config/in


Stores CANopen defaults and logical node configuration inside the actor.<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "APP",
  "task": {
    "alias": "CONFIG_CANOPEN",
    "taskParams": {
      "defaults": {
        "sdo_timeout": "<SDO_TIMEOUT>"
      },
      "nodes": {
        "<NODE_NAME>": {
          "node_id": "<NODE_ID>",
          "steps_per_rev": "<STEPS_PER_REV>"
        }
      }
    }
  }
}
</syntaxhighlight>'''Reply-Topic''': canopenRM8007/config/out<syntaxhighlight lang="json">
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "CONFIG_CANOPEN",
    "taskResult": {
      "defaults": {
        "success": true
      },
      "motor_1": {
        "success": true
      }
    }
  }
}
</syntaxhighlight>'''Request Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Example'''
|'''Description'''
|-
|id
| string
|Yes
|"1777041089180"
|Unique request identifier. The same value isreturned in the response, allowing the applicationto match requests and responses.
|-
|origin
| string
|No
|"APP"
|Identifies who sent the request. Common values canbe
'''APP''', '''NodeRED''', '''CLI''', or '''TestClient'''.
|-
|task
|object
|Yes
|{...}
|Main command object containing the CANopencommand alias and parameters.
|-
|task.alias
|string
|Yes
|"CONFIG_CANOPEN"
|Defines the command to execute. For this section,the alias must be
'''CONFIG_CANOPEN'''.
|-
|task.taskParams
|object
|Yes
|{...}
|Contains the configuration data to store inside theCANopen actor.
|-
|task.taskParams.defaults
|object
|No
|{ "sdo_timeout":2.0 }
|Defines default CANopen settings used by the actorwhen no command-specific value is provided.
|-
|task.taskParams.defaults.sdo_timeout
|number
|No
|2.0
|Default timeout, in seconds, used for SDOoperations. For example, '''2.0'''
means the actor waitsup to 2 seconds for an SDO response.
|-
|task.taskParams.nodes
|object
|Yes
|<nowiki>{ "motor_1": {...}}</nowiki>
|List of logical CANopen nodes known by the actor.Each key is a user-defined node name.
|-
|task.taskParams.nodes.<NODE_NAME>
|object
|Yes
|"motor_1"
|Logical name used to reference a node later withoutrepeatedly sending its
'''node_id'''.
|-
|task.taskParams.nodes.<NODE_NAME>.node_id
|integer
|Yes
|5
|CANopen node ID on the CAN bus. Valid CANopennode IDs are normally in the range
'''1..127'''.
|-
|task.taskParams.nodes.<NODE_NAME>.steps_per_rev
|integer
|Recommended
|4096
|Number of raw position counts corresponding toone full mechanical revolution, 360°. The actor usesthis later to convert raw position into revolutions and angle_deg.
|}
'''Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| string
|"1777041089180"
|Same request identifier received in the request. Used to correlate the
response with the original command.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the response. For successful
handler replies, this is canopen-actor.
|-
|task
|object
|{...}
|Main response object containing the executed alias and the command
result.
|-
|task.alias
|string
|"CONFIG_CANOPEN"
|Alias of the command that was executed.
|-
|task.taskResult
|object
|{...}
|Result object containing the configuration outcome.
|-
|task.taskResult.defaults.success
|boolean
|true
|Indicates that the '''defaults''' configuration was successfully stored. This
field is present only if '''defaults''' was included in the request.
|-
|task.taskResult.<NODE_NAME>.success
|boolean
|true
|Indicates that the logical node configuration was successfully stored.
Example: '''taskResult.motor_1.success = true'''.
|-
|task.taskResult.success
|boolean
|true
|Generic success field. This appears when no specific '''defaults''' or '''nodes'''
result is generated.
|-
|task.taskResult.error
|string
|"unknown alias
CONFIG_X"
|Error description. This field is present only when the command fails.
|}
'''CONFIG_CANOPEN''' does not communicate with the CANopen device directly. It stores logical node configuration inside the actor so that later runtime
commands can refer to nodes by name instead of always sending the numeric '''node_id'''.
====== Configure one CANopen Transmit PDO ======
Configures a TPDO to transmit position data.
The handler validates '''pdo_num''' as '''1..4''', defaults '''map_index''' to '''0x6004''', '''map_subindex''' to '''0x00''', '''map_bits''' to '''32''', '''transmission_type''' to '''255''' , '''event_timer_ms'''
to '''20''', and '''inhibit_time_100us''' to '''0'''.
'''Req-Topic''': canopenRM8007/config/in<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "APP",
  "task": {
    "alias": "CONFIG_POSITION_TPDO",
    "taskParams": {
      "node_id": 5,
      "pdo_num": 1,
      "map_index": "0x6004",
      "map_subindex": "0x00",
      "map_bits": 32,
      "transmission_type": 255,
      "event_timer_ms": 2,
      "inhibit_time_100us": 20,
      "offset": 0,
      "size": 4,
      "signed": false,
      "steps_per_rev": 4096,
      "timeout": 2.0
    }
  }
}
</syntaxhighlight>'''Reply-Topic''': canopenRM8007/config/out<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "CONFIG_POSITION_TPDO",
    "taskResult": {
      "success": true,
      "node_id": 5,
      "pdo_num": 1,
      "comm_index": 6144,
      "map_index": 6656,
      "mapped_object": "0x6004:00/32",
      "transmission_type": 255,
      "event_timer_ms": 2,
      "inhibit_time_100us": 20,
      "cob_id": 389
    }
  }
}
</syntaxhighlight>
====== Parameter description ======
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Default'''
|'''Description'''
|-
|node_id
| int
|Yes*
|—
|CANopen node ID.
|-
|node
| string
|Yes*
|—
|Logical node name configured previously with '''CONFIG_CANOPEN'''.
|-
|pdo_num
|int
|No
|1
|TPDO number. Valid range: '''1..4'''.
|-
|map_index
|int/string
|No
|0x6004
|Object dictionary index to map. Hex strings such as "'''0x6004'''" are
supported.
|-
|map_subindex
|int/string
|No
|0x00
|Object dictionary subindex.
|-
|map_bits
|int
|No
|32
|Number of mapped bits.
|-
|transmission_type
|int
|No
|255
|CANopen TPDO transmission type.
|-
|event_timer_ms
|int
|No
|20
|TPDO event timer in milliseconds.
|-
|inhibit_time_100us
|int
|No
|0
|Inhibit time in 100 μs units.
|-
|offset
|int
|No
|0
|Byte offset used later when decoding PDO position.
|-
|size
|int
|No
|4
|Number of bytes used later when decoding PDO position.
|-
|signed
|bool
|No
|false
|Whether decoded raw position should be interpreted as signed.
|-
|steps_per_rev
|int
|Recommended
|'''4096''' if saved into node
config
|Encoder steps per revolution.
|-
|timeout
|float
|No
|actor default
|SDO/NMT operation timeout.
|}
===== 24.14.3 Runtime Operations =====
Supported runtime aliases include <small>'''READ, READ_CONTINUOUS, STOP_READ_CONTINUOUS, READ_PDO_CONTINUOUS, STOP_READ_PDO_CONTINUOUS, PING, NMT_COMMAND, SDO_READ, SDO_WRITE, READ_HEARTBEAT, RECV_PDO, RECV_EMCY'''</small>, and <small>'''QUERY_NODE_INFO'''</small><small>'''.'''</small>
====== NMT Control ======
'''Req-Topic''': canopenRM8007/runtime/in<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "APP",
  "task": {
    "alias": "CONFIG_CANOPEN",
    "taskParams": {
      "defaults": {
        "sdo_timeout": "<SDO_TIMEOUT>"
      },
      "nodes": {
        "<NODE_NAME>": {
          "node_id": "<NODE_ID>",
          "steps_per_rev": "<STEPS_PER_REV>"
        }
      }
    }
  }
}
</syntaxhighlight>'''Reply-Topic''': canopenRM8007/runtime/out<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "NMT_COMMAND",
    "taskResult": {
      "success": true,
      "id": "nmt-start-node5",
      "node_id": 5,
      "command": "START",
      "confirmed": true,
      "state": null
    }
  }
}
</syntaxhighlight>
* '''Supported NMT Commands'''
{| class="wikitable"
|'''Command'''
|'''Meaning'''
|'''Notes'''
|-
|START
| Puts the node into Operational state.
|If '''wait''' = '''true''', the actor waits for heartbeat state '''OPERATIONAL'''.
|-
|STOP
| Puts the node into Stopped state.
|If '''wait''' = '''true''', the actor waits for heartbeat state '''STOPPED'''.
|-
|PRE_OP
|Puts the node into Pre-Operational state.
|Used before operations such as PDO remapping.
|-
|PRE-OP
|Same as '''PRE_OP'''.
|Alternative accepted spelling.
|-
|PREOP
|Same as '''PRE_OP'''.
|Alternative accepted spelling.
|-
|PRE_OPERATIONAL
|Same as '''PRE_OP'''.
|Alternative accepted spelling.
|-
|RESET_NODE
|Resets the CANopen node application.
|If '''wait''' = '''true''', the actor waits for boot-up heartbeat.
|-
|RESET
|Same as '''RESET_NODE'''.
|Alternative accepted spelling.
|-
|RESET_COMM
|Resets CANopen communication.
|Sends NMT reset communication command.
|}
* '''Request Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Example'''
|'''Description'''
|-
|id
| string
|Yes
|"<TIMESTAMP>"
|Unique request identifier. The same value is returned in the response.
|-
|origin
| string
|No
|"APP"
|Identifies the sender of the request, for example '''APP''' , '''NodeRED''' , '''CLI''' , or
'''TestClient'''.
|-
|task
|object
|Yes
|{...}
|Main command object containing the command alias and parameters.
|-
|task.alias
|string
|Yes
|"NMT_COMMAND"
|Defines the command to execute. For this operation, it must be
'''NMT_COMMAND'''.
|-
|task.taskParams
|object
|Yes
|{...}
|Contains the NMT command parameters.
|-
|task.taskParams.node_id
|integer
|Yes
|5
|Numeric CANopen node ID that will receive the NMT command.
|-
|task.taskParams.nodeId
|integer
|Yes
|5
|Alternative spelling for '''node_id'''.
|-
|task.taskParams.node
|string
|Yes
|"motor_1"
|Logical node name previously configured with '''CONFIG_CANOPEN'''.
|-
|task.taskParams.command
|string
|No
|"START"
|NMT command to send. If omitted, the actor defaults to '''START'''.
|-
|task.taskParams.wait
|boolean
|No
|false
|If '''true''', the actor waits for heartbeat/state confirmation when supported. If
'''false''', the command is sent without waiting for confirmation.
|-
|task.taskParams.timeout
|number
|No
|5.0
|Timeout, in seconds, used when waiting for NMT confirmation.
Default is '''5.0'''.
|-
|task.taskParams.sdo_timeout
|number
|No
|2.0
|Internal timeout used when creating/updating the CANopen node wrapper.
Usually not required for basic NMT usage.
|}
* '''Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| string
|"<TIMESTAMP>"
|Same request identifier received in the request. Used to correlate request and
response.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the response.
|-
|task
|object
|{...}
|Main response object.
|-
|task.alias
|string
|"NMT_COMMAND"
|Alias of the command that was executed.
|-
|task.taskResult
|object
|{...}
|Result object containing the NMT command result.
|-
|task.taskResult.success
|boolean
|true
|Indicates whether the actor successfully executed the NMT command.
|-
|task.taskResult.id
|string
|"nmt-start-node5"
|Internal/request ID returned by the CANopen RPC call. Usually matches the
original request '''id'''.
|-
|task.taskResult.node_id
|integer
|5
|CANopen node ID that received the NMT command.
|-
|task.taskResult.command
|string
|"START"
|NMT command that was executed. For aliases such as '''RESET''' , the response may
normalize the command to '''RESET_NODE'''.
|-
|task.taskResult.confirmed
|boolean
|true
|Indicates whether the command was considered successful. If '''wait''' = '''false''' , this
usually means the command was sent successfully. If '''wait''' = '''true''' , this means
the expected heartbeat/state confirmation was received.
|-
|task.taskResult.state
|string/null
|null
|Confirmed NMT state when '''wait''' = '''true''' and state confirmation is available.
Otherwise it is '''null''' . Possible values include '''OPERATIONAL''' , '''STOPPED''' , and '''PRE_OP'''.
|-
|task.taskResult.error
|string
|"unsupported NMT
command X"
|Error description. Present only when the command fails.
|}
====== SDO Read ======
Reads an object dictionary value using SDO upload.
'''Req-Topic''': canopenRM8007/runtime/in<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "APP",
  "task": {
    "alias": "SDO_READ",
    "taskParams": {
      "node_id": 5,
      "index": "0x6004",
      "subindex": "0x00",
      "timeout": 2.0
    }
  }
}
</syntaxhighlight>'''Reply-Topic''': canopenRM8007/runtime/out<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "SDO_READ",
    "taskResult": {
      "success": true,
      "id": "sdo-read-position-node5",
      "node_id": 5,
      "index": 24580,
      "subindex": 0,
      "value": 123456
    }
  }
}
</syntaxhighlight>
* '''Request Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Example'''
|'''Description'''
|-
|id
| string
|Yes
|"<TIMESTAMP>"
|Unique request identifier. The same value is returned in the response.
|-
|origin
| string
|No
|"APP"
|Identifies the sender of the request, for example '''APP''' , '''NodeRED''' , '''CLI''' , or
'''TestClient'''.
|-
|task
|object
|Yes
|{...}
|Main command object containing the command alias and parameters.
|-
|task.alias
|string
|Yes
|"SDO_READ"
|Defines the command to execute. For this operation, it must be '''SDO_READ'''.
|-
|task.taskParams
|object
|Yes
|{...}
|Contains the SDO read parameters.
|-
|task.taskParams.node_id
|integer
|Yes
|5
|Numeric CANopen node ID to read from.
|-
|task.taskParams.nodeId
|integer
|Yes
|5
|Alternative spelling for '''node_id'''.
|-
|task.taskParams.node
|string
|Yes
|"motor_1"
|Logical node name previously configured with '''CONFIG_CANOPEN'''.
|-
|task.taskParams.index
|integer/string
|Yes
|"0x6004"
|CANopen object dictionary index to read. Hex strings such as "'''0x6004'''"
are supported.
|-
|task.taskParams.subindex
|integer/string
|No
|"0x00"
|CANopen object dictionary subindex to read. If omitted, defaults to '''0'''.
|-
|task.taskParams.timeout
|number
|No
|2.0
|Timeout, in seconds, used for the SDO read operation. If omitted, the
actor default SDO timeout is used.
|}
* '''Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| string
|"<TIMESTAMP>"
|Same request identifier received in the request. Used to correlate request and
response.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the response.
|-
|task
|object
|{...}
|Main response object.
|-
|task.alias
|string
|"SDO_READ"
|Alias of the command that was executed.
|-
|task.taskResult
|object
|{...}
|Result object containing the SDO read result.
task
|-
|task.taskResult.success
|boolean
|true
|Indicates whether the SDO read was successful.
|-
|task.taskResult.id
|string
|"sdo-read-positionnode5"
|Internal/request ID returned by the CANopen RPC call. Usually matches the
original request '''id'''.
|-
|task.taskResult.node_id
|integer
|5
|CANopen node ID that was read.
|-
|task.taskResult.index
|integer
|24580
|Object dictionary index that was read, returned as decimal. Example: '''24580''' =
'''0x6004'''.
|-
|task.taskResult.subindex
|integer
|0
|Object dictionary subindex that was read.
|-
|task.taskResult.value
|integer
|123456
|Raw value returned by the SDO read operation.
task.
|-
|task.taskResult.error
|string
|"SDO timeout
0x6004:00"
|Error description. Present only when the command fails.
SDO
|}
* SDO Write
Writes an object dictionary value using SDO download.
'''Req-Topic''': canopenRM8007/runtime/in<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "APP",
  "task": {
    "alias": "SDO_WRITE",
    "taskParams": {
      "node_id": 5,
      "index": "0x6003",
      "subindex": "0x00",
      "value": 0,
      "size": 4,
      "timeout": 2.0
    }
  }
}
</syntaxhighlight>'''Reply-Topic''': canopenRM8007/runtime/out<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "SDO_WRITE",
    "taskResult": {
      "success": true,
      "id": "sdo-write-node5",
      "node_id": 5,
      "index": 24579,
      "subindex": 0,
      "value": 0,
      "size": 4
    }
  }
}
</syntaxhighlight>
* '''Parameter descirption'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Default'''
|'''Description'''
|-
|node_id / nodeId / node
| int/string
|Yes
|—
|CANopen node reference.
|-
|index
| int/string
|Yes
|—
|Object dictionary index.
|-
|subindex
|int/string
|No
|0
|Object dictionary subindex.
|-
|value
|int/string
|Yes
|—
|Value to write.
|-
|size
|int
|No
|4
|Number of bytes: usually '''1''' , '''2''' , or '''4.'''
|-
|timeout
|float
|No
|actor default
|SDO timeout in seconds.
|}
* '''Request Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Example'''
|'''Description'''
|-
|id
| string
|Yes
|"<TIMESTAMP>"
|Unique request identifier. The same value is returned in the response.
|-
|origin
| string
|No
|"APP"
|Identifies the sender of the request, for example '''APP''' , '''NodeRED''' , '''CLI''' , or
'''TestClient'''.
|-
|task
|object
|Yes
|{...}
|Main command object containing the command alias and parameters.
|-
|task.alias
|string
|Yes
|"SDO_WRITE"
|Defines the command to execute. For this operation, it must be
'''SDO_WRITE'''.
|-
|task.taskParams
|object
|Yes
|{...}
|Contains the SDO write parameters.
|-
|task.taskParams.node_id
|integer
|Yes*
|5
|Numeric CANopen node ID to write to.
|-
|task.taskParams.nodeId
|integer
|Yes*
|5
|Alternative spelling for '''node_id'''.
|-
|task.taskParams.node
|string
|Yes*
|"motor_1"
|Logical node name previously configured with '''CONFIG_CANOPEN.'''
|-
|task.taskParams.index
|integer/string
|Yes
|"0x6003"
|CANopen object dictionary index to write. Hex strings such as "'''0x6003'''"
are supported.
|-
|task.taskParams.subindex
|integer/string
|No
|"0x00"
|CANopen object dictionary subindex to write. If omitted, defaults to '''0'''.
|-
|task.taskParams.value
|integer/string
|Yes
|0
|Value to write into the selected object dictionary entry. Hex strings are
also supported.
|-
|task.taskParams.size
|integer
|No
|4
|Number of bytes to write. Valid values are normally '''1''' , '''2''' , '''3''' , or '''4''' . If
omitted, defaults to '''4'''.
|-
|task.taskParams.timeout
|number
|No
|2.0
|Timeout, in seconds, used for the SDO write operation. If omitted, the
actor default SDO timeout is used.
|}
* '''Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| string
|"<TIMESTAMP>"
|Same request identifier received in the request. Used to correlate request andresponse.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the response.
|-
|task
|object
|{...}
|Main response object.
|-
|task.alias
|string
|"SDO_WRITE"
|Alias of the command that was executed.
|-
|task.taskResult
|object
|{...}
|Result object containing the SDO write result.
|-
|task.taskResult.success
|boolean
|true
|Indicates whether the SDO write was successful.
|-
|task.taskResult.id
|string
|"sdo-write-node5"
|Internal/request ID returned by the CANopen RPC call.
Usually matches theoriginal request '''id'''.
|-
|task.taskResult.node_id
|integer
|5
|CANopen node ID that was written to.
|-
|task.taskResult.index
|integer
|24579
|Object dictionary index that was written, returned as decimal.
Example: '''24579 =0x6003'''.
|-
|task.taskResult.subindex
|integer
|0
|Object dictionary subindex that was written.
|-
|task.taskResult.value
|integer
|0
|Value that was written.
|-
|task.taskResult.size
|integer
|4
|Number of bytes written.
|-
|task.taskResult.error
|string
|"SDO abort 0x6003:00 ->..."
|Error description. Present only when the command fails.
|}
====== Read Node State ======
Reads position and returns raw value, revolutions, and angle. Internally, this reads the configured position object, defaulting to
'''0x6004:00'''. '''If''' '''steps_per_rev''' is not provided, the owner reads '''0x6501:00''' to obtain it.
'''Req-Topic''': canopenRM8007/runtime/in<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "APP",
  "task": {
    "alias": "READ",
    "taskParams": {
      "node_id": 5,
      "index": "0x6004",
      "subindex": "0x00",
      "steps_per_rev": 4096,
      "timeout": 2.0
    }
  }
}
</syntaxhighlight>'''Reply-Topic''': canopenRM8007/runtime/out<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "READ",
    "taskResult": {
      "success": true,
      "id": "read-node5",
      "node_id": 5,
      "raw": 1024,
      "steps_per_rev": 4096,
      "revolutions": 0.25,
      "angle_deg": 90.0,
      "index": 24580,
      "subindex": 0
    }
  }
}
</syntaxhighlight>
* '''Request Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Example'''
|'''Description'''
|-
|id
| string
|Yes
|"<TIMESTAMP>"
|Unique request identifier. The same value is returned in theresponse.
|-
|origin
| string
|No
|"APP"
|Identifies the sender of the request, for example '''APP, NodeRED, CLI''', or '''TestClient'''.
|-
|task
|object
|Yes
|{...}
|Main command object containing the command alias andparameters.
|-
|task.alias
|string
|Yes
|"READ"
|Defines the command to execute. For this operation, it must be '''READ'''.
|-
|task.taskParams
|object
|Yes
|{...}
|Contains the position read parameters.
|-
|task.taskParams.node_id
|integer
|Yes*
|5
|Numeric CANopen node ID to read from.
|-
|task.taskParams.nodeId
|integer
|Yes*
|5
|Alternative spelling for '''node_id'''.
|-
|task.taskParams.node
|string
|Yes*
|"motor_1"
|Logical node name previously configured with '''CONFIG_CANOPEN'''.
|-
|task.taskParams.index
|integer/string
|No
|"0x6004"
|CANopen object dictionary index used as the raw positionsource. If omitted, defaults to '''0x6004'''.
|-
|task.taskParams.subindex
|integer/string
|No
|"0x00"
|CANopen object dictionary subindex used as the raw positionsource. If omitted, defaults to '''0x00'''.
|-
|task.taskParams.steps_per_rev
|integer
|Recommended
|4096
|Number of raw counts per mechanical revolution. Used tocalculate
'''revolutions''' and '''angle_deg'''. If omitted, the ownerattempts to read '''0x6501:00'''.
|-
|task.taskParams.timeout
|number
|No
|2.0
|Timeout, in seconds, used for the internal SDO reads. Ifomitted, the actor default SDO timeout is used.
|-
|task.taskParams.sdo_timeout
|number
|No
|2.0
|Internal SDO timeout used when creating/updating the nodewrapper. Usually not required.
|}
* '''Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| string
|"<TIMESTAMP>"
|Same request identifier received in the request. Used to correlate request andresponse.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the response.
|-
|task
|object
|{...}
|Main response object.
|-
|task.alias
|string
|"READ"
|Alias of the command that was executed.
|-
|task.taskResult
|object
|{...}
|Result object containing the position read result.
|-
|task.taskResult.success
|boolean
|true
|Indicates whether the position read was successful.
|-
|task.taskResult.id
|string
|"read-node5"
|Internal/request ID returned by the CANopen RPC call. Usually matches theoriginal request '''id'''.
|-
|task.taskResult.node_id
|integer
|5
|CANopen node ID that was read.
|-
|task.taskResult.raw
|integer
|1024
|Raw position value read from the selected object dictionary entry.
|-
|task.taskResult.steps_per_rev
|integer
|4096
|Number of raw counts per revolution used for the conversion.
|-
|task.taskResult.revolutions
|number
|0.25
|Raw position converted into revolutions. Example: '''1024 / 4096 = 0.25'''.
|-
|task.taskResult.angle_deg
|number
|90.0
|Position inside one revolution, in degrees. Example: '''0.25 × 360 = 90°'''.
|-
|task.taskResult.index
|integer
|24580
|Object dictionary index read as the raw position source. Example: '''24580 =0x6004'''.
|-
|task.taskResult.subindex
|integer
|0
|Object dictionary subindex read as the raw position source.
|-
|task.taskResult.error
|string
|"SDO timeout0x6004:00"
|Error description. Present only when the command fails.
|}
====== READ SDO Continuous ======
Starts continuous SDO-based position reading.
The initial command returns an acknowledgement. After that, continuous samples are published repeatedly to '''canopen/runtime/out'''
The continuous loop requires a single node reference and requires '''steps_per_rev'''.
* '''Start request'''
'''Req-Topic''': canopenRM8007/runtime/in<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "APP",
  "task": {
    "alias": "READ_CONTINUOUS",
    "taskParams": {
      "node_id": 5,
      "steps_per_rev": 4096,
      "index": "0x6004",
      "subindex": "0x00",
      "timeout": 0.2,
      "interval_ms": 20
    }
  }
}
</syntaxhighlight>'''Reply-Topic''': canopenRM8007/runtime/out<syntaxhighlight lang="json" line="1">
// Start acknowledgement
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "READ_CONTINUOUS",
    "taskResult": {
      "success": true,
      "running": true,
      "mode": "single",
      "interval_ms": 20
    }
  }
}
</syntaxhighlight><syntaxhighlight lang="json" line="1">
// Continuous sample response
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "READ_CONTINUOUS",
    "taskResult": {
      "success": true,
      "node_id": 5,
      "angle_deg": 90.0,
      "steps_per_rev": 4096,
      "revolutions": 0.25,
      "raw": 1024,
      "index": 24580,
      "subindex": 0,
      "continuous": true,
      "request_id": "read-cont-node5"
    }
  }
}
</syntaxhighlight>
* '''Start Request Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Example'''
|'''Description'''
|-
|id
| string
|Yes
|"<TIMESTAMP>"
|Unique request identifier. Continuous sample responses include
this value as '''request_id'''.
|-
|origin
| string
|No
|"APP"
|Identifies the sender of the request, for example '''APP''', '''NodeRED''',
'''CLI''', or '''TestClient'''.
|-
|task
|object
|Yes
|{...}
|Main command object containing the command alias and
parameters.
|-
|task.alias
|string
|Yes
|"READ_CONTINUOUS"
|Defines the command to execute. For this operation, it must be
'''READ_CONTINUOUS'''.
|-
|task.taskParams
|object
|Yes
|{...}
|Contains the continuous read parameters.
|-
|task.taskParams.node_id
|integer
|Yes*
|5
|Numeric CANopen node ID to read continuously.
|-
|task.taskParams.nodeId
|integer
|Yes*
|5
|Alternative spelling for '''node_id'''.
|-
|task.taskParams.node
|string
|Yes*
|"motor_1"
|Logical node name previously configured with '''CONFIG_CANOPEN'''.
|-
|task.taskParams.steps_per_rev
|integer
|Yes**
|4096
|Number of raw counts per mechanical revolution. Required by
the fast continuous read loop to calculate '''revolutions''' and
'''angle_deg'''.
|-
|task.taskParams.index
|integer/string
|No
|"0x6004"
|CANopen object dictionary index used as the raw position
source. If omitted, defaults to '''0x6004'''.
|-
|task.taskParams.subindex
|integer/string
|No
|"0x00"
|CANopen object dictionary subindex used as the raw position
source. If omitted, defaults to '''0x00'''.
|-
|task.taskParams.timeout
|number
|No
|0.2
|Timeout, in seconds, used for each fast SDO read cycle. If
omitted, defaults to '''0.2'''.
|-
|task.taskParams.interval_ms
|integer
|No
|20
|Delay between continuous read cycles, in milliseconds. If '''0''' , the
loop runs again immediately after each cycle yields control.
|-
|task.taskParams.action
|string
|No
|"START"
|Optional action. If set to '''STOP''' , '''OFF''' , '''CANCEL''' , or '''DISABLE''' , the
actor stops the continuous read loop instead of starting it.
|}
* '''Start Acknowledgement Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| string
|"<TIMESTAMP>"
|Same request identifier received in the start request.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the response.
|-
|task
|object
|{...}
|Main response object.
|-
|task.alias
|string
|"READ_CONTINUOUS"
|Alias of the command that was executed.
|-
|task.taskResult
|object
|{...}
|Result object confirming that continuous reading was started.
|-
|task.taskResult.success
|boolean
|true
|Indicates whether the start command was accepted.
|-
|task.taskResult.running
|boolean
|true
|Indicates that the continuous read loop is running.
|-
|task.taskResult.mode
|string
|"single"
|Read mode. In this fast continuous path, a single node is used when '''node_id''' ,
'''nodeId''' , or '''node''' is provided.
|-
|task.taskResult.interval_ms
|integer
|20
|Configured delay between continuous read cycles, in milliseconds.
|-
|task.taskResult.error
|string
|"no configured nodes"
|Error description. Present only when the start command fails.
|}
* '''Continuous Sample Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| integer/string
|1777041089180
|Sample response ID generated by the actor for each read cycle.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the sample response.
|-
|task
|object
|{...}
|Main response object.
|-
|task.alias
|string
|"READ_CONTINUOUS"
|Alias of the running continuous command.
|-
|task.taskResult
|object
|{...}
|Result object containing one continuous position sample.
|-
|task.taskResult.success
|boolean
|true
|Indicates whether this specific read cycle was successful.
|-
|task.taskResult.node_id
|integer
|5
|CANopen node ID that was read.
|-
|task.taskResult.angle_deg
|number
|90.0
|Position inside one revolution, in degrees.
|-
|task.taskResult.steps_per_rev
|integer
|4096
|Number of raw counts per revolution used for conversion.
|-
|task.taskResult.revolutions
|number
|0.25
|Raw position converted into revolutions.
|-
|task.taskResult.raw
|integer
|1024
|Raw position value read from the node.
|-
|task.taskResult.index
|integer
|24580
|Object dictionary index read as the raw position source. Example: '''24580 ='''
'''0x6004'''.
|-
|task.taskResult.subindex
|integer
|0
|Object dictionary subindex read as the raw position source.
|-
|task.taskResult.continuous
|boolean
|true
|Marks this response as a continuous sample, not just the initial
acknowledgement.
|-
|task.taskResult.request_id
|string
|"read-cont-node5"
|Original request '''id''' that started the continuous loop. Useful to correlate
samples with the start command.
|-
|task.taskResult.error
|string
|"operation failed"
|Error description for a failed sample. Present only when a read cycle fails.
|}
====== Stop request ======
You can stop using either '''STOP_READ_CONTINUOUS''' :
'''Req-Topic''': canopenRM8007/runtime/in<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "APP",
  "task": {
    "alias": "STOP_READ_CONTINUOUS",
    "taskParams": {}
  }
}
</syntaxhighlight>Or by sending '''READ_CONTINUOUS''' with '''action: "STOP"''' :<syntaxhighlight lang="json" line="1">
{
  "id": "stop-read-cont",
  "origin": "APP",
  "task": {
    "alias": "READ_CONTINUOUS",
    "taskParams": {
      "action": "STOP"
    }
  }
}
</syntaxhighlight>'''Reply-Topic''': canopenRM8007/runtime/out<syntaxhighlight lang="json" line="1">
{
  "id": "stop-read-cont",
  "origin": "canopen-actor",
  "task": {
    "alias": "READ_CONTINUOUS",
    "taskResult": {
      "success": true,
      "running": false,
      "stopped": true
    }
  }
}
</syntaxhighlight>
* '''Stop Request Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Example'''
|'''Description'''
|-
|id
| string
|Yes
|"stop-read-cont"
|Unique request identifier.
|-
|origin
| string
|No
|"APP"
|Identifies the sender of the request.
|-
|task.alias
|string
|Yes
|"READ_CONTINUOUS" or
"STOP_READ_CONTINUOUS"
|Command used to stop the continuous read loop.
|-
|task.taskParams.action
|string
|No
|"STOP"
|When using '''READ_CONTINUOUS''' , set '''action''' to '''STOP''' , '''OFF''' , '''CANCEL''' ,
or '''DISABLE''' to stop the loop. Not required when using
'''STOP_READ_CONTINUOUS'''.
|}
* '''Stop Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| string
|"stop-read-cont"
|Same request identifier received in the stop request.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the response.
|-
|task.alias
|string
|"READ_CONTINUOUS"
|Alias returned by the stop handler.
|-
|task.taskResult.success
|boolean
|true
|Indicates whether the stop request was processed successfully.
|-
|task.taskResult.running
|boolean
|false
|Indicates that the continuous read loop is no longer running.
|-
|task.taskResult.stopped
|boolean
|true
|Indicates whether there was an active task that was actually stopped. If no continuous
task was running, this may be '''false'''.
|}
====== Continuous PDO Reading ======
Starts continuous PDO-based position reading.
This command does not perform repeated SDO reads. Instead, it waits for TPDO frames already being transmitted by the CANopen node.
Normally, the TPDO should be configured first using '''CONFIG_POSITION_TPDO'''.
The initial command returns an acknowledgement. After that, continuous sample responses are published repeatedly to: '''canopenRM8007/runtime/out'''
* '''Start Request'''
'''Req-Topic''': canopenRM8007/runtime/in<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "APP",
  "task": {
    "alias": "READ_PDO_CONTINUOUS",
    "taskParams": {
      "node_id": 5,
      "pdo_num": 1,
      "steps_per_rev": 4096,
      "offset": 0,
      "size": 4,
      "signed": false,
      "timeout": 1.0,
      "interval_ms": 20
    }
  }
}
</syntaxhighlight>
* '''Start Acknowledgement'''
'''Reply-Topic''': canopenRM8007/runtime/out<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "READ_PDO_CONTINUOUS",
    "taskResult": {
      "success": true,
      "running": true,
      "mode": "pdo",
      "pdo_num": 1,
      "interval_ms": 20
    }
  }
}
</syntaxhighlight>
* '''Continuous Sample Response'''
'''Reply-Topic''': canopenRM8007/runtime/out<syntaxhighlight lang="json" line="1">
{
  "id": "<TIMESTAMP>",
  "origin": "canopen-actor",
  "task": {
    "alias": "READ_PDO_CONTINUOUS",
    "taskResult": {
      "success": true,
      "node_id": 5,
      "pdo_num": 1,
      "angle_deg": 90.0,
      "steps_per_rev": 4096,
      "revolutions": 0.25,
      "raw": 1024,
      "data": [0, 4, 0, 0],
      "offset": 0,
      "size": 4,
      "signed": false,
      "continuous": true,
      "request_id": "read-pdo-cont-node5"
    }
  }
}
</syntaxhighlight>'''Start Request Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Required'''
|'''Example'''
|'''Description'''
|-
|id
| string
|Yes
|"<TIMESTAMP>"
|Unique request identifier. Continuous sample responses include
this value as '''request_id'''.
|-
|origin
| string
|No
|"APP"
|Identifies the sender of the request, for example '''APP , NodeRED ,'''
'''CLI , or TestClient'''.
|-
|task
|object
|Yes
|{...}
|Main command object containing the command alias and
parameters.
|-
|task.alias
|string
|Yes
|"READ_PDO_CONTINUOUS"
|Defines the command to execute.
|-
|task.taskParams
|object
|Yes
|{...}
|Contains the PDO continuous read parameters.
|-
|task.taskParams.node_id
|integer
|Yes*
|5
|Numeric CANopen node ID to read from.
|-
|task.taskParams.nodeId
|integer
|Yes*
|5
|Alternative spelling for '''node_id'''.
|-
|task.taskParams.node
|string
|Yes*
|"motor_1"
|Logical node name previously configured with '''CONFIG_CANOPEN'''. If
used, the actor can reuse saved '''steps_per_rev , pdo_num ,'''
'''offset , size''' , and '''signed''' values from the node configuration.
|-
|task.taskParams.pdo_num
|integer
|No
|1
|TPDO number to listen to. If omitted, defaults to '''1'''.
|-
|task.taskParams.steps_per_rev
|integer
|Yes**
|4096
|Number of raw position counts per mechanical revolution. Used
to convert '''raw''' into '''revolutions''' and '''angle_deg'''.
|-
|task.taskParams.offset
|integer
|No
|0
|Byte offset inside the PDO payload where the position value
starts.
|-
|task.taskParams.size
|integer
|No
|4
|Number of bytes to decode from the PDO payload. For a 32-bit
position value, use '''4'''.
|-
|task.taskParams.signed
|boolean
|No
|false
|Defines whether the raw value should be decoded as signed or
unsigned.
|-
|task.taskParams.timeout
|number
|No
|1.0
|Timeout, in seconds, while waiting for each TPDO frame. If no
TPDO is received within this time, the cycle fails.
|-
|task.taskParams.interval_ms
|integer
|No
|20
|Delay between read cycles, in milliseconds. If '''0''' , the loop
immediately starts the next cycle after yielding control.
|-
|task.taskParams.action
|string
|No
|"START"
|Optional action. If set to '''STOP , OFF , CANCEL , or DISABLE''' , the
actor stops the continuous loop instead of starting it.
|}
* '''Start Acknowledgement Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| string
|"<TIMESTAMP>"
|Same request identifier received in the start request.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the response.
|-
|task
|object
|{...}
|Main response object.
|-
|task.alias
|string
|"READ_PDO_CONTINUOUS"
|Alias of the command that was executed.
|-
|task.taskResult
|object
|{...}
|Result object confirming whether the continuous PDO read
was started.
|-
|task.taskResult.success
|boolean
|true
|Indicates whether the start command was accepted.
|-
|task.taskResult.running
|boolean
|true
|Indicates that the continuous PDO read loop is running.
|-
|task.taskResult.mode
|string
|"pdo"
|Indicates that the loop is using PDO reception mode.
|-
|task.taskResult.pdo_num
|integer
|1
|TPDO number being listened to.
|-
|task.taskResult.interval_ms
|integer
|20
|Configured delay between read cycles, in milliseconds.
|-
|task.taskResult.error
|string
|"'''READ_PDO_CONTINUOUS''' requires a single
node"
|Error description. Present only when the start command
fails.
|}
* '''Continuous Sample Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| integer/string
|1777041089180
|Sample response ID generated by the actor for each PDO read cycle.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the sample.
|-
|task
|object
|{...}
|Main response object.
|-
|task.alias
|string
|"READ_PDO_CONTINUOUS"
|Alias of the running continuous command.
task.
|-
|task.taskResult
|object
|{...}
|Result object containing one decoded PDO position sample.
|-
|task.taskResult.success
|boolean
|true
|Indicates whether this specific PDO read cycle was successful.
|-
|task.taskResult.node_id
|integer
|5
|CANopen node ID that produced the TPDO.
|-
|task.taskResult.pdo_num
|integer
|1
|TPDO number received and decoded.
|-
|task.taskResult.angle_deg
|number
|90.0
|Position inside one revolution, in degrees.
|-
|task.taskResult.steps_per_rev
|integer
|4096
|Number of raw counts per revolution used for conversion.
|-
|task.taskResult.revolutions
|number
|0.25
|Raw position converted into revolutions. Example: '''1024 / 4096 = 0.25''' .
|-
|task.taskResult.raw
|integer
|1024
|Decoded raw position value from the PDO payload.
|-
|task.taskResult.data
|array
|[0, 4, 0, 0]
|Raw PDO payload bytes received from the CANopen node.
|-
|task.taskResult.offset
|integer
|0
|Byte offset used to extract the raw position value from '''data.'''
|-
|task.taskResult.size
|integer
|4
|Number of bytes decoded from '''data'''.
|-
|task.taskResult.signed
|boolean
|false
|Indicates whether the raw value was decoded as signed or unsigned.
|-
|task.taskResult.continuous
|boolean
|true
|Marks this response as a continuous sample.
|-
|task.taskResult.request_id
|string
|"read-pdo-cont-node5"
|Original request '''id''' that started the continuous loop. Useful to
correlate samples with the start command.
|-
|task.taskResult.error
|string
|"pdo operation failed"
|Error description. Present only when a sample fails.
|}
====== Stop Request ======
'''Req-Topic''': canopenRM8007/runtime/in<syntaxhighlight lang="json" line="1">
{
  "id": "stop-pdo-cont",
  "origin": "APP",
  "task": {
    "alias": "STOP_READ_PDO_CONTINUOUS",
    "taskParams": {}
  }
}
</syntaxhighlight>Alternative stop request using '''READ_PDO_CONTINUOUS''':<syntaxhighlight lang="json" line="1">
{
  "id": "stop-pdo-cont",
  "origin": "APP",
  "task": {
    "alias": "READ_PDO_CONTINUOUS",
    "taskParams": {
      "action": "STOP"
    }
  }
}
</syntaxhighlight>
====== '''Stop Response''' ======
'''Reply-Topic''': canopenRM8007/runtime/out<syntaxhighlight lang="json" line="1">
{
  "id": "stop-pdo-cont",
  "origin": "canopen-actor",
  "task": {
    "alias": "READ_PDO_CONTINUOUS",
    "taskResult": {
      "success": true,
      "running": false,
      "stopped": true
    }
  }
}
</syntaxhighlight>
* '''Stop Response Parameters'''
{| class="wikitable"
|'''Parameter'''
|'''Type'''
|'''Example'''
|'''Description'''
|-
|id
| string
|"stop-pdo-cont"
|Same request identifier received in the stop request.
|-
|origin
| string
|"canopen-actor"
|Identifies the component that generated the response.
|-
|task.alias
|string
|"READ_PDO_CONTINUOUS"
|Alias returned by the stop handler.
|-
|task.taskResult.success
|boolean
|true
|Indicates whether the stop request was processed successfully.
|-
|task.taskResult.running
|boolean
|false
|Indicates that the continuous PDO loop is no longer running.
|-
|task.taskResult.stopped
|boolean
|true
|Indicates whether there was an active continuous task that was actually stopped. If
no task was running, this may be '''false'''.
|}
====== Decode Logic ======
The received PDO payload is decoded using:
* '''offset''': first byte to read from the PDO payload;
* '''size''': number of bytes to read;
* '''signed''': whether the value is signed or unsigned;
* '''steps_per_rev''': scaling factor used to calculate revolutions and angle.
The raw value is decoded as little-endian:
'''raw = int.from_bytes(data[offset : offset + size], byteorder="little", signed=signed)'''
Then:
'''revolutions = raw / steps_per_rev'''
'''angle_deg = ((raw % steps_per_rev) / steps_per_rev) * 360.0'''
===== 24.14.4 Design Principles =====
* '''Protocol-Only Actor'''
The CANopen Actor is responsible for:
- CAN frame encoding/decoding;
- SDO communication;
- PDO reception (raw);
- NMT control;
- Heartbeat monitoring.
* '''No Device Semantics'''
The actor:
'''Does NOT know''':
- What 0x6004 represents;
- Units (RPM, mm, °C, etc.);
- Device-specific logic.
'''Only returns''':
- Raw values;
- Protocol-level responses.
* '''Node Management'''
- Nodes must be defined via configuration;
- Actor maintains node state internally;
- Supports multiple nodes simultaneously.
===== Summary =====
The CANopen Actor:
- Implements '''CANopen protocol communication;'''
- Provides '''generic MQTT interface;'''
- Supports '''multi-node networks;'''
- Keeps '''strict separation between transport and semantics.'''
=== 25. Included Software Stack ===
<br>
[[File:AtlasCode.svg|center|frameless|583x583px]]
[[File:AtlasCode.svg|center|frameless|583x583px]]


=== 26. Hardware Libraries ===
<br>
[IMAGE PLACEHOLDER]
 
=== 26. Software Stack and IPC Architecture ===
<br>
[[File:Hardware Peripherals Library Architecture.svg|center|frameless|1934x1934px]]


=== 27. Related Products ===
=== 27. Related Products ===