Close

working with libmodbus

A project log for Grizzly G0704 CNC Conversion

Joining the ranks of the Chinese made Grizzy G0704/BF20 conversions.

charliexcharliex 02/05/2016 at 21:031 Comment

interfacing to libmodbus is fairly straightforward. i'm leaving out the error checking and what not, since the whole code is on github

opening a connection

   modbus_t *ctx = modbus_new_rtu ( "COM1", 9600, 1,8 ,1);
   if ( ctx) { 
        if(modbus_connect ( ctx ) == -1 ) {
          modbus_free ( ctx );
          ctx = NULL;
        }
  }

set as slave

if ( modbus_set_slave ( ctx, 1 ) == -1 ) {
     // error
}

read a register

if ( modbus_read_registers ( ctx, status_monitor_2_addr, 1, ( uint16_t* ) &status_monitor_2 ) == -1 ) {
    //error
}

write a register

 if ( modbus_write_register ( ctx, run_stop_addr, 1 ) == -1 ) {
    //error
}

that is more or less it.

a modbus RTU message looks like this

            // 01 10 09 1b 00 02 04 02 58 00 01 5a 66

            // breakdown
            // 01 node address
            // 10 command (write registers)
            // 09 1b register to write to 9.26
            // 00 02 number of registers to write, consecutive
            // 04 amount of data to write
            // 02 58 00 01 data to send 0258 to 9.26 and 0001 to 9.27
            // 5a 66 crc

from the VFD's manual it has the following info

       /*
        Status Monitor 2 - Memory Address h2101
        	Address	Bit(s) Val	AC Drive Status
        	Bit(s)	Binary(Dec)
        	-------	------		---------------
        	0 and 1	00 (0)		Drive operation stopped(STOP)
        			01 (1)		Run to Stop transition
        			10 (2)		Standby
        			11 (3)		Drive operation running(RUN)
        	2		1 (4)		JOG active
        	3 and 4	00 (0)		Rotational direction forward(FWD)
        			01 (8)		REV to FWD transition
        			10 (16)		FWD to REV transition
        			11 (24)		Rotational direction reverse(REV)
        	5 ~7	N/A			Reserved
        	8		1 (32)		Source of frequency determined by serial comm interface (P4.00 = 5)
        	9		1 (64)		Source of frequency determined by AI terminal(P4.00 = 2, 3, 4 or 6)
        	10		1 (128)		Source of operation determined by serial comm interface (P3.00 = 3 or 4)
        	11		1 (256)		Parameters have been locked (P9.07 = 1)
        	12		N/A			Copy command eable(sp?)
        */

a quick bitfield, being wary of how compilers pack bitfiles and differences in systems.

        typedef struct  statusMonitor2_tag {
            uint16_t	drive_state : 2;
            uint16_t	jog_active : 1;
            uint16_t	direction : 2;
            uint16_t	reserved_1 : 3;
            uint16_t	freq_src_1 : 1;
            uint16_t	freq_src_2 : 1;
            uint16_t	freq_src_3 : 1;
            uint16_t	params_locked : 1;
            uint16_t	reserved_2 : 1;
        } statusMonitor2;
        statusMonitor2 status_monitor_2;

and the VFD manual again to get the registers, hackaday has some issues with colouring.

Modbus is strange (to me anyway) that the decimal is in a range, may map to a memory address so you have to know which range the register is in, then add the register value to that range. For the status monitors, seems to be 40000 + they're indexed at 1, versus 0, so it is +1, therefore Status Monitor 1 is 0x2100, which to get the decimal address is 40000+0x2100+1 = 48449

Octal and Hex are just the address.

	Durapulse GS3 status registers form CH5 of manual

	Name					Hex  Dec   Oct   Mode
	Status Monitor 1			2100 48449 20400 RO
	Status Monitor 2			2101 48450 20401 RO
	Frequency Command F			2102 48451 20402 RO
	Output Frequency H			2103 48452 20403 RO
	Output Current A			2104 48453 20404 RO
	DC Bus Voltage d			2105 48454 20405 RO
	Output Voltage U			2106 48455 20406 RO
	Motor RPM			        2107 48456 20407 RO
	Scale Frequency(Low Word)	        2108 48457 20410 RO
	Scale Frequency(High Word)	        2109 48458 20411 RO
	Power Factor Angle			210A 48459 20412 RO
	% Load					210B 48460 20413 RO
	PID Setpoint				210C 48461 20414 RO
	PID Feedback Signal(PV)		        210D 48462 20415 RO
	Firmware Version			2110 48465 20420 RO


quick macro

#define MODBUS_CMD(maj,minor) ((uint16_t)(maj<<8)+minor)
some enums for commands i'm using.
        // commands for GS3 DuraPulse VFD
        enum modbuscmds  {
            hz_addr             = MODBUS_CMD ( 9, 26 ),
            run_stop_addr       = MODBUS_CMD ( 9, 27 ),

            control_freq_addr   = MODBUS_CMD ( 4, 0 ),
            control_drive_addr  = MODBUS_CMD ( 3, 0 ),

            status_monitor_2_addr = MODBUS_CMD ( 0x21, 01 ) //(BD42)

        } ;
so if we do
 if ( modbus_read_registers ( ctx, status_monitor_2_addr, 1, ( uint16_t* ) &status_monitor_2 ) == -1 ) {
}
and it goes well , status_monitor_2.drive_state will be the state of the drive.

       enum {
            DRIVE_STOPPED,
            DRIVE_RUN_TO_STOP,
            DRIVE_STANDBY,
            DRIVE_RUNNING
        };
turning the motor on and off is just 0 for off, 1 for on to be passed into the last argument

    if ( modbus_write_register ( ctx, run_stop_addr, 1 ) == -1 ) {
}

and that is pretty much it.

Discussions

[deleted]

[this comment has been deleted]

charliex wrote 04/15/2016 at 01:02 point

as noted it's all on github, the parity is dependent on the vf+interface so you have to match whatever you set it at.

  Are you sure? yes | no