/** * CANopen access from other networks - ASCII mapping (CiA 309-3 DS v3.0.0) * * @file CO_gateway_ascii.h * @ingroup CO_CANopen_309_3 * @author Janez Paternoster * @author Martin Wagner * @copyright 2020 Janez Paternoster * * This file is part of CANopenNode, an opensource CANopen Stack. * Project home page is . * For more information on CANopen see . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CO_GATEWAY_ASCII_H #define CO_GATEWAY_ASCII_H #include "301/CO_driver.h" #include "301/CO_fifo.h" #include "301/CO_SDOclient.h" #include "301/CO_NMT_Heartbeat.h" #include "305/CO_LSSmaster.h" #include "303/CO_LEDs.h" /* default configuration, see CO_config.h */ #ifndef CO_CONFIG_GTW #define CO_CONFIG_GTW (0) #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII) || defined CO_DOXYGEN #ifdef __cplusplus extern "C" { #endif /** * @defgroup CO_CANopen_309_3 Gateway ASCII mapping * @ingroup CO_CANopen_309 * @{ * * CANopen access from other networks - ASCII mapping (CiA 309-3 DSP v3.0.0) * * This module enables ascii command interface (CAN gateway), which can be used * for master interaction with CANopen network. Some sort of string input/output * stream can be used, for example serial port + terminal on microcontroller or * stdio in OS or sockets, etc. * * For example, one wants to read 'Heartbeat producer time' parameter (0x1017,0) * on remote node (with id=4). Parameter is 16-bit integer. He can can enter * command string: `[1] 4 read 0x1017 0 i16`. CANopenNode will use SDO client, * send request to remote node via CAN, wait for response via CAN and prints * `[1] OK` to output stream on success. * * This module is usually initialized and processed in CANopen.c file. * Application should register own callback function for reading the output * stream. Application writes new commands with CO_GTWA_write(). */ /** * @defgroup CO_CANopen_309_3_Syntax Command syntax * @{ * * @code{.unparsed} Command strings start with '"[""]"' followed by: [[] ] r[ead] [] # SDO upload. [[] ] w[rite] # SDO download. [[] ] start # NMT Start node. [[] ] stop # NMT Stop node. [[] ] preop[erational] # NMT Set node to pre-operational. [[] ] reset node # NMT Reset node. [[] ] reset comm[unication] # NMT Reset communication. [] set network # Set default net. [] set node # Set default node. [] set sdo_timeout # Configure SDO time-out. [] set sdo_block # Enable/disable SDO block transfer. help [datatype|lss] # Print this or datatype or lss help. led # Print status LED diodes. log # Print message log. Response: "[""]" OK | | ERROR: | ERROR: * Every command must be terminated with ('\\r\\n'). characters. Same is response. String is not null terminated, is optional in command. * Comments started with '#' are ignored. They may be on the beginning of the line or after the command string. * 'sdo_timeout' is in milliseconds, 500 by default. Block transfer is disabled by default. * If '' or '' is not specified within commands, then value defined by 'set network' or 'set node' command is used. Datatypes: b # Boolean. i8, i16, i32, i64 # Signed integers. u8, u16, u32, u64 # Unsigned integers. x8, x16, x32, x64 # Unsigned integers, displayed as hexadecimal, non-standard. r32, r64 # Real numbers. t, td # Time of day, time difference. vs # Visible string (between double quotes if multi-word). os, us # Octet, unicode string, (mime-base64 (RFC2045) based, line). d # domain (mime-base64 (RFC2045) based, one line). hex # Hexagonal data, optionally space separated, non-standard. LSS commands: lss_switch_glob <0|1> # Switch state global command. lss_switch_sel \\ #Switch state selective. lss_set_node # Configure node-ID. lss_conf_bitrate \\ # Configure bit-rate. lss_activate_bitrate # Activate new bit-rate. lss_store # LSS store configuration. lss_inquire_addr [] # Inquire LSS address. lss_get_node # Inquire node-ID. _lss_fastscan [] # Identify fastscan, non-standard. lss_allnodes [ [ \\ [ \\ ]]] # Node-ID configuration of all nodes. * All LSS commands start with '\"[\"\"]\" []'. * : 0=1000 kbit/s, 1=800 kbit/s, 2=500 kbit/s, 3=250 kbit/s, 4=125 kbit/s, 6=50 kbit/s, 7=20 kbit/s, 8=10 kbit/s, 9=auto * : 0=fastscan, 1=ignore, 2=match value in next parameter * @endcode * * This help text is the same as variable contents in CO_GTWA_helpString. * @} */ /** Size of response string buffer. This is intermediate buffer. If there is * larger amount of data to transfer, then multiple transfers will occur. */ #ifndef CO_GTWA_RESP_BUF_SIZE #define CO_GTWA_RESP_BUF_SIZE 200 #endif /** Timeout time in microseconds for some internal states. */ #ifndef CO_GTWA_STATE_TIMEOUT_TIME_US #define CO_GTWA_STATE_TIMEOUT_TIME_US 1200000 #endif /** * Response error codes as specified by CiA 309-3. Values less or equal to 0 * are used for control for some functions and are not part of the standard. */ typedef enum { /** 0 - No error or idle */ CO_GTWA_respErrorNone = 0, /** 100 - Request not supported */ CO_GTWA_respErrorReqNotSupported = 100, /** 101 - Syntax error */ CO_GTWA_respErrorSyntax = 101, /** 102 - Request not processed due to internal state */ CO_GTWA_respErrorInternalState = 102, /** 103 - Time-out (where applicable) */ CO_GTWA_respErrorTimeOut = 103, /** 104 - No default net set */ CO_GTWA_respErrorNoDefaultNetSet = 104, /** 105 - No default node set */ CO_GTWA_respErrorNoDefaultNodeSet = 105, /** 106 - Unsupported net */ CO_GTWA_respErrorUnsupportedNet = 106, /** 107 - Unsupported node */ CO_GTWA_respErrorUnsupportedNode = 107, /** 200 - Lost guarding message */ CO_GTWA_respErrorLostGuardingMessage = 200, /** 201 - Lost connection */ CO_GTWA_respErrorLostConnection = 201, /** 202 - Heartbeat started */ CO_GTWA_respErrorHeartbeatStarted = 202, /** 203 - Heartbeat lost */ CO_GTWA_respErrorHeartbeatLost = 203, /** 204 - Wrong NMT state */ CO_GTWA_respErrorWrongNMTstate = 204, /** 205 - Boot-up */ CO_GTWA_respErrorBootUp = 205, /** 300 - Error passive */ CO_GTWA_respErrorErrorPassive = 300, /** 301 - Bus off */ CO_GTWA_respErrorBusOff = 301, /** 303 - CAN buffer overflow */ CO_GTWA_respErrorCANbufferOverflow = 303, /** 304 - CAN init */ CO_GTWA_respErrorCANinit = 304, /** 305 - CAN active (at init or start-up) */ CO_GTWA_respErrorCANactive = 305, /** 400 - PDO already used */ CO_GTWA_respErrorPDOalreadyUsed = 400, /** 401 - PDO length exceeded */ CO_GTWA_respErrorPDOlengthExceeded = 401, /** 501 - LSS implementation- / manufacturer-specific error */ CO_GTWA_respErrorLSSmanufacturer = 501, /** 502 - LSS node-ID not supported */ CO_GTWA_respErrorLSSnodeIdNotSupported = 502, /** 503 - LSS bit-rate not supported */ CO_GTWA_respErrorLSSbitRateNotSupported = 503, /** 504 - LSS parameter storing failed */ CO_GTWA_respErrorLSSparameterStoringFailed = 504, /** 505 - LSS command failed because of media error */ CO_GTWA_respErrorLSSmediaError = 505, /** 600 - Running out of memory */ CO_GTWA_respErrorRunningOutOfMemory = 600 } CO_GTWA_respErrorCode_t; /** * Internal states of the Gateway-ascii state machine. */ typedef enum { /** Gateway is idle, no command is processing. This state is starting point * for new commands, which are parsed here. */ CO_GTWA_ST_IDLE = 0x00U, /** SDO 'read' (upload) */ CO_GTWA_ST_READ = 0x10U, /** SDO 'write' (download) */ CO_GTWA_ST_WRITE = 0x11U, /** SDO 'write' (download) - aborted, purging remaining data */ CO_GTWA_ST_WRITE_ABORTED = 0x12U, /** LSS 'lss_switch_glob' */ CO_GTWA_ST_LSS_SWITCH_GLOB = 0x20U, /** LSS 'lss_switch_sel' */ CO_GTWA_ST_LSS_SWITCH_SEL = 0x21U, /** LSS 'lss_set_node' */ CO_GTWA_ST_LSS_SET_NODE = 0x22U, /** LSS 'lss_conf_bitrate' */ CO_GTWA_ST_LSS_CONF_BITRATE = 0x23U, /** LSS 'lss_store' */ CO_GTWA_ST_LSS_STORE = 0x24U, /** LSS 'lss_inquire_addr' or 'lss_get_node' */ CO_GTWA_ST_LSS_INQUIRE = 0x25U, /** LSS 'lss_inquire_addr', all parameters */ CO_GTWA_ST_LSS_INQUIRE_ADDR_ALL = 0x26U, /** LSS '_lss_fastscan' */ CO_GTWA_ST__LSS_FASTSCAN = 0x30U, /** LSS 'lss_allnodes' */ CO_GTWA_ST_LSS_ALLNODES = 0x31U, /** print message 'log' */ CO_GTWA_ST_LOG = 0x80U, /** print 'help' text */ CO_GTWA_ST_HELP = 0x81U, /** print 'status' of the node */ CO_GTWA_ST_LED = 0x82U } CO_GTWA_state_t; #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO) || defined CO_DOXYGEN /* * CANopen Gateway-ascii data types structure */ typedef struct { /** Data type syntax, as defined in CiA309-3 */ char* syntax; /** Data type length in bytes, 0 if size is not known */ size_t length; /** Function, which reads data of specific data type from fifo buffer and * writes them as corresponding ascii string. It is a pointer to * #CO_fifo_readU82a function or similar and is used with SDO upload. For * description of parameters see #CO_fifo_readU82a */ size_t (*dataTypePrint)(CO_fifo_t *fifo, char *buf, size_t count, bool_t end); /** Function, which reads ascii-data of specific data type from fifo buffer * and copies them to another fifo buffer as binary data. It is a pointer to * #CO_fifo_cpyTok2U8 function or similar and is used with SDO download. For * description of parameters see #CO_fifo_cpyTok2U8 */ size_t (*dataTypeScan)(CO_fifo_t *dest, CO_fifo_t *src, CO_fifo_st *status); } CO_GTWA_dataType_t; #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO */ /** * CANopen Gateway-ascii object */ typedef struct { /** Pointer to external function for reading response from Gateway-ascii * object. Pointer is initialized in CO_GTWA_initRead(). * * @param object Void pointer to custom object * @param buf Buffer from which data can be read * @param count Count of bytes available inside buffer * @param [out] connectionOK different than 0 indicates connection is OK. * * @return Count of bytes actually transferred. */ size_t (*readCallback)(void *object, const char *buf, size_t count, uint8_t *connectionOK); /** Pointer to object, which will be used inside readCallback, from * CO_GTWA_init() */ void *readCallbackObject; /** Sequence number of the command */ uint32_t sequence; /** Default CANopen Net number is undefined (-1) at startup */ int32_t net_default; /** Default CANopen Node ID number is undefined (-1) at startup */ int16_t node_default; /** Current CANopen Net number */ uint16_t net; /** Current CANopen Node ID */ uint8_t node; /** CO_fifo_t object for command (not pointer) */ CO_fifo_t commFifo; /** Command buffer of usable size @ref CO_CONFIG_GTWA_COMM_BUF_SIZE */ char commBuf[CO_CONFIG_GTWA_COMM_BUF_SIZE + 1]; /** Response buffer of usable size @ref CO_GTWA_RESP_BUF_SIZE */ char respBuf[CO_GTWA_RESP_BUF_SIZE]; /** Actual size of data in respBuf */ size_t respBufCount; /** If only part of data has been successfully written into external * application (with readCallback()), then Gateway-ascii object will stay * in current state. This situation is indicated with respHold variable and * respBufOffset indicates offset to untransferred data inside respBuf. */ size_t respBufOffset; /** See respBufOffset above */ bool_t respHold; /** Sum of time difference from CO_GTWA_process() in case of respHold */ uint32_t timeDifference_us_cumulative; /** Current state of the gateway object */ CO_GTWA_state_t state; /** Timeout timer for the current state */ uint32_t stateTimeoutTmr; #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO) || defined CO_DOXYGEN /** SDO client object from CO_GTWA_init() */ CO_SDOclient_t *SDO_C; /** Timeout time for SDO transfer in milliseconds, if no response */ uint16_t SDOtimeoutTime; /** SDO block transfer enabled? */ bool_t SDOblockTransferEnable; /** Indicate status of data copy from / to SDO buffer. If reading, true * indicates, that response has started. If writing, true indicates, that * SDO buffer contains only part of data and more data will follow. */ bool_t SDOdataCopyStatus; /** Data type of variable in current SDO communication */ const CO_GTWA_dataType_t *SDOdataType; #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_NMT) || defined CO_DOXYGEN /** NMT object from CO_GTWA_init() */ CO_NMT_t *NMT; #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LSS) || defined CO_DOXYGEN /** LSSmaster object from CO_GTWA_init() */ CO_LSSmaster_t *LSSmaster; /** 128 bit number, uniquely identifying each node */ CO_LSS_address_t lssAddress; /** LSS Node-ID parameter */ uint8_t lssNID; /** LSS bitrate parameter */ uint16_t lssBitrate; /** LSS inquire parameter */ CO_LSS_cs_t lssInquireCs; /** LSS fastscan parameter */ CO_LSSmaster_fastscan_t lssFastscan; /** LSS allnodes sub state parameter */ uint8_t lssSubState; /** LSS allnodes node count parameter */ uint8_t lssNodeCount; /** LSS allnodes store parameter */ bool_t lssStore; /** LSS allnodes timeout parameter */ uint16_t lssTimeout_ms; #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG) || defined CO_DOXYGEN /** Message log buffer of usable size @ref CO_CONFIG_GTWA_LOG_BUF_SIZE */ char logBuf[CO_CONFIG_GTWA_LOG_BUF_SIZE + 1]; /** CO_fifo_t object for message log (not pointer) */ CO_fifo_t logFifo; #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_HELP) || defined CO_DOXYGEN /** Offset, when printing help text */ const char *helpString; size_t helpStringOffset; #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_LEDS) || defined CO_DOXYGEN /** CO_LEDs_t object for CANopen status LEDs imitation from CO_GTWA_init()*/ CO_LEDs_t *LEDs; uint8_t ledStringPreviousIndex; #endif } CO_GTWA_t; /** * Initialize Gateway-ascii object * * @param gtwa This object will be initialized * @param SDO_C SDO client object * @param SDOclientTimeoutTime_ms Default timeout in milliseconds, 500 typically * @param SDOclientBlockTransfer If true, block transfer will be set by default * @param NMT NMT object * @param LSSmaster LSS master object * @param LEDs LEDs object * @param dummy dummy argument, set to 0 * * @return #CO_ReturnError_t: CO_ERROR_NO or CO_ERROR_ILLEGAL_ARGUMENT */ CO_ReturnError_t CO_GTWA_init(CO_GTWA_t* gtwa, #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_SDO) || defined CO_DOXYGEN CO_SDOclient_t* SDO_C, uint16_t SDOclientTimeoutTime_ms, bool_t SDOclientBlockTransfer, #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_NMT) || defined CO_DOXYGEN CO_NMT_t *NMT, #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LSS) || defined CO_DOXYGEN CO_LSSmaster_t *LSSmaster, #endif #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_PRINT_LEDS) || defined CO_DOXYGEN CO_LEDs_t *LEDs, #endif uint8_t dummy); /** * Initialize read callback in Gateway-ascii object * * Callback will be used for transfer data to output stream of the application. * It will be called from CO_GTWA_process() zero or multiple times, depending on * the data available. If readCallback is uninitialized or NULL, then output * data will be purged. * * @param gtwa This object will be initialized * @param readCallback Pointer to external function for reading response from * Gateway-ascii object. See #CO_GTWA_t for parameters. * @param readCallbackObject Pointer to object, which will be used inside * readCallback */ void CO_GTWA_initRead(CO_GTWA_t* gtwa, size_t (*readCallback)(void *object, const char *buf, size_t count, uint8_t *connectionOK), void *readCallbackObject); /** * Get free write buffer space * * @param gtwa This object * * @return number of available bytes */ static inline size_t CO_GTWA_write_getSpace(CO_GTWA_t* gtwa) { return CO_fifo_getSpace(>wa->commFifo); } /** * Write command into CO_GTWA_t object. * * This function copies ascii command from buf into internal fifo buffer. * Command must be closed with '\n' character. Function returns number of bytes * successfully copied. If there is not enough space in destination, not all * bytes will be copied and data can be refilled later (in case of large SDO * download). * * @param gtwa This object * @param buf Buffer which will be copied * @param count Number of bytes in buf * * @return number of bytes actually written. */ static inline size_t CO_GTWA_write(CO_GTWA_t* gtwa, const char *buf, size_t count) { return CO_fifo_write(>wa->commFifo, buf, count, NULL); } #if ((CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG) || defined CO_DOXYGEN /** * Print message log string into fifo buffer * * This function enables recording of system log messages including CANopen * events. Function can be called by application for recording any message. * Message is copied to internal fifo buffer. In case fifo is full, old messages * will be owerwritten. Message log fifo can be read with non-standard command * "log". After log is read, it is emptied. * * @param gtwa This object * @param message Null terminated string */ void CO_GTWA_log_print(CO_GTWA_t* gtwa, const char *message); #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII_LOG */ /** * Process Gateway-ascii object * * This is non-blocking function and must be called cyclically * * @param gtwa This object will be initialized. * @param enable If true, gateway operates normally. If false, gateway is * completely disabled and no command interaction is possible. Can be connected * to hardware switch, for example. * @param timeDifference_us Time difference from previous function call in * [microseconds]. * @param [out] timerNext_us info to OS - see CO_process(). * * @return CO_ReturnError_t: CO_ERROR_NO on success or CO_ERROR_ILLEGAL_ARGUMENT */ void CO_GTWA_process(CO_GTWA_t *gtwa, bool_t enable, uint32_t timeDifference_us, uint32_t *timerNext_us); /** @} */ /* CO_CANopen_309_3 */ #ifdef __cplusplus } #endif /*__cplusplus*/ #endif /* (CO_CONFIG_GTW) & CO_CONFIG_GTW_ASCII */ #endif /* CO_GATEWAY_ASCII_H */