OpenEtherCATsociety / SOEM

Simple Open Source EtherCAT Master
Other
1.3k stars 668 forks source link

PDO cycle question #475

Closed orivesky closed 1 year ago

orivesky commented 3 years ago

Hello! I am new on the SOEM and Ethercat world, so I am a bit lost. I am trying to control a Mitsubishi servo via Ethercat and I run the slaveinfo program with these results:

Name:MR-JET-G-N1 Output size: 192bits Input size: 192bits State: 4 Delay: 0[ns] Has DC: 1 DCParentport:0 Activeports:1.0.0.0 Configured address: 1001 Man: 00000a1e ID: 00000305 Rev: 00010001 SM0 A:2000 L: 276 F:00010026 Type:1 SM1 A:2800 L: 276 F:00010022 Type:2 SM2 A:1000 L: 24 F:00010064 Type:3 SM3 A:1800 L: 24 F:00010020 Type:4 FMMU0 Ls:00000000 Ll: 24 Lsb:0 Leb:7 Ps:1000 Psb:0 Ty:02 Act:01 FMMU1 Ls:00000018 Ll: 24 Lsb:0 Leb:7 Ps:1800 Psb:0 Ty:01 Act:01 FMMUfunc 0:1 1:2 2:3 3:0 MBX length wr: 276 rd: 276 MBX protocols : 0c CoE details: 2f FoE details: 01 EoE details: 00 SoE details: 00 Ebus current: 0[mA] only LRD/LWR:0 End slaveinfo, close socket End program

I have tried on the Pre-OP operation to take some information via the ec_SDOread and it works perfectly, so I would say that the comm works perfectly. Now, as far as I know, I have to map the variables that I would like to read every cycle, right? I would like to have a custom mapping of the PDO input and Output. Can I do it on the input and output PDO separately? How could I do it? On the tutorial.txt, it says to use ec_SDOwrite and then using the PO2SOconfig. Is that correct?

Should I use ec_config_map(&IOmap) after making my custom PDO map, right? But when am I changing the value of IOmap?

Sorry, but I am a bit lost. Regards,

ArthurKetels commented 3 years ago

I am new on the SOEM and Ethercat world, so I am a bit lost. I am trying to control a Mitsubishi servo via Ethercat Recipe for disaster ???

Please read the available information on EtherCAT. First from ETG, then from your slave vendor manual, then here on this site on the issues section. Lots of the same questions pop up here.

First try a simple slave with digital IO, when you get the basics go for something more difficult like analog IO. Only then try do do the most difficult type of slaves "servo drives".

orivesky commented 3 years ago

Thank you. I have been reading a lot, and now it's more clear. However, when I am trying to map the PDO though the SDO with the ec_SDOwrite command, the slave does not change the value. The result is 1, which it would mean that the slave has received the command, but when I am doing a ec_SDOread I can see that the value does not change (it has always the default value). Do you have by any chance any clue?

I am trying as well to be in the OP sate, however when I am trying to change from Safe-OP to OP an error pops-up using the tutorial.txt sample, do you have any clue as well? Thanks a lot!

ArthurKetels commented 3 years ago

If you have code different than the sample code please post that. Also post the result of running your code with wireshark packet capture. Capture the whole sequence and save in native format (not txt). Then zip and post.

orivesky commented 3 years ago

Thank you for your comment ArthurKetels. However, I can't attach the wireshark packet, as my machine doesn't have user interface (only terminal) and I am having issues installing the software. I will figure out.

At the same time, I have made many other experiements, and I was able to use the ec_SDOwrite with a different index. It worked using the PDO settings index (0x1c32 and 0x1c33), however with other indexes still works (result is 1), but the index value doesn't change. My main guess is that there are some indexes that only work when you're in OP mode (could it be?). I am attaching my short code, maybe you could guess why the slave can't go to OP mode. Thank you ArthurKetels.

#include <stdio.h>
#include <string.h>
//#include <Mmsystem.h>
#include "osal.h"
#include "ethercat.h"

#define EC_TIMEOUTMON 500

char IOmap[4096];
int usedmem;

void SDOread (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, void *readOutput)
{
        int readSizeBytes = sizeof(readOutput);
        int resultado = ec_SDOread(slave, index, subindex, wholeSubIndex, &readSizeBytes, readOutput, EC_TIMEOUTSAFE);
        printf("\nResultado del ec_SDOread con index %x, subindex %x es igual a %d \n", index, subindex, resultado);
}

void SDOwrite_uint8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint8 writeOutput)
{
        int writeSizeBytes = sizeof(writeOutput);
        int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
        printf("\nResultado del ec_SDOwrite con index %x, subindex %x es igual a %d \n", index, subindex, resultado);
}

void SDOwrite_uint16 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint16 writeOutput)
{
        int writeSizeBytes = sizeof(writeOutput);
        int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
        printf("\nResultado del ec_SDOwrite con index %x, subindex %x es igual a %d \n", index, subindex, resultado);
}

void SDOwrite_uint32 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint32 writeOutput)
{
        int writeSizeBytes = sizeof(writeOutput);
        int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
        printf("\nResultado del ec_SDOwrite con index %x, subindex %x es igual a %d \n", index, subindex, resultado);
}

void SDOwrite_int8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, int8 writeOutput)
{
        int writeSizeBytes = sizeof(writeOutput);
        int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
        printf("\nResultado del ec_SDOwrite con index %x, subindex %x es igual a %d \n", index, subindex, resultado);
}

int main()
{
  /* initialise SOEM, bind socket to ifname */
  if (ec_init("eth1") > 0)
  {
    /* find and auto-config slaves */
    if ( ec_config_init(FALSE) > 0 )
    {
    printf("%d slaves found and configured.\n",ec_slavecount);

    uint16 u16bits;
        int8 i8bits;

    //PDO Config - 0x1C32h - 0x01h  Synchronization Type, 0 means Free run and 2 means DC Sync0
    u16bits = 0x0002;
    SDOwrite_uint16(1, 0x1c32, 0x01, FALSE, u16bits); //It works!

        //PDO Config - 0x1C33h - 0x01h  Synchronization Type, 0 means Free run and 2 means DC Sync0
    u16bits = 0x0002;
    SDOwrite_uint16(1, 0x1c33, 0x01, FALSE, u16bits); // It works!

//Operation mode of the servo (it doesnt work, it has always the default value and not the value 9)  --------------------------------------------------------------------------------
    // Value 9 is equal of cycling velocity mode
    i8bits = 9;
    SDOwrite_int8(1, 0x6060, 0x00, FALSE, i8bits);

// Let's go from Pre-OP, to Safe-OP and then to OP ----------------------------------------------------------------------------------------------------------------
    ec_configdc();
    usedmem = ec_config_map(&IOmap);
    printf("\n usedmem = %d \n", usedmem);
    ec_slave[0].state = EC_STATE_OPERATIONAL;
    ec_writestate(0);
    ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE);

    /*ec_send_processdata();            // This part is commented, however it doesn't work either 
    int wkc = ec_receive_processdata(EC_TIMEOUTRET);
    printf("\n Resultado de ec_receive_processdata: %d \n", wkc);
    ec_writestate(0);
    ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE);*/

    }

  }
  return 1;
}
ArthurKetels commented 3 years ago
void SDOread (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, void *readOutput)
{
        int readSizeBytes = sizeof(readOutput);

You can not do this in C, sizeof(readOutput) will give you the size of a void pointer which is 4 or 8 bytes depending on your platform.

If you want to make a custom mapping then you first have to look up in the ESI (xml file) of the slave what (and if) the objects are writable. Some slave do not allow to modify the mapping, you can only choose which mapping you want from a pre-selected combination.

Some objects are only writable in a certain state (pre-op, OP), or only via PDO mapping and not via SDO. Again the ESI file will tell you.

If you post the ESI file of your drive I could help.

orivesky commented 3 years ago

Thank you ArthurKetels. I thought that all objects could be modified via SDO. I am attaching here the ESI file. MELSERVO MR_JET_G_N1.zip

On the other hand, I have tried as well to go to OP state without chaging the PDO mapping and same thing....It always remains in Safe-OP. When I look on the servo 7-segment display, it seems that it's on OP operation just for a millisecond and then it comes back to Safe-OP (it's my guess). I tried as well the simple test, and same thing, however the simple test give me a comm error in the servo. I will do my best to give you a wireshark packet. Thank you again, I owe you a big one! :)

orivesky commented 3 years ago

Hi again Arthur. I am attaching the wiresharks of the simple_test (getting an error on the servo and not going to OP) and my test (no error but not going to OP either). Thanks a lot! :)

Wireshark.zip

ArthurKetels commented 3 years ago

From the ESI:

                <ESC>
                    <Reg0400>2498</Reg0400>
                    <Reg0420>1000</Reg0420>
                </ESC>

This is an instruction to program register 0x400 and 0x0420 (ESC watchdog see datasheet). If you do not do this (your responsibility!) eventually the firmware will kick you out, or the watchdog fires and resets the ESC. This is exactly what I see in the wireshark trace. You are in safe-op for a while and nothing is wrong, then all of a sudden the SyncManagers get reset and the PDO exchange fails (wkc = 0). The slave slate then falls back to pre-op+error. The Al status gives 0x001e which is "Invalid input configuration". This is by itself the wrong error message and should be 0x001f "Invalid watchdog configuration".

So this is a case of a sloppy user (you) and a sloppy firmware programmer (Mitsubishi).

In your test program you have another bug. You switch from safe-op to op without checking the state transition. First check if you are actually in safe-op, only then issue the op state change. Also you have to set-up a real time task that at set intervals exchanges the process data (PDO). This has to start at least before you request the state change to OP. See other threads here on how to do that.

orivesky commented 3 years ago

Thank you. This is funny, because on the datasheet there is nothing about the watchdog. There is nothing about the register 0x0400 and 0x0420, is this normal? I don't even know the wathdog cycle time, the only cycle time on the datasheet are the RxPDO and TxPDO cycle time which are totally different.

You were right about the safe-op to op transition, it's fixed! ;) Thanks!

EDIT: I implemented the following code, but I still have the issue. I would say that now it stays a bit more on OP mode than before, maybe some miliseconds more. I would say it's the timing difference of sending the data, but I have no idea how to fix it.

u16val = 2498;
ec_FPWR(ec_slave[slave].configadr, 0x0400, sizeof(u16val), &u16val, EC_TIMEOUTRET);
// set ESC watchdog counter
u16val = 1000;
ec_FPWR(ec_slave[slave].configadr, 0x0420, sizeof(u16val), &u16val, EC_TIMEOUTRET);
orivesky commented 3 years ago

Hi ArthurKetels. I have been trying out all day, but no lack. When the slave reaches the OP state, it comes up with an error. I implemented the watchdog code using the ec_FPWR, but nothing.

I am attaching, just in case the wireshark and the code bellow. Thanks a lot!

Wathdog.zip

#include <stdio.h>
#include <string.h>
//#include <Mmsystem.h>
#include "osal.h"
#include "ethercat.h"

#include <unistd.h>
#include <time.h>

#define EC_TIMEOUTMON 500

char IOmap[4096];
int usedmem;

void SDOread_uint8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint8 *readOutput)
{
    int readSizeBytes = sizeof(readOutput);
    int resultado = ec_SDOread(slave, index, subindex, wholeSubIndex, &readSizeBytes, readOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOread_uint8 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, *readOutput, resultado);
}

void SDOread_uint16 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint16 *readOutput)
{
    int readSizeBytes = sizeof(readOutput);
    int resultado = ec_SDOread(slave, index, subindex, wholeSubIndex, &readSizeBytes, readOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOread_uint16 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, *readOutput, resultado);
}

void SDOread_uint32 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint32 *readOutput)
{
    int readSizeBytes = sizeof(readOutput);
    int resultado = ec_SDOread(slave, index, subindex, wholeSubIndex, &readSizeBytes, readOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOread_uint32 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, *readOutput, resultado);
}

void SDOread_int8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, int8 *readOutput)
{
    int readSizeBytes = sizeof(readOutput);
    int resultado = ec_SDOread(slave, index, subindex, wholeSubIndex, &readSizeBytes, readOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOread_int8 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, *readOutput, resultado);
}

void SDOwrite_uint8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint8 writeOutput)
{
    int writeSizeBytes = sizeof(writeOutput);
    int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOwrite_uint8 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, writeOutput, resultado);
}

void SDOwrite_uint16 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint16 writeOutput)
{
    int writeSizeBytes = sizeof(writeOutput);
    int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOwrite_uint16 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, writeOutput, resultado);
}

void SDOwrite_uint32 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint32 writeOutput)
{
    int writeSizeBytes = sizeof(writeOutput);
    int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOwrite_uint32 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, writeOutput, resultado);
}

void SDOwrite_int8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, int8 writeOutput)
{
    int writeSizeBytes = sizeof(writeOutput);
    int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOwrite_int8 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, writeOutput, resultado);
}

int main()
{

  /* initialise SOEM, bind socket to ifname */
  if (ec_init("enp0s3") > 0)
  {
    /* find and auto-config slaves */
    if ( ec_config_init(FALSE) > 0 )
    {
    printf("%d slaves found and configured.\n",ec_slavecount);
// Pre-OP -----------------------------------------------------------------------------------------

    //uint8 u8bits;
//    uint16 u16bits;
   uint32 u32bits;

//    int8 i8bits;
    //int16 16bits;
    //int32 32 bits;

    //Watchdog -------------------------------------------------------------------------------------
uint16 u16val = 2498;
ec_FPWR(ec_slave[1].configadr, 0x0400, sizeof(u16val), &u16val, EC_TIMEOUTRET);
//set ESC watchdog counter
//u16val = 1000;
//ec_FPWR(ec_slave[1].configadr, 0x0410, sizeof(u16val), &u16val, EC_TIMEOUTRET);
// set ESC watchdog counter
u16val = 1000;
ec_FPWR(ec_slave[1].configadr, 0x0420, sizeof(u16val), &u16val, EC_TIMEOUTRET);

    //Check that we're on Pre.OP state
    ec_statecheck(0, EC_STATE_PRE_OP, EC_TIMEOUTSTATE * 4);

// Safe-OP ------------------------------------------------------------------------------------------
    ec_configdc();
    usedmem = ec_config_map(&IOmap);
    printf("\n usedmem = %d \n", usedmem);

    //check that we're on Safe-OP
    ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4);

// OP -------------------------------------------------------------------------------------------
    ec_slave[1].state = EC_STATE_OPERATIONAL;
    ec_send_processdata();
    ec_receive_processdata(EC_TIMEOUTRET);
    ec_writestate(1);

    do{
    ec_send_processdata();
        ec_receive_processdata(EC_TIMEOUTRET);
        ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE);
    }while(ec_slave[1].state != EC_STATE_OPERATIONAL);

    if(ec_slave[1].state == EC_STATE_OPERATIONAL)
    {
    // All slaves are in OP STATE!
    while(1)
    {
    ec_send_processdata();
    ec_receive_processdata(EC_TIMEOUTRET);
     }
    }
   }
  }
 printf("\nFINAL \n");
  return 1;
}
ArthurKetels commented 3 years ago
    do{
    ec_send_processdata();
        ec_receive_processdata(EC_TIMEOUTRET);
        ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE);
    }while(ec_slave[1].state != EC_STATE_OPERATIONAL);

In the wireshark trace you see that the do while loop is not doing what it is supposed to do. You want a short check period then a processdata exchange, repeat until state is OP. But because EC_TIMEOUTSTATE is very long it sticks there. Better use a timeout of 2ms or so. This is 2000. You also want to limit the number of cycles in the while loop, and exit with an error if the state has not changed after the cycle limit.

orivesky commented 3 years ago

Thank you ArthurKetels. I have tried out on the simple_test sample, but I'm still having an error. However, I think that the error is different, it doesn't say anymore "network error". In addition, it's curious, but I haven't add the watchdog code on the simple test, and it doesn't give me that error anymore (maybe the servo has stored the value and it doesn't need anymore???).

I am attaching the wireshark just in case. I will try out more things, thanks! ;) simple_test.zip

The error now I think it's on the servo mode operation (0x6060 RxPDO and 0x6061 TxPDO), which it says that it's not correct. The default value on the datasheet it says is 0, but everytime that I read at the beginning of the program is 8. It's a int8 data, I don't know why I am reading it wrong (it should be 0 instead of 8). Do you have by any chance a clue? In addition, when I am trying to modify the value with a SDOwrite, and then a SDOread in order to see if that value has changed, it doens't change, it always remains on 8... Thanks a lot!

ArthurKetels commented 3 years ago

When am SDO is mapped in a PDO it is no longer possible to manipulate it with a SDOwrite. In some slave implementations you can, but it gets overwritten right after the next PDO cyle. So when you map 0x6060 in the PDO then you need to set the values via the IOmap structure. That is why I urged you to make a separate task for process data exchange. Starting this at the pre-OP to safe-OP transition. This is the standard solution for servo drives.

orivesky commented 3 years ago

Gotcha! Thank you ArthurKetels. Using pointers I was able successfully to acess the IOmap :)

However, the error is still there, but I know the reason. I am attaching a small piece of the datasheet. It seems that the sync jitter need to be kept less than 2microseconds, otherwise the servo gives me an error. Is it possible to do that?

image

ArthurKetels commented 3 years ago

In DC sync0 mode the cycle clock is generated by hardware. The maximum jitter is expressed in ns rather than us. So this should be no problem.

The point is you are now operating in free-running mode. There should be no timing constraints other than the cycle watchdog timeout (approximately 100ms). However there could be hidden timing requirements, and if you not comply the drive will complain.

My advice is to rebuild your code to use DC sync0 and synchronize your PDO cycle with the sync0 at an appropriate interval. There are multiple post here on that subject and how to properly implement it.

orivesky commented 3 years ago

Hi Arthur, once again thank you for your reply. I have tried to add part of the code of the red_test. I added the thread ecatthread that synchronize the computer and DC time, but I haven't had luck. I have added the ec_dcsync0 function as well, but no luck either. Do you have an idea or function that could be useful? Thanks a lot!

msevnctkn commented 2 years ago

hi @orivesky . what did you about it. Did you drive succesfully? And can u share the codes?

nakarlsson commented 2 years ago

@orivesky , can this issue be closed?

JLBicho commented 2 years ago

Hi!

I don't know about @orivesky , but I'm having a problem with a similar servo.

Slave:1
 Name:MR-J5-G-RJN1
 Output size: 192bits
 Input size: 192bits
 State: 2
 Delay: 0[ns]
 Has DC: 1
 Configured address: 1001
 SyncManager: 
  SM0 A:2000 L: 276 F:00010026 Type:1
  SM1 A:2800 L: 276 F:00010022 Type:2
  SM2 A:1000 L:  24 F:00010064 Type:3
  SM3 A:1800 L:  24 F:00010020 Type:4
 Fieldbus Memory Management Unit: 
  FMMU0 Ls:00000000 Ll:  24 Lsb:0 Leb:7 Ps:1000 Psb:0 Ty:02 Act:01
  FMMU1 Ls:00000018 Ll:  24 Lsb:0 Leb:7 Ps:1800 Psb:0 Ty:01 Act:01
 MBX length wr: 276 rd: 276 MBX protocols : 0c
 CoE details: 2f FoE details: 01 EoE details: 00 SoE details: 00
PDO mapping according to CoE :
  SM2 outputs
     addr b   index: sub bitl value  data_type    name
  [0x0000.0] 0x6060:0x00 0x08
  [0x0001.0] 0x0000:0x00 0x08
  [0x0002.0] 0x6040:0x00 0x10
  [0x0004.0] 0x2D01:0x00 0x10
  [0x0006.0] 0x2D02:0x00 0x10
  [0x0008.0] 0x2D03:0x00 0x10
  [0x000A.0] 0x607A:0x00 0x20
  [0x000E.0] 0x60FF:0x00 0x20
  [0x0012.0] 0x2D20:0x00 0x20
  [0x0016.0] 0x6071:0x00 0x10
  SM3 inputs
     addr b   index: sub bitl value data_type    name
  [0x0018.0] 0x6061:0x00 0x08
  [0x0019.0] 0x0000:0x00 0x08
  [0x001A.0] 0x6041:0x00 0x10
  [0x001C.0] 0x2D11:0x00 0x10
  [0x001E.0] 0x2D12:0x00 0x10
  [0x0020.0] 0x2D13:0x00 0x10
  [0x0022.0] 0x6064:0x00 0x20
  [0x0026.0] 0x606C:0x00 0x20
  [0x002A.0] 0x60F4:0x00 0x20
  [0x002E.0] 0x6077:0x00 0x10

My problem is that it reaches OPERATIONAL state, but then it raises an error in the device that I am now able to see in the code. The code is similar to what @orivesky last posted, with the inclusion of the check and the timeout to 2ms.

#include <stdio.h>
#include <string.h>
//#include <Mmsystem.h>
#include "osal.h"
#include "ethercat.h"

#include <unistd.h>
#include <time.h>

#define EC_TIMEOUTMON 500

char IOmap[4096];
int usedmem;

void SDOread_uint8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint8 *readOutput)
{
    int readSizeBytes = sizeof(readOutput);
    int resultado = ec_SDOread(slave, index, subindex, wholeSubIndex, &readSizeBytes, readOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOread_uint8 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, *readOutput, resultado);
}

void SDOread_uint16 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint16 *readOutput)
{
    int readSizeBytes = sizeof(readOutput);
    int resultado = ec_SDOread(slave, index, subindex, wholeSubIndex, &readSizeBytes, readOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOread_uint16 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, *readOutput, resultado);
}

void SDOread_uint32 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint32 *readOutput)
{
    int readSizeBytes = sizeof(readOutput);
    int resultado = ec_SDOread(slave, index, subindex, wholeSubIndex, &readSizeBytes, readOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOread_uint32 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, *readOutput, resultado);
}

void SDOread_int8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, int8 *readOutput)
{
    int readSizeBytes = sizeof(readOutput);
    int resultado = ec_SDOread(slave, index, subindex, wholeSubIndex, &readSizeBytes, readOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOread_int8 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, *readOutput, resultado);
}

void SDOwrite_uint8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint8 writeOutput)
{
    int writeSizeBytes = sizeof(writeOutput);
    int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOwrite_uint8 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, writeOutput, resultado);
}

void SDOwrite_uint16 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint16 writeOutput)
{
    int writeSizeBytes = sizeof(writeOutput);
    int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOwrite_uint16 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, writeOutput, resultado);
}

void SDOwrite_uint32 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, uint32 writeOutput)
{
    int writeSizeBytes = sizeof(writeOutput);
    int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOwrite_uint32 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, writeOutput, resultado);
}

void SDOwrite_int8 (uint16 slave, uint16 index, uint8 subindex, boolean wholeSubIndex, int8 writeOutput)
{
    int writeSizeBytes = sizeof(writeOutput);
    int resultado = ec_SDOwrite(slave, index, subindex, wholeSubIndex, writeSizeBytes, &writeOutput, EC_TIMEOUTSAFE);
    printf("ec_SDOwrite_int8 con index %x, subindex %x y valor %d ha tenido un resultado %d \n", index, subindex, writeOutput, resultado);
}

int main(){
  /* initialise SOEM, bind socket to ifname */
    if (ec_init("enp0s31f6") > 0){
        /* find and auto-config slaves */
        if (ec_config_init(FALSE) > 0 ) {
            printf("%d slaves found and configured.\n",ec_slavecount);
            // Pre-OP -----------------------------------------------------------------------------------------

            // Watchdog -------------------------------------------------------------------------------------
            uint16 u16val = 2498;
            ec_FPWR(ec_slave[1].configadr, 0x0400, sizeof(u16val), &u16val, EC_TIMEOUTRET);
            u16val = 1000;
            ec_FPWR(ec_slave[1].configadr, 0x0420, sizeof(u16val), &u16val, EC_TIMEOUTRET);

            //Check that we're on PRE_OP state
            if(ec_statecheck(1, EC_STATE_PRE_OP,  EC_TIMEOUTSTATE * 4) == EC_STATE_PRE_OP){
                printf("Esclavo en estado PRE_OP.\n");
            }

            // Safe-OP ------------------------------------------------------------------------------------------
            ec_configdc();
            usedmem = ec_config_map(&IOmap);
            printf("IOMapMem = %d \n", usedmem);

            //Check that we're on SAFE_OP state
            if(ec_statecheck(1, EC_STATE_SAFE_OP,  EC_TIMEOUTSTATE * 4) == EC_STATE_SAFE_OP){
                printf("Esclavo en estado SAFE_OP.\n");
            }

            // OP -------------------------------------------------------------------------------------------
            ec_slave[1].state = EC_STATE_OPERATIONAL;
            ec_send_processdata();
            ec_receive_processdata(EC_TIMEOUTRET);
            ec_writestate(1);
            int chk = 0;
            do{
            ec_send_processdata();
                ec_receive_processdata(EC_TIMEOUTRET);
                ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE);
                chk++;
            }while(chk<200 && ec_slave[1].state != EC_STATE_OPERATIONAL);

            if(ec_slave[1].state == EC_STATE_OPERATIONAL){
                // All slaves are in OP STATE!
                while(1){
                    ec_send_processdata();
                    ec_receive_processdata(EC_TIMEOUTRET);
                    printf("Slave in state %d.\n", ec_slave[1].state);
                }
            }
            printf("Slave in state %d.\n", ec_slave[1].state);
        }
    }
    printf("\nFINAL \n");
    return 1;
}

I have some doubts regarding the while(1) part. Should I put something like ec_readstate(1) or ec_statecheck(...) to make sure the actual state is updating? Because the output of the code is always Slave in state 8., which is OPERATIONAL, but on the device it has an error.

Additionally, once in OP mode, I should be able to see the Inputs and Outputs like this, right?:

for(uint32 i = 0; i<ec_slave[0].Ibytes; i++){
    printf("%d",ec_slave[1].inputs[i]);
}
printf("\n");

Thank you for your time!

nakarlsson commented 1 year ago

Close due to inactivity

Bipin903 commented 4 months ago

Hi nakrlsson can I get simple code from your side like I have made a slave using Arduino UNO now I want to send data at any specific pdo how to do please it will be helpful for me to understand SOEM.