Modbus RTU Force Coil (5)


c3lighting

Recommended Posts

Our custom LED light control PCBA with PIC24FJ64GA104 using RS-485 to Ethernet adapter connected to PC. Successfully sending Force Coil (5) commands to PIC from DaqFactory and controlling the PCBA.

Using Modbus RTU protocol.

I want a button to use Force Coil (5) and send FF hex the first time it is pressed and 00 hex the second time it is pressed. I thougt that using the Toggle Between would handle this but apparently I was wrong.

Using the Button Component with Action set to Toggle Between with values 0 to 1. The following is transmitted and observed in the Comm Monitor:

01 05 00 0C 00 00 0D C9 (all in hex)

Modbus Protocol Specification V1.1b: Address (DaqFactory Device#), Function, Output Address Hi, Output Address Lo (DaqFactory Ch#), Output Value Hi, Output Value Lo, CRC, CRC

I thought that the output value Lo would change from 00 to FF hex as stated in the Modbus spec? Or that the output value Lo would go from 0 to 1? The output in the Comm Monitor and in my PIC controller is always the same 01 05 00 0C 00 00 0D C9 when the button is pushed. This is Modbus write coil OFF (00)

If I change the Toggle Between with values 1 to 0 I get the following in the Comm Monitor:

\x01 \x05 \x00 \x0c \xFF \x00L9

If I change to 01 05 00 01 FF 00 DD FA hex the Modbus ON command is sent (FF).

Regards,

Bob Taylor

Link to comment
Share on other sites

DAQFactory is actually working correctly. First, its important to understand that toggle between looks at the current value for the channel to determine which state to switch to. If no value exists yet for the channel, it tries to set the channel to whatever the first value indicated in the toggle between. That's why you get 01 05 00 0C 00 00 0D C9, which is the correct output to set channel 12 to 0. If you repetitively click the button before your device has confirmed that the output has changed, DAQFactory will send the identical command. This is to avoid someone clicking a button a bunch of times because they don't realize there is a slight delay in comms (or just accidently double clicking) and then turning the channel on and then immediately back off again (or vica-versa). When you switch the order of the toggle values to 1 and 0, DAQFactory tries to output 1, and \x01 \x05 \x00 \x0c \xFF \x00L9 is the correct output for this. The L9 is the CRC represented as ASCII (because you didn't check "Display all ASCII chars as codes". It just happens that the 0 version doesn't have any values between 32 and 127, the valid ASCII range.

Now, as to why its not working correctly, most likely your device doesn't have a channel 12. The command you said worked, 01 05 00 01 FF 00 DD FA, is for channel 1, not 12.

If you are testing your device, you might consider using the command alert window along with the comm monitor (perhaps docked separately) to see what is happening. For example, to set channel 12 to 1, you'd do:

device.myDevice.forceCoil(1, 12, 1)

(change myDevice to your device name).

Link to comment
Share on other sites

Thanks for the information and solutions - I changed it to display all ASCII chars as codes and that solved my misunderstanding. I've been using the command alert along with the comm monitor.

Since I am writing the firmware for my PIC the response to force coil from my PCBA back to DaqFactory will be an echo of the command sent (based on the Modbus protocol as I understand it). I assume that I need to use Read Coil Status (1) (Modbus 01 (0x01) Read Coils) and check the condition of each of my switch/LED states.

My project - PCBA LED dimmer control PCBA is quite simple. There are 5 switches with an ON/OFF button.

100% = switch 6 & LED 6

80% = switch 5 & LED 5

60% = switch 4 & LED 4

40% = switch 3 & LED 3

20% = switch 2 & LED 2

On/Off switch 1 & LED 1

Using Modbus force coil I am controlling these switches via my existing firmware. My goal is to use DaqFactory to control my LED Dimmer. The control is working but I am not sure how to get the component LEDs in DaqFactory to read my PCBA LED status and change LED colors in DaqFactory (from Green to Red). I assume that I will need to use Read Coils (1) to check the status of my PCBA but I am not sure how to write the code in DaqFactory to control the LED component. I phoned in and someone remote controlled my PC but it was too fast for me to follow. Can you point me to any tutorials to help with this in your docs? I am using the Userguide, Modbus Tour, and Serialguide pdfs.

Link to comment
Share on other sites

Thanks for the information. I want to Read Coil Status only after a Force Coil (using one of my 5 buttons: 100%, 80%, 60%, 40%, or 20%). I only want the Read Coil Status to occur once. Would I use the channel event or write a sequence?

Link to comment
Share on other sites

Create the channel, set the timing to 0, then in the sequence do:

read(myChannel)

Why wouldn't you just let it read every second or so? Then, if you changed the state outside of DAQFactory, say with a physical button, then the LED in DAQFactory would update.

Link to comment
Share on other sites

Thanks for the information and quick response. The reason I am trying to minimize the communication is due to the fact that my PCBA with a PIC controller is the master for my RS-485 (9-bit UART2) network of 11 more LED dimmers and 2 more PWM controllers. The PCBA LED Dimmer that is communicating with DaqFactory (UART1) is acting as the Master/Coordinator for the other (UART2) RS-485 9-bit network using our own protocol. The 9-bit mode is so that multiple slaves can be on the same bus without conflict. I am working out the issues with my firmware to deal with UART1 amd UART2 (both interrupt driven) being able to handle the communication load. I would like to try the method you mentioned above because it will put the minimum load on my firmware until I work out the timing issues with my UART2 9-bit network.

Question: The Modbus Read Coils (1) can read from 1 to 2000 contiguous status of coils in a remote device. Request example: Function 01, Starting Address Hi 00, Starting Address Lo 5, Quantity of Ouputs Hi 00, Quantity of Outputs Lo 5 (all in hex),

The response from my PCBA would give the status of each of the 6 bits 1 = ON and 0 = OFF. I know that bit packing is not the most effecient way to deal with DaqFactory but if I wanted to us this method to minimize my communiation load how would I use either a script or expression to mask out unwanted bits and test a bit for ON or OFF? I know how to do this in C and assume that in DaqFactory I can AND a mask variable with the results of the channel read to strip out the bit I want and then test it in an IF or SWITCH (for example). My question is how would this script or expression look in DaqFactory? Or can you point me to a tutorial or example? The goal would be to update all of my LEDs with one channel read using this method to minimize communication with my PCBA.

Thanks again for your support.

Link to comment
Share on other sites

Actually bit packing is fine in DAQFactory. When we spoke on the phone, I was suggesting against bit packing because I thought it would be easier for you to implement on the PIC side, not the DAQFactory side. DAQFactory takes care of all the bit packing and unpacking for Modbus for you. So, if you want to read coils 3 through 23, just create a channel for each, specifying channel #'s 3-23, and provided the channels have the same timing and offset, it will make a single query for those 21 values and unpack the response and place the bits in the appropriate channels. You can also use the readCoilStatus() function and request multiple bits, and it will return an array of the bits, already unpacked for you.

Link to comment
Share on other sites

Thanks for the input and suggestions. I am able to control the LED light panels via DaqFactory - my PCBA (PIC). Also successfully using Read Coil Status to get the status of my PCBA. However, I need to use the data byte of the response sent to DaqFactory from my PCBA as a TRUE / FALSE logic check to turn the DaqFactory LEDs on or off. I have tried document searches and forum searches but have been unable to find anything pertaining to readCoilStatus() function that returns an array of bits. Thanks for your help and long suffering with us who are new to Modbus and DaqFactory.

Link to comment
Share on other sites

01 (0x01) Read Coils (From Modbus Protocol)

This function code is used to read from 1 to 2000 contiguous status of coils in a remote device. The Request PDU specifies the starting address, i.e. the address of the first coil specified, and the number of coils. In the PDU Coils are addressed starting at zero. Therefore coils numbered 1-16 are addressed as 0-15. The coils in the response message are packed as one coil per bit of the data field. Status is indicated as 1= ON and 0= OFF. The LSB of the first data byte contains the output addressed in the query. The other coils follow toward the high order end of this byte, and from low order to high order in subsequent bytes.

If the returned output quantity is not a multiple of eight, the remaining bits in the final data byte will be padded with zeros (toward the high order end of the byte). The Byte Count field specifies the quantity of complete bytes of data.

Command from DaqFactory: 00 01 00 01 00 01 AD DB 100% 80% 60% 40% 20% On N/A N/A

My response: Device 00 Function 01 Byte count 01 Output status 14 CRC 50 7b where 14 is the status of my 5 LEDs 0 0 0 1 0 1 0 0

When a button is pressed (100%, 80%, 60%, 40%, 20% or On/Off) Read Coil function is used and my PCBA responds with the status of the LEDs as shown above.

I can create a channel for each Button / LED but how do I get the Read Coil function, as interperted by DaqFactory, to give me the data held in the Output status byte as I showed above? Obviously I am misunderstanding the Modbus protocol definition of Read Coil Status. I assumed that the 8 bit value, in my case, would represent each of the LEDs and all I need to do is a simple MASK AND to get the value of true or false in DaqFactory.

Please point me to the user guide section or tutorial or forum note that explains this. My searches for information for self-help have turned up nothing.

Regards,

Link to comment
Share on other sites

Two things:

1) you can't use Device #0. This is not an allowed ID for Modbus as it corresponds to a broadcast ID and its not really supported by DAQFactory. Use 1 instead.

2) you are trying to hard. Stop trying to mask the value. If you create 8 channels, 0 to 7, DAQFactory will request one byte from your controller, which will return one packed byte. DAQFactory will then do the masking for you, splitting that byte into the 8 channels.

Link to comment
Share on other sites

I set up individual channels to read the individual LED status. Works as expected - every time I press a button it starts a sequence that reads the status of the LEDs. The LED components use the channels I created Modbus_In_1 through _6 [0] to turn the LED components on and off. However, my question remains: is there anyway to read the individual bits of the data byte(s) from my reply to DaqFactory Read Coil Status (1) or is that not supported in DaqFactory?

Link to comment
Share on other sites

I'm sorry, but your controller is returning an incorrect response. DAQFactory should never return anything from Read coil status but 0 or 1. You said if DAQFactory outputted this:

00 01 00 01 00 01 AD DB

which would occur if you did: device.mydevice.readCoilStatus(0,1,1)

Now ignoring the fact that you shouldn't use ID = 0, the output that you said your device returns is:

00 01 01 14 50 7B:

00 Function 01 Byte count 01 Output status 14 CRC 50 7b

That is actually the incorrect response. Reread the Modbus protocol spec carefully. Here's the important part of the spec (and this is right out of the official spec doc from modbus.org):

The coils in the response message are packed as one coil per bit of the data field. Status is

indicated as 1= ON and 0= OFF. The LSB of the first data byte contains the output addressed

in the query. The other coils follow toward the high order end of this byte, and from low order

to high order in subsequent bytes.

If the returned output quantity is not a multiple of eight, the remaining bits in the final data

byte will be padded with zeros (toward the high order end of the byte). The Byte Count field

specifies the quantity of complete bytes of data.

This means that if you request only one coil, as DAQFactory did in this packet:

00 01 00 01 00 01 AD DB

Then you should return either 1 or 0, never 14. The query is only for coil #1 (numbered from 0). You shouldn't be returning the status of all 5 LED's, just the one assigned to the second coil (#1). The command to request all 5 coils, starting at #1 (and let me reiterate that #1 is the second coil, not the first), would be:

00 01 00 01 00 05 AC 18

and you should return the coils starting at #1, packed to the LSB side (i.e. coil #1 is the LSB, coil #2 is the next bit, etc) and padded on the high side with 0's. If you requested 5 coils starting at coil #4, it'd be the same thing, except the LSB of the response would be coil #4, etc.

Do you see where your problem is? You are treating the request as a request for a packed register, when really its a request for individual bits, and its the controllers responsibility to pack the bits in the response if more than one coil is requested, even if the bits don't line up with the byte. This is why I recommended on the phone sticking with holding registers, its much simpler.

Link to comment
Share on other sites

Thanks for the info: DaqFactory is sending 01 (Device # 01) (Function Read Coil Status 01) (Starting Address Hi 00) (Channel # OR Starting Address Lo 01) (Qty of Outputs Hi 01) and the rest is CRC AC 0A

My new response: (Device # 01) (Function 01) (Byte Count 01) (Coil Off 00 OR On 01) and the rest is CRC

For the other 5 input channels the above is repeated but the channel numbers change.

Then using MyChannel [0] in the LED component expression the LED changes from Green to Red.

All of this works fine.

I did not look at the DaqFactory read coil status request closely - now I see the error of my ways and have fixed it.

Thanks again for your long suffering and help.

Regards,

Bob

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.