Technical questions about CIA402 and homecomp.comp on A6

More
8 months 2 weeks ago - 8 months 2 weeks ago #343121 by Marcos DC
@Andrax
I think we’re actually very close in spirit here, but there is one important distinction worth making.In the current code, the triggers for global_estop() are:
  • drive reports error, or
  • start watchdog timeout, or
  • run watchdog timeout.
All of these indicate a sequence / handshake failure (homing didn’t start in time, didn’t finish in time, or the drive signaled a fault). They are not, by themselves, evidence of uncontrolled or runaway motion.So from an industrial point of view, this logic is more a process watchdog / fault handling than a true safety watchdog. Using a global E-Stop here is a very conservative choice (and understandable for a “fail-safe” CNC-style approach), but conceptually it mixes two layers:
  • Fault handling: homing failed, timeout, drive error → abort sequence, raise alarm, inhibit motion.
  • Safety / E-Stop: hazardous or potentially uncontrolled motion → immediate safety stop (usually hardware/ STO).
That’s why I was suggesting that most of these cases could be handled as axis/sequence faults rather than a global E-Stop, unless there is real evidence of dangerous or uncontrolled motion (e.g. runaway, loss of feedback, velocity growing when it shouldn’t, etc.).So the current logic is consistent as a conservative “stop everything on any homing failure”, but the triggers themselves are not proof of uncontrolled motion — they are proof that the homing sequence didn’t progress as expected.
Last edit: 8 months 2 weeks ago by Marcos DC.

Please Log in or Create an account to join the conversation.

More
8 months 2 weeks ago #343123 by Hakan
You don't do estop in the component.
Have an ouput pin flag for this condition and connect that to machine estop pin if you like to,

Please Log in or Create an account to join the conversation.

More
8 months 2 weeks ago #343127 by Marcos DC
I think there is a small but important distinction here between a process/sequence watchdog and a true safety watchdog.

A true safety watchdog is about detecting potentially dangerous or uncontrolled physical motion and forcing the system into a safe state (typically STO / safety stop), for example:

If |actual_velocity| > Vmax_safe when the axis should not be moving → trigger STO

If |pos_cmd - pos_fb| grows beyond a safe limit → trigger STO

If encoder feedback is lost while moving → trigger STO

If an axis moves while it is not enabled → trigger STO

If the real-time servo thread watchdog trips → trigger STO

If a safety PLC reports an invalid condition → trigger STO

These kinds of checks usually live in the safety layer (drive safety functions, safety PLC, safety relays, or core safety logic), not inside a process component.

What the current code is doing is more a sequence/process watchdog: it detects that homing did not start in time, did not finish in time, or the drive reported an error. That’s important, but it’s not the same as detecting unsafe or uncontrolled motion.

So I agree with Hakan’s point: the component should probably just signal a fault condition via an output pin, and the machine/safety layer can decide whether that becomes a controlled stop, an axis fault, or in some cases a real E-Stop.

Please Log in or Create an account to join the conversation.

More
8 months 6 days ago #343550 by andrax
I have continued to work on this issue.
Since I don't know “C,” I asked chatgpt for help. Unfortunately, with limited success. On the one hand, chatgpt provides good suggestions for implementation, but on the other hand, it suffers from Alzheimer's. It constantly forgets important components when creating the code. 
So it's useless as a programming aid.
Nevertheless, after many attempts, I managed to create a code that basically corresponds to my ideas of the logical sequence. Unfortunately, it cannot be created and crashes with massive errors. 
Could someone please help me troubleshoot this?
Code:
component cia402_safety_homecomp "cia402 homing module"; description """ A homing module for cia402 devices buildable with halcompile. This module should be used with the provided cia402.comp \\fBHOMING_BASE\\fR must be #defined and must point to a valid homing.c file, an example of a customized homing module is built. This module creates input hal pins joint.n.request-cia-homing that enables an alternate joint homing state machine for requested joints. A hal output pin joint.N.is_cia-homing verifies selection" If \\fBHOMING_BASE\\fR is not #defined, an actual homing scheme is \\fBnot\\fR implemented but all necessary functions are included as skeleton code. (All joints are effectively homed at all times and cannot be unhomed). See the source code file: src/emc/motion/homing.c for the baseline implementation that includes all functions for the default \\fBhomemod\\fR module. This cia402 homing component can be built and installed with halcompile and then substituted for the default homing module (\\fBhomemod\\fR) using: Sor by inifile setting: \\fB[EMCMOT]HOMEMOD=user_homecomp\\fR \\fBNote:\\fR If using a deb install: 1) halcompile is provided by the package linuxcnc-uspace-dev """; pin out bit is_module = 1; // required by halcompile pin out bit homing_error; // new pin to signal homing error pin in float cia_homing_timer1; // Timer for homing start timeout pin in float cia_homing_timer2; // Timer for movement check timeout license "GPL"; author "me and chatgpt"; option homemod; option extra_setup; /* To incorporate default homing.c file from a local git src tree: ** enable #define HOMING_BASE set to the path to the current homing.c file. */ #define HOMING_BASE /home/ich/linuxcnc/Sicherung/cia402/homing.c #define STR(s) #s #define XSTR(s) STR(s) #include "motion.h" #include "homing.h" static char *home_parms; RTAPI_MP_STRING(home_parms, "Example home parms"); // EXTRA_SETUP executed before rtapi_app_main() EXTRA_SETUP() {     rtapi_print_msg(RTAPI_MSG_ERR, "HomeComp Started\n");     return 0; } //===================================================================== #ifdef HOMING_BASE #define USE_HOMING_BASE XSTR(HOMING_BASE) // Disable duplicate symbols from homing.c #define CUSTOM_HOMEMODULE #include USE_HOMING_BASE // CIA Status Word typedef union {     struct     {         unsigned char ReadyToSwitchOn : 1;         unsigned char SwitchOn : 1;         unsigned char OperationEnabled : 1;         unsigned char Fault : 1;         unsigned char VoltageEnabled : 1;         unsigned char QuickStop : 1;         unsigned char SwitchOnDisabled : 1;         unsigned char Warning : 1;         unsigned char keep1 : 1;         unsigned char Remote : 1;         unsigned char TargetReached : 1;         unsigned char bit11 : 1;         unsigned char HomingCompletedOutput : 1;         unsigned char keep2 : 1;         unsigned char keep3 : 1;         unsigned char HomingComplete : 1;     } b;     hal_u32_t Word; } Status_t; typedef struct {     bool request_cia_homing;     bool is_cia_homing;     bool start_cia_homing;     // CIA402 pins     Status_t Status;     float timer1; // Timer for start timeout     float timer2; // Timer for movement check     int step;     // safety state machine step } cia_home_local_data; static cia_home_local_data ciaH[EMCMOT_MAX_JOINTS]; // Data for per-joint CIA-Homing HAL pins typedef struct {     hal_bit_t *request_cia_homing;     hal_bit_t *is_cia_homing;     hal_bit_t *start_cia_homing;     hal_bit_t *homing_error;     hal_u32_t *statusword;     hal_float_t *timer1;     hal_float_t *timer2; } cia_one_joint_home_data_t; typedef struct {     cia_one_joint_home_data_t cia_jhd[EMCMOT_MAX_JOINTS]; } cia_all_joints_home_data_t; static cia_all_joints_home_data_t *cia_joint_home_data = 0; //------------------------------------------------------------- static int cia_makepins(int id, int njoints) {     int jno, retval;     cia_one_joint_home_data_t *addr;     cia_joint_home_data = hal_malloc(sizeof(cia_all_joints_home_data_t));     if (!cia_joint_home_data)     {         rtapi_print_msg(RTAPI_MSG_ERR, "HOMING: malloc failed\n");         return -1;     }     retval = 0;     for (jno = 0; jno < njoints; jno++)     {         addr = &(cia_joint_home_data->cia_jhd[jno]);         retval += hal_pin_bit_newf(HAL_IN, &(addr->request_cia_homing), id,                                    "joint.%d.request-cia-homing", jno);         retval += hal_pin_bit_newf(HAL_OUT, &(addr->is_cia_homing), id,                                    "joint.%d.is-cia-homing", jno);         retval += hal_pin_bit_newf(HAL_IO, &(addr->start_cia_homing), id,                                    "joint.%d.start-cia-homing", jno);         retval += hal_pin_bit_newf(HAL_OUT, &(addr->homing_error), id,                                    "joint.%d.homing-error", jno);         retval += hal_pin_u32_newf(HAL_IN, &(addr->statusword), id,                                    "joint.%d.cia-statusword", jno);         retval += hal_pin_float_newf(HAL_IN, &(addr->timer1), id,                                      "joint.%d.homing-timer1", jno);         retval += hal_pin_float_newf(HAL_IN, &(addr->timer2), id,                                      "joint.%d.homing-timer2", jno);     }     return retval; } //------------------------------------------------------------- static void cia_read_homing_in_pins(int njoints) {     int jno;     cia_one_joint_home_data_t *addr;     for (jno = 0; jno < njoints; jno++)     {         addr = &(cia_joint_home_data->cia_jhd[jno]);         ciaH[jno].request_cia_homing = *(addr->request_cia_homing);         ciaH[jno].Status.Word = *(addr->statusword);         ciaH[jno].timer1 = *(addr->timer1);         ciaH[jno].timer2 = *(addr->timer2);     } } //------------------------------------------------------------- static void cia_write_homing_out_pins(int njoints) {     int jno;     cia_one_joint_home_data_t *addr;     for (jno = 0; jno < njoints; jno++)     {         addr = &(cia_joint_home_data->cia_jhd[jno]);         *(addr->is_cia_homing) = ciaH[jno].is_cia_homing;         *(addr->start_cia_homing) = ciaH[jno].start_cia_homing;         *(addr->homing_error) = homing_error; // global error flag     } } //------------------------------------------------------------- static int cia_1joint_state_machine(int joint_num) {     typedef enum     {         CIA_IDLE = 0,         CIA_BEGIN = 1,         CIA_START_WAIT = 2,         CIA_RUNNING = 3,         CIA_FINISHED = 4,     } cia_home_state_t;     static cia_home_state_t chomestate[EMCMOT_MAX_JOINTS] = {0};     static float t_start[EMCMOT_MAX_JOINTS] = {0};     static float t_run[EMCMOT_MAX_JOINTS] = {0};     cia_home_state_t nextcstate;     if (H[joint_num].home_state == HOME_IDLE)         return 0;     if ((H[joint_num].home_state == HOME_START) && (chomestate[joint_num] == CIA_IDLE))     {         H[joint_num].homing = 1;         H[joint_num].homed = 0;         chomestate[joint_num] = CIA_BEGIN;         ciaH[joint_num].step = 0;         t_start[joint_num] = 0;         t_run[joint_num] = 0;         homing_error = 0;     }     switch (chomestate[joint_num])     {     case CIA_BEGIN:         // initialize parameters         rtapi_print("CIA homing: init parameters\n");         t_start[joint_num] = 0;         t_run[joint_num] = 0;         chomestate[joint_num] = CIA_START_WAIT;         break;     case CIA_START_WAIT:         t_start[joint_num] += H[joint_num].servo_period;         // check if CIA402 bit9 (Remote) set to start homing         if (ciaH[joint_num].Status.b.Remote)         {             chomestate[joint_num] = CIA_RUNNING;         }         else if (t_start[joint_num] > ciaH[joint_num].timer1)         {             rtapi_print("CIA homing: start timeout\n");             homing_error = 1;             H[joint_num].homing = 0;             H[joint_num].home_state = HOME_IDLE;             chomestate[joint_num] = CIA_IDLE;             return 0;         }         break;     case CIA_RUNNING:         t_run[joint_num] += H[joint_num].servo_period;         // safety check: axis movement         if (!H[joint_num].joint_moving) // Example: joint_moving needs to be replaced by real check         {             rtapi_print("CIA homing: axis not moving, error\n");             homing_error = 1;             H[joint_num].homing = 0;             H[joint_num].home_state = HOME_IDLE;             chomestate[joint_num] = CIA_IDLE;             return 0;         }         // check for CIA homing completion         if (ciaH[joint_num].Status.b.HomingCompletedOutput &&             ciaH[joint_num].Status.b.HomingComplete)         {             chomestate[joint_num] = CIA_FINISHED;         }         else if (t_run[joint_num] > ciaH[joint_num].timer2)         {             rtapi_print("CIA homing: movement timeout\n");             homing_error = 1;             H[joint_num].homing = 0;             H[joint_num].home_state = HOME_IDLE;             chomestate[joint_num] = CIA_IDLE;             return 0;         }         break;     case CIA_FINISHED:         rtapi_print("CIA homing complete\n");         ciaH[joint_num].start_cia_homing = 0;         H[joint_num].homing = 0;         H[joint_num].homed = 1;         H[joint_num].home_state = HOME_IDLE;         chomestate[joint_num] = CIA_IDLE;         return 0;     case CIA_IDLE:     default:         break;     }     ciaH[joint_num].is_cia_homing = (chomestate[joint_num] != CIA_IDLE);     return 1; } //------------------------------------------------------------- // Augment base functions int homing_init(int id, double servo_period, int n_joints, int n_extrajoints, emcmot_joint_t *pjoints) {     int retval = base_homing_init(id, servo_period, n_joints, n_extrajoints, pjoints);     retval += cia_makepins(id, n_joints);     return retval; } void read_homing_in_pins(int njoints) {     base_read_homing_in_pins(njoints);     cia_read_homing_in_pins(njoints); } void write_homing_out_pins(int njoints) {     base_write_homing_out_pins(njoints);     cia_write_homing_out_pins(njoints); } // do_homing with CIA safety bool do_homing(void) {     int joint_num;     int homing_flag = 0;     bool beginning_allhomed = get_allhomed();     do_homing_sequence();     for (joint_num = 0; joint_num < all_joints; joint_num++)     {         if (!H[joint_num].joint_in_sequence) continue;         if (!GET_JOINT_ACTIVE_FLAG(&joints[joint_num])) continue;         if (ciaH[joint_num].is_cia_homing)             homing_flag += cia_1joint_state_machine(joint_num);         else             homing_flag += base_1joint_state_machine(joint_num);     }     homing_active = (homing_flag > 0) ? 1 : 0;     if (!beginning_allhomed && get_allhomed())     {         homing_active = 0;         return 1;     }     return 0; } //===================================================================== // EXPORT_SYMBOLS EXPORT_SYMBOL(homeMotFunctions); EXPORT_SYMBOL(homing_init); EXPORT_SYMBOL(do_homing); EXPORT_SYMBOL(get_allhomed); EXPORT_SYMBOL(get_homed); EXPORT_SYMBOL(get_home_is_idle); EXPORT_SYMBOL(get_home_is_synchronized); EXPORT_SYMBOL(get_home_needs_unlock_first); EXPORT_SYMBOL(get_home_sequence); EXPORT_SYMBOL(get_homing); EXPORT_SYMBOL(get_homing_at_index_search_wait); EXPORT_SYMBOL(get_homing_is_active); EXPORT_SYMBOL(get_index_enable); EXPORT_SYMBOL(read_homing_in_pins); EXPORT_SYMBOL(do_home_joint); EXPORT_SYMBOL(do_cancel_homing); EXPORT_SYMBOL(set_unhomed); EXPORT_SYMBOL(set_joint_homing_params); EXPORT_SYMBOL(update_joint_homing_params); EXPORT_SYMBOL(write_homing_out_pins); // Clean up optional #undef XSTR #undef STR #undef HOMING_BASE #undef USE_HOMING_BASE #undef CUSTOM_HOMEMODULE #endif // HOMING_BASE

Please Log in or Create an account to join the conversation.

More
8 months 5 days ago #343593 by Marcos DC
@Andrax
I’m currently restructuring my CiA402 integration to make it more modular and deterministic before going back to real EtherCAT hardware. My goal is to formalize the state handling and timing assumptions first, instead of patching behavior incrementally.

I think studying industrial PLC CiA402 homing FBs is a good idea conceptually. Even if the source code is compiled, the documented state diagrams and the Execute/Busy/Done/Error behavior usually define the real logic contract.

If you have access to an EtherCAT PLC with CiA402 homing FBs, could you share the documented state diagram or the Execute/Busy/Done/Error sequence? That would help us model a proper state machine for LinuxCNC.

I plan to open a separate thread focused specifically on formalizing the homing state machine. If you're interested in contributing your PLC perspective there, I think that would be very valuable.

Please Log in or Create an account to join the conversation.

More
8 months 5 days ago #343622 by andrax
No, I haven't.I posted a query about this on the German sps forum. The response was arrogant, and I don't expect any help.We're on our own. If you have working code to test, I'd be happy to help

Please Log in or Create an account to join the conversation.

Time to create page: 0.151 seconds
Powered by Kunena Forum