RS485 cable replacement, no modbus?

I’m wanting to use the photon/electron for an rs485 cable replacement between two pieces of equipment, no computer on either end. From what I’ve read, the modbus is a protocol for communication between a computer and an RS485 device, if this is true, I shouldn’t need a modbus lib for this application.

How would I go about just transferring RS485 data between two units?

What are the two end-nodes?
If you are planning to replace the cable between two existing RS485 nodes, you need a way to talk to the nodes on either side, and that would still be RS485.
What protocol the two virtual-wire devices use to communicate with eachother is another thing and can be virtually anything.

3 Likes

So I need to find the 485 protocol the end points currently use? I guess rs485 isn’t a basic standard across the board.

RS485 isn’t really a protocol but the definition of the electrical characteristics of the link between nodes.
https://en.wikipedia.org/wiki/RS-485

If might be easier to state what devices you want to connect to decide what you are actually looking for.

Ptisecurity.com this is the products I want to talk with. From the computer to the controller, there are multiple options, and since the controller is usually within sight of the computer, there is no need to reinvent the wheel, so to speak, on that. I’m looking to communicate between the controller and the keypads, or other devices, here is what I found from technical specifications, they’re not extremely transparent.

The given connectivity options (PC or AI Devices) are only stating the “transport” but don’t really provide any detail about the protocol.
You need to find some datasheets/appliction notes to get that info too before being able to make a educated choice.

If you can get a decent documentation about how to use TCP/IP that might be the easiest to go with. Just hook up the device to your LAN via Ethernet and the Photon via WiFi.

Otherwise RS232 would be my next best option.

1 Like

Rs232 is only an option for pc to controller. I’m looking for controller to AI devices, which they only list 485, and 900mhz as options. I’ll see if I can find more information on the protocol.

Based on information from the Wikipedia page you posted, it looks like there are a limited number of communication standards transmitted over 485, with one being pointed out as the most frequently used. I guess if nothing else, I could use trial and error to find which one works; while not ideal, it may be my only option.

So after an hour of digging through what little information I could find, I decided to look at the 900mhz modems they are using to backtrace any information. They use the digi xtend wireless modems, which are specifically made to be universal, per the manufacturers website (which I take to mean they probably use asynchronous serial communication). They also provide the parameters for communication for this particular controller, and it’s predecessor, here.

Now, I’m still not sure if I have all of the information needed to make a decision on this project, there is still quite a few assumptions being made with no evidence to be found to back it up; also none to dispute it.

In their terminology the controller is probably one of their own devices and not any arbitrary microcontroller like the Photon. In your setup the Photon can act as PC or a "man in the middle" to the PC.
Their terminology only reflects the most likely standard setup but doesn't exclude any other setup specifically.
After all, how would their device tell whether the binary bitstream it receives originated from a PC or any other device? That's a simple deduction.

1 Like

You are right about the controller, it’s their equipment, it’s what holds all of the user information such as card numbers, names, codes, allowable access times, etc, and is programmed via computer over rs232, Ethernet, etc.

I’m wanting to be between the controller and AI device, ie: keypad or card reader. This is similar to the wiegand system you have helped me with before, but this company uses RS485 instead of wiegand to transmit the data back.

Here is what I’ve come up with, so far:

void keypad(const char *event, const char *data);

int incomingByte = 0;
int outgoingByte = 0;
void setup() {
    Serial1.begin(9600, SERIAL_8N2);
    Particle.subscribe("Janus485b", keypad, MY_DEVICES);
}

void loop() {
    while(Serial1.available() > 0){
       String incomingByte = Serial1.readStringUntil('\n');
        Particle.publish("Janus485a", incomingByte, PRIVATE);
    }
    
}
void keypad(const char *event, const char *data) {
	unsigned long outgoingByte = strtoul(data, NULL, 0);
	Serial.printlnf("received value=0x%x", outgoingByte);
    while(Serial1.available() > 0){
        Serial1.write(outgoingByte);
         }
}

I’m not sure how this will work out, I have some hardware on the way for testing.

To avoid confusion, you should not have global and local variables share the same name.
Next String incomingByte is a misleading name, as it’s not really a byte but a String.
The same goes for unsigned long outgoingByte.
And finally this potentially creates an infinite loop but still doesn’t write out the actual data you want

  while(Serial1.available() > 0){
    Serial1.write(outgoingByte);
  }

When you have bytes in the RX buffer (indicated by Serial1.available() > 0) they will stay there forever, since you never consume them in that while() and hence will never get out of there again.
And unsigned long is four byte, but you keep on writing out the first one only.

2 Likes

I changed the variables from “Byte” to “String”, a lot of this will change to something more relevant to my application once it works. I also removed the second while(Serial1.available() > 0), since looking again, it doesn’t seem to be needed, and from what you said, would do more harm anyway.

Shouldn’t Serial1.write output the data over serial? Looking in the docs here: https://docs.particle.io/reference/firmware/photon/#write-
serial.write() should output the same as serial.print(), although print, according to the docs, is used: to send the characters representing the digits of a number use the print() function instead..

Last, but not least, there were no references that I found concerning the bytes in unsigned long, it did show 4 bytes, but no example of how to make that change, so I took a guess (not exactly the right way to do this, I know) and made a change to it.

void keypad(const char *event, const char *data) {
    //read incoming data from secondary at keypad, write to controller
	unsigned long outgoingString = strtoul(data, NULL, 0000);
	Serial.printlnf("received value=0x%x", outgoingString);
         Serial1.write(outgoingString);
         Serial1.flush();//wait for serial buffer to empty
    }

Yes it does, but not as you anticipate when providing an unsigned long.

There is no version that takes an unsigned long and sends four bytes.
You may use Serial.write((uint8_t*)&val, sizeof(val)) (according to third syntax example) but then you need to care for endianness of the µC (what works on one platform may be inverted on another).

BTW, outgoingString is still declared as unsigned long and not a string so the parameter will still be treated as a byte and not as string.

1 Like

I used your sample, and made changes for my data instead of val, I’m not sure how to allow for endianness, there is no reference to it in the docs, or in my information from the manufacturer of how they read for that. I am sending infromation, as I can see over the console. It still doesn’t seem like I have the receiving part right, my terminal only shows a row of “c” across the screen.

void keypad(const char *event, const char *data) {
    //read incoming data from secondary at keypad, write to controller
	String outgoingString = ("received value=0x%x", outgoingString);
	Serial1.write((uint8_t*)&data, sizeof(data));
         Serial1.flush();//wait for serial buffer to empty
    }

Where does that sneaky ampersand (&) come from in this line

Serial1.write((uint8_t*)&data, sizeof(data));

const char* is already a pointer so you wouldn’t want the address of the pointer, but rather the place it’s pointing to.
Also you can’t use sizeof(data) as this will always return 4 since data is a pointer and hence always 4 byte long. If data was a string you’d rather write strlen(data).

BTW, I was not using data but val which was created by your

  unsigned long outgoingString = strtoul(data, NULL, 0000);
2 Likes

I couldn’t figure out exactly how to finish this up the way you described. However, this seems to work, one setup on two different computers, I can type into a terminal program, and it will show up on the other end, in ascii text. This is the best test I can do until my equipment gets delivered tomorrow. Do you see any reason this won’t work ok?

void keypad(const char *event, const char *data);//forward declaration
String outgoingString;
void setup() {
    Serial1.begin(19200);//begin serial at: 19200
    Particle.subscribe("Janus485b", keypad, MY_DEVICES);// subscribe to secondary publish of data from keypad
}

void loop() {
    //read incoming data on serial, and publish back to secondary for keypad
    while(Serial1.available() > 0){
       String incomingString = Serial1.readStringUntil('\n');
        Particle.publish("Janus485a", String(incomingString), PRIVATE);
        Serial1.flush();//wait for serial buffer to empty
    }
    
}
void keypad( const char *event, const char *data) {
    //read incoming data from secondary at keypad, write to controller
    String outgoingString = (event,data);
	Serial1.printlnf(outgoingString);
	Serial1.flush();//wait for serial buffer to empty
    }

Not particular anything that wouldn't work, but here you are doing extra work

incomingString already is a String so there is no need to make the string into a string by wrapping it in String(...).

Serial1.flush() doesn't empty the RX buffer, but only waits for the TX buffer to be fully sent.
I usually do this to flush the RX buffer

  while(Serial1.read() >= 0);

This will consume one byte at a time and once the RX buffer is empty Serial1.read() will return -1 and hence the loop will be broken.

String outgoingString = (event,data);

Does this really do what you expect? That's not a syntax I'm usually using.

2 Likes

It seems to be working as expected, assigning “outgoingString” with the event (although thinking through it now, that may be useless given that the node doesn’t care about the event) and incoming data, it doesn’t seem to affect the data coming in through the terminal program. I’ll know more about it tomorrow, I’m curious if it would throw off the incoming data, sending an event name, which isn’t expected, causing unreadable data.

If anyone is interested in the outcome, this system won’t work reliably for what we need. 485 on these devices has to be absolute, we can vary the timeout between .5 and 3 seconds, but whatever it is set to cant vary. So if we have a hiccup in the Wi-Fi somewhere, and it takes 4 seconds, weve lost that transaction and the customer isn’t happy. Shelved for now, maybe we can work it with the mesh products coming out.