Go Down

Topic: MODBUS wind vane without datasheet (Read 710 times) previous topic - next topic

heinburgh

Hello forum,

I recently bought this wind vane off Amazon:

https://www.amazon.co.uk/gp/product/B07VNDFYDW

I did not look at the availability of the datasheet before I bought, because Google...

Now I've spent several hours on Google trying to get hold of the datasheet so I can figure out the MODBUS parameters so I can get the direction from the sensor, but I cannot find it anywhere. The model number on the unit does not even deliver a single result. (Model number being 3001-FX-RS-1)

Is there anyone here who has successfully communicated with this specific model? I must say it is very well made, aluminium frame and very smooth turning.

I'm using a MAX485 converter between the wind vane and a MEGA and I've been able to get some data back using the code below, but obviously talking to the wrong registers and getting a fixed value back every time. The code is for an anemometer, not a wind vane, so the feedback is m/s.


Code: [Select]

#define RTS_pin    9   
#define RS485Transmit    HIGH
#define RS485Receive     LOW


void setup() {

  pinMode(RTS_pin, OUTPUT);
  Serial.begin(9600);
  Serial1.begin(9600);   
  delay(1000);
}

void loop() {

  digitalWrite(RTS_pin, RS485Transmit);   
  byte Anemometer_request[] = {0x01, 0x03, 0x00, 0x16, 0x00, 0x01, 0x65, 0xCE};
  Serial1.write(Anemometer_request, sizeof(Anemometer_request));
  Serial1.flush();
 
  digitalWrite(RTS_pin, RS485Receive);   
  byte Anemometer_buf[8];
  Serial1.readBytes(Anemometer_buf, 8);
 
  Serial.print("wind speed : ");
  for( byte i=0; i<7; i++ ) {
  Serial.print(Anemometer_buf[i], HEX);
  Serial.print(" ");
  }
  Serial.print(" ==> ");
  Serial.print(Anemometer_buf[4]);
  Serial.print(" m/s");
  Serial.println();                 
  delay(500);

}



pylon

Try to read the first 20 registers and print out the values while turning the vane. The value that changes is probably the sensor value.

heinburgh

Can you suggest a good library that I can do this with? MODBUS is still a new thing for me and I'm not entirely sure I'm doing it right.


OR - from the code I posted, how would you change this code to allow me to read the first 20 registers?

Do I just read them, or do I request data from the wind vane?

heinburgh

#3
Sep 17, 2019, 11:20 pm Last Edit: Sep 17, 2019, 11:25 pm by heinburgh
Ok I've changed the code I started off with to this:

Code: [Select]

void setup() {

  pinMode(9, OUTPUT);
  Serial.begin(9600);
  Serial1.begin(9600);   
}

void loop() {

  digitalWrite(9, HIGH);   
  byte Anemometer_request[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20};
  Serial1.write(Anemometer_request, sizeof(Anemometer_request));
  Serial1.flush();

delay(500);
 
  digitalWrite(9, LOW);   
  byte Anemometer_buf[32];
  Serial1.readBytes(Anemometer_buf, 32);

  for( byte i = 0; i < sizeof(Anemometer_request); i++ )
  {
    Serial.print(" ");
    Serial.print(Anemometer_buf[i], HEX);
  }
  Serial.println();                 
  delay(500);
}




And this is the serial output I'm getting. Values stay the same.

Code: [Select]

23:14:23:22:53.844 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:22:55.896 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:22:57.929 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:00.000 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:02.994 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:05.057 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:09.402 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:11.441 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:13.566 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:17.456 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:19.495 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:23.893 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:28.440 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:32.996 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:35.047 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:39.217 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:41.255 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:43.303 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:45.366 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:47.429 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:49.472 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:51.523 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:53.581 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:55.633 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:57.655 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:23:59.723 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:24:01.777 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC
23:24:03.807 ->  71 0 B9 CB FC 80 FF 2 80 0 0 2 0 0 3 19 2 0 0 D 0 0 33 0 0 1 2 3 4 5 6 DC




Now I'm pretty sure if this doesn't even mean anything, when I pull out the pin from the MAX485 (RO) that goes into my MEGA's serial1 RX, the numbers carry on displaying in eactly the same fashion.

How do I confirm that the MEGA is actually hearing back from the wind vane?


Idahowalker

How do I confirm that the MEGA is actually hearing back from the wind vane?
One could use a LED and a resistor, current limiting, on the data out from the vane.

One could use a very inexpensive Logic Analyzer.

One could use an O-Scope.

jremington

#5
Sep 18, 2019, 02:06 am Last Edit: Sep 18, 2019, 02:08 am by jremington
I would send it back. Amazon has a pretty good return policy.

I use and recommend the La Crosse Technology TX-23U Wind Sensor. It is well made and intended to be used with a La Crosse wireless weather sensor, but the  transmission protocol has been hacked and decoded for Arduino.  Use a 3.3V Pro Mini with it.

heinburgh

@jremington, I followed your advice and applied to return it. Suddenly the seller decided to send me a manual. So I thought I'd give it another go.

The manual is in Chinese (of course) but Google helped me to decipher it chop-chop.

I've spent a couple days looking through many threads on RS485 and I'm now well confused as to how to construct a request to read a register. I know the info below has the answer I need but I don't know how to proceed.


Here are the details:

Code: [Select]
3.1 Communication basic parameters
Parameter content
Encoding 8-bit binary
Data bit 8 bits
Parity bit
Stop bit 1 bit
Error calibration CRC lengthy cyclic code
The baud rate is 2400bps/4800bps/9600 bps. The factory default is 9600bps.
Encoding 8-bit binary
3.2 Data frame format definition
Adopt the Modbus-RTU consultation protocol in the following format:
Initial structure >= 4 bytes of time
Address code = 1 byte
Function code = 1 byte
Data area = N bytes
Error check = 16-bit CRC code
End structure >= 4 bytes of time
Address code: is the address of the transmitter, which is unique in the polling network (factory default 0x01).
Function code: The command function of the command sent by the host. This transmitter only uses function code 0x03 (read memory data).
Data area: The data area is the specific query number area. Note that the 16bits data high byte is in front.
CRC code: Two-byte check code.
Inquiry frame
Address code Function code Register start address Register length Check code low bit Check code high
1 byte 1 byte 2 bytes 2 bytes 1 byte 1 byte
Response frame
Address code Function code Effective number of bytes Data one area Second data area Nth data area1 byte 1 byte 1 byte 2 bytes 2 bytes 2 bytes
3.3 Register Address
Register Address PLC Configuration Address Contents Operation
0017H 40018 Wind direction (unit 1 position) Read only
0100H 40101 device address (0-252) read and write
0101H 40102 baud rate (2400/4800/9600) read and write
3.4 Communication protocol examples and explanations
3.4.1 Reading the Wind Direction Value of Device Address 0x01
Inquiry frame
Address code Function code Start address Data length Check code low Bit Check code high
0x02 0x03 0x00, 0x17 0x00, 0x01 0x34 0x0E
Response frame (for example, read wind direction value is 3)
Address code Function code Effective number of bytes Wind direction value Check code
Low parity
High position
0x02 0x03 0x02 0x00 0x03 0xF8 0x45
Wind direction:
0003 H (hexadecimal) = 3 => wind direction = Zhengdong
3.4.2 Wind direction sensor output value corresponds to wind direction position
RS485 output data definition
正北: 0x000F North East: 0x0000
Northeast: 0x0001 Northeast East: 0x0002
正东: 0x0003 East East: 0x0004
Southeast: 0x0005 Southeast: 0x0006
Zhengnan: 0x0007 Southwest: 0x0008
Southwest: 0x0009 West-West: 0x000A
正西: 0x000B West-West: 0x000C
Northwest: 0x000D North-northwest: 0x000E

heinburgh

I've tried this code, using the "enquiry frame" as per the manual:

"Inquiry frame
Address code Function code Start address Data length Check code low Bit Check code high
0x02 0x03 0x00, 0x17 0x00, 0x01 0x34 0x0E"


Code: [Select]

void setup() {

  pinMode(9, OUTPUT);
  Serial.begin(9600);
  Serial1.begin(9600);   
}

void loop() {

  digitalWrite(9, HIGH);   
  byte Anemometer_request[] = {0x02, 0x03, 0x00, 0x17, 0x00, 0x01, 0x34, 0x0E};
  Serial1.write(Anemometer_request, sizeof(Anemometer_request));
  Serial1.flush();
 
  digitalWrite(9, LOW);   
  byte Anemometer_buf[8];
  Serial1.readBytes(Anemometer_buf, 8);

  for( byte i = 0; i < sizeof(Anemometer_request); i++ )
  {
    Serial.print(" ");
    Serial.print(Anemometer_buf[i], HEX);
  }
  Serial.println();                 
  delay(500);
}




And the output remains the same:

Code: [Select]
14:17:49.374 ->  FF 21 2 3 21 F3 21 EB
14:17:51.353 ->  FF 21 2 3 21 F3 21 EB
14:17:52.894 ->  FF 21 2 3 21 F3 21 EB
14:17:54.421 ->  FF 21 2 3 21 F3 21 EB
14:17:55.936 ->  FF 21 2 3 21 F3 21 EB
14:17:57.438 ->  FF 21 2 3 21 F3 21 EB
14:17:58.972 ->  FF 21 2 3 21 F3 21 EB
14:18:00.501 ->  FF 21 2 3 21 F3 21 EB
14:18:01.995 ->  FF 21 2 3 21 F3 21 EB
14:18:03.517 ->  FF 21 2 3 21 F3 21 EB
14:18:05.036 ->  FF 21 2 3 21 F3 21 EB
14:18:06.571 ->  FF 21 2 3 21 F3 21 EB
14:18:08.092 ->  FF 21 2 3 21 F3 21 EB
14:18:09.616 ->  FF 21 2 3 21 F3 21 EB
14:18:11.131 ->  FF 21 2 3 21 F3 21 EB
14:18:12.653 ->  FF 21 2 3 21 F3 21 EB
14:18:14.173 ->  FF 21 2 3 21 F3 21 EB
14:18:15.698 ->  FF 21 2 3 21 F3 21 EB



Can anyone help me along on what else I need to add to the code?



pylon

Code: [Select]
  byte Anemometer_request[] = {0x02, 0x03, 0x00, 0x17, 0x00, 0x01, 0x34, 0x0E};


That's a Modbus request to slave #2 asking for the value of register number 23.

In your first post the request was for slave #1 asking for the value of register number 22. If you really got a response from the device it must have multiple IDs.

The request you send in post #3 is not a valid Modbus request so a Modbus device shouldn't react on it.

As you don't check the return value of Serial.readBytes() you don't know if the data is actually from the device or just any random data.

Quote
Can you suggest a good library that I can do this with?
There are several libraries for Modbus RTU, I use a slightly modified version of this one for my project.
But do you really know that device is talking Modbus? The responses you posted doesn't look like coming from a Modbus device.

Quote
The manual is in Chinese (of course) but Google helped me to decipher it chop-chop.
Why don't you post it here? We might get more information from it.

heinburgh

#9
Sep 18, 2019, 06:05 pm Last Edit: Sep 18, 2019, 06:07 pm by heinburgh
Here's the original manual.

I've tried several different libraries and this one is probably the clearest one so far - got it through youtube: https://www.youtube.com/watch?v=tBw15SfmuwI

Here's the code I'm trying now, sending a request to the wind direction register, to slave with address 1.

Code: [Select]


#include <ModbusMaster.h>

#define MAX485_DE      3
#define MAX485_RE_NEG  2

ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);

  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);


  Serial.begin(115200);

  // Modbus slave ID 1
  node.begin(1, Serial);   // wind sensor id = 1

  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}


void loop()
{
  uint8_t resultMain ;

  resultMain = node.readInputRegisters(0x0017, 1);
  if (resultMain == node.ku8MBSuccess)
  {
    Serial.println("Result: ");
    Serial.println(node.getResponseBuffer(0x00));
  } 

  delay(1000);
}


The results is this:

Code: [Select]


It does use different wiring, DE on my MAX485 goes to 3 and DE goes to 2. With the other libraries I had these two bridged and going to 9, or whatever pin the sketch asked for.

heinburgh

I've swapped wind vanes (I'm on #2 out of three), swapped boards as well as MAX485 boards.

Can this really be so difficult to achieve?

heinburgh

Could it be my wiring?

The wind vane runs off 12 - 24VDC. So I have it hooked up to a 7Ah 12V battery, maintaining 13.5V with small charger. The MAX485 board is connected to the Arduino 5V and GND. The Arduino is powered through a 5Amp power supply that delivers 7.5V into the Vin pin, this supply converts 12V from the same battery to 7.5V. All GND pins are common to the 12V negative through the 7.5V supply.

RS485 A and B into MAX485 A and B respectively

DI to TX
DE to 3
RE to 2
RO to RX

I started off using a MEGA but in case something was different that I may have missed, I'm now trying on an UNO.


heinburgh

Just received another manual from another seller.

So I've tried sending these bytes to the wind vane as per this manual, and the result stays exactly the same.

Code: [Select]

//  byte Anemometer_request[] = {0x01, 0x04, 0x00, 0x03, 0x00, 0xC1, 0xD5, 0xF9};  // get direction
  byte Anemometer_request[] = {0xFF, 0x06, 0x00, 0x03, 0x00, 0x00, 0x6C, 0x14};  // get address


Results:

Code: [Select]
07:28:07.424 ->  FF 21 2 3 21 F3 21 EB
07:28:08.926 ->  FF 21 2 3 21 F3 21 EB
07:28:10.470 ->  FF 21 2 3 21 F3 21 EB
07:28:11.974 ->  FF 21 2 3 21 F3 21 EB
07:28:13.484 ->  FF 21 2 3 21 F3 21 EB
07:28:15.018 ->  FF 21 2 3 21 F3 21 EB
07:28:16.544 ->  FF 21 2 3 21 F3 21 EB
07:28:18.062 ->  FF 21 2 3 21 F3 21 EB
07:28:19.590 ->  FF 21 2 3 21 F3 21 EB
07:28:21.090 ->  FF 21 2 3 21 F3 21 EB
07:28:22.627 ->  FF 21 2 3 21 F3 21 EB
07:28:24.137 ->  FF 21 2 3 21 F3 21 EB
07:28:25.662 ->  FF 21 2 3 21 F3 21 EB
07:28:27.162 ->  FF 21 2 3 21 F3 21 EB
07:28:28.709 ->  FF 21 2 3 21 F3 21 EB
07:28:30.213 ->  FF 21 2 3 21 F3 21 EB
07:28:31.731 ->  FF 21 2 3 21 F3 21 EB
07:28:33.270 ->  FF 21 2 3 21 F3 21 EB
07:28:34.767 ->  FF 21 2 3 21 F3 21 EB
07:28:36.308 ->  FF 21 2 3 21 F3 21 EB
07:28:37.821 ->  FF 21 2 3 21 F3 21 EB
07:28:39.354 ->  FF 21 2 3 21 F3 21 EB
07:28:40.875 ->  FF 21 2 3 21 F3 21 EB
07:28:42.372 ->  FF 21 2 3 21 F3 21 EB



Can I get these bytes on the serial monitor if there is no comms fro the wind vane?

heinburgh

#13
Sep 19, 2019, 08:07 am Last Edit: Sep 19, 2019, 08:09 am by heinburgh
When I add this line to clear the array after printing its values I can see that I'm actually not getting anything from the sensor, apart from the first line after booting, which is always the same. Not sure where these values come from.

 
Code: [Select]
for( byte i = 0; i < sizeof(Anemometer_request); i++ )
  {
    Serial.print(" ");
    Serial.print(Anemometer_buf[i], HEX);
  }
  memset(Anemometer_buf , 0, sizeof(Anemometer_buf));
 



Result:

Code: [Select]
08:04:44.841 ->  FF 21 2 3 21 F3 21 EB
08:04:46.356 ->  0 0 0 0 0 0 0 0
08:04:47.871 ->  0 0 0 0 0 0 0 0
08:04:49.404 ->  0 0 0 0 0 0 0 0
08:04:50.917 ->  0 0 0 0 0 0 0 0
08:04:52.432 ->  0 0 0 0 0 0 0 0
08:04:53.947 ->  0 0 0 0 0 0 0 0
08:04:55.454 ->  0 0 0 0 0 0 0 0
08:04:57.004 ->  0 0 0 0 0 0 0 0
08:04:58.523 ->  0 0 0 0 0 0 0 0
08:05:00.021 ->  0 0 0 0 0 0 0 0
08:05:01.544 ->  0 0 0 0 0 0 0 0



If there's anyone out there that can give me an idea on how to get this thing working I would be forever grateful.

pylon

Code: [Select]
  node.begin(1, Serial);   // wind sensor id = 1

Code: [Select]
  if (resultMain == node.ku8MBSuccess)
  {
    Serial.println("Result: ");
    Serial.println(node.getResponseBuffer(0x00));
  }


You can use the Serial interface either for Modbus or for debugging but not for both. Although it won't be sent as long as the driver pin is kept low it might stay in the buffer and be sent later mixing up with Modbus timing.

If you own a Mega, use that for the tests and set Serial1 to be the Modbus UART.

In your first tests you used 9600 baud and now you use 115200...
The Chinese documentation says 9600 baud is the maximum speed.

The documentation also says that the sensor only supports Modbus function 3:
Code: [Select]
  resultMain = node.readInputRegisters(0x0017, 1);
So that code should be
Code: [Select]
  resultMain = node.readHoldingRegisters(0x0017, 1);

The bus address is 2 according to the documentation, so the node should be initialized like this:

Code: [Select]
  node.begin(2, Serial1);   // wind sensor id = 2

Go Up