Communicating via RS485

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: Communicating via RS485

Post by mattallen37 »

doc-helmut wrote:thx so far!
what I never understood:
what is "flatten" actually doing? why do I have to "flatten" an array into a string? Is it only for more-dim arrays? Or is it just adding a "0x00" at the end? Apart from that a string doesn't appear to me to be anything different than a 1-dim array...?
Well, strings in NXC are very weird compared to strings for other things. You can add elements to a string as easily as doing:

Code: Select all

string temp_str = "Hello";
temp_str += " World" + "!";
TextOut(0, LCD_LINE1, temp_str); // displays "Hello World!"
Because it's seen sort-of like any other variable (non array), it's interesting trying to deal with it (like extracting the 3rd element for example). Once you try to use a string with another dimension, things get more interesting.

flatten and unflatten copy the string into an array of bytes (dropping the NULL). I think you also need to be sure the byte array has the right number of elements. I have found that byte arrays are easier to work with for some stuff, and strings are better for some things.

Strings allow you to think less about the number of elements (and easily add more), but byte arrays are easier for things like converting into or from int or long.

I'm staying with my uncle right now who is a professional programmer, and he said the way NXC uses strings is similar to visual basic or cstring in C++ (which he agrees is not necessarily optimal).
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Communicating via RS485

Post by HaWe »

Thanks for the detailed answer.
so it's indeed just dropping the final "0x00" for strings to1-dim arrays (or adding one other way round) as I assumed.

Now I'm curious about the result of alphasucht's Token Ring implementation... 8-)
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Communicating via RS485

Post by afanofosc »

a. Yes, with the enhanced firmware you can address messages to a specific NXT. Not well tested. I'd be happy to work with you on that to see if it works correctly or not and if not, work on fixing what doesn't work.

b. Flatten doesn't work like Matt said. Let's try to keep the information as completely accurate as possible and just say "I don't really know" if we don't really know. An array of bytes when flattened would result in a string containing the bytes from the array with a null at the end. The array might have zeros in some of the elements so the "string" would not be a string in the usual sense since it would contain embedded nulls. You can flatten an array of structs or a struct or an array of ints (2 bytes per element) or an unsigned long (4 bytes + 1 null) or any datatype whatsoever. The results is a single dimensional array of bytes with a null added at the end. To correctly unflatten something you give it an example type (i.e., a variable of type LocationType or an unsigned long variable) and it uses the type of that variable to decide where all the bytes go. A flattened 1-d array of longs with 4 elements in it would result in a 17 element byte array (4*4+1) which you could store as a string (since it has the extra null at the end the firmware considers it to be a string)

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Communicating via RS485

Post by HaWe »

what is your model in the efw to address single NXTs in a rs485 network?
would you use the BT name?
Intuitively it would be the easiest way to address them like

Code: Select all

SendRS485Msg(string BTname, char len, char msg[])
(my personal 2ct)

for a Token Ring net each single NXT will only have to know the name of his next neighbour in the ring, not necessarily all the members of the ring, plus the name(s) of the member(s) he is allowed to send to. An array of all names would be an option, too.

there should be an ack by the recipient if all the msg has been receveived:

Code: Select all

do {
  SendRS485Msg(BTname, len, msg[]);
} while (AckRS485(BTName)!=0xff);
you may implement a checksum additionally:

Code: Select all

int checksum(char msg) {
  int sum;
  for (char i=0; i<len(msg); ++i) {
    sum+=msg[i]; 
  }
  return sum;
}
the checksum will have to be returned by the recipient as an ack parameter.

the sending function could be then:

Code: Select all

do {
  SendRS485Msg(BTname, len, msg[]);
} while (AckRS485(BTName)!=checksum(msg) );
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: Communicating via RS485

Post by mattallen37 »

John, thanks for expanding on what I knew. I was only talking about a specific situation, and didn't mean to limit the abilities of flatten to my example (which I thought was the only pertinent situation).
doc-helmut wrote:what is your model in the efw to address single NXTs in a rs485 network?
There isn't any FW level RS485 addressing available; it all needs to be user-code based.
doc-helmut wrote:would you use the BT name?
No. The mac address is far longer than I care to send with every message. IIRC, it's a total of 6 bytes (3 unique bytes). I guess you could use it, but I don't, and don't care to.
doc-helmut wrote:Intuitively it would be the easiest way to address them like

Code: Select all

SendRS485Msg(string BTname, char len, char msg[])
(my personal 2ct)

for a Token Ring net each single NXT will only have to know the name of his next neighbour in the ring, not necessarily all the members of the ring, plus the name(s) of the member(s) he is allowed to send to. An array of all names would be an option, too.

there should be an ack by the recipient if all the msg has been receveived:

Code: Select all

do {
  SendRS485Msg(BTname, len, msg[]);
} while (AckRS485(BTName)!=0xff);
you may implement a checksum additionally:

Code: Select all

int checksum(char msg) {
  int sum;
  for (char i=0; i<len(msg); ++i) {
    sum+=msg[i]; 
  }
  return sum;
}
the checksum will have to be returned by the recipient as an ack parameter.

the sending function could be then:

Code: Select all

do {
  SendRS485Msg(BTname, len, msg[]);
} while (AckRS485(BTName)!=checksum(msg) );
Just curious, but why would you want to use peer/peer with token instead of master/slave? Master/slave can still distribute information across the entire network, but it is all controlled by one common NXT (and if you want other NXTs to get the info instantly, they can "listen in").

Normally I combine the ack message with the response of the slave.

Also, if you want checksum or crc, you need to implement it yourself.

Edit: normally the checksum is calculated by the sender, and then transmitted with the message. The receiver usually also computes the checksum, and then compares them.
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Communicating via RS485

Post by HaWe »

mattallen wrote:There isn't any FW level RS485 addressing available; it all needs to be user-code based.
afanofosc wrote:a. Yes, with the enhanced firmware you can address messages to a specific NXT. Not well tested. I'd be happy to work with you on that to see if it works correctly or not and if not, work on fixing what doesn't work.
John Hansen

doc-helmut wrote:what is your model in the efw to address single NXTs in a rs485 network?
would you use the BT name?
Intuitively it would be the easiest way to address them like

Code: Select all

SendRS485Msg(string BTname, char len, char msg[])
(my personal 2ct)
[/color]
mattallen wrote:Just curious, but why would you want to use peer/peer with token instead of master/slave? Master/slave can still distribute information across the entire network, but it is all controlled by one common NXT (and if you want other NXTs to get the info instantly, they can "listen in").
my NXT Token Ring net (NX#T) I would use
e.g., for multi-NXT-console-multi-threaded-multi-player games (MNCMTMP games),
or multi-ARM32-core- super-paralellized multi-NXT calculating systems e.g., for super- parallelized NXT chess computers (NeXT Deep Thought =NxDT),
or multi-NXT-Behaviour-Robots where all NXTs may send event messages at any point-of-interest (NX-POI)
^^
(not meant quite so seriously)
but no, actually it's alphasucht's issue, not mine in the moment - but: que sera, sera;)
gloomyandy
Posts: 323
Joined: 29 Sep 2010, 05:03

Re: Communicating via RS485

Post by gloomyandy »

If you are interested in master/slave RS485 networks rather than inventing your own (which can be a lot of fun), you may want to implement something based on a standard, or at least take a look at one. leJOS implements a bitbus network http://www.bitbus.org/. This has details of error detection, addressing acknowledgements etc. and is used in many industrial situations over long distances in noisy environments (so it may be overkill for a Lego robot!). This means that the protocol is well tested, robust (it uses CRC-16-CITT error checking which is way more effective than a simple checksum) and many of the potential problems have already been worked out...

The leJOS implementation builds standard Java streams on top of the low level implementation allowing the sending of pretty much any data type over the connection...

Andy
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Communicating via RS485

Post by afanofosc »

I have some inclination to implement bitbus support in the enhanced NBC/NXC firmware but have not gotten around to doing anything like that yet. Maybe someday in 2012.

I also have no intention of using the bluetooth address or the brick name or anything like that at the firmware level for addressing a message to a specific NXT.

The addressing ability is only implemented when the Comm module's HsDataMode is set to DATA_MODE_NXT rather than its default value of DATA_MODE_RAW. This allows you to send Direct Command and System Command messages to another NXT over RS485 via all of the NXC API functions for these DC and SC messages. See all of the Remote* functions, which for your convenience I have listed below.

Code: Select all

RemoteBluetoothFactoryReset(byte conn)
RemoteCloseFile(byte conn, byte handle)
RemoteConnectionIdle(byte conn)
RemoteConnectionWrite(byte conn, byte buffer[])
RemoteDatalogRead(byte conn, bool remove, byte & cnt, byte & log[])
RemoteDatalogSetTimes(byte conn, long synctime)
RemoteDeleteFile(byte conn, string filename)
RemoteDeleteUserFlash(byte conn)
RemoteFindFirstFile(byte conn, string mask, byte & handle, string & name, long & size)
RemoteFindNextFile(byte conn, byte & handle, string & name, long & size)
RemoteGetBatteryLevel(byte conn, int & value)
RemoteGetBluetoothAddress(byte conn, byte & btaddr[])
RemoteGetConnectionCount(byte conn, byte & cnt)
RemoteGetConnectionName(byte conn, byte idx, string & name)
RemoteGetContactCount(byte conn, byte & cnt)
RemoteGetContactName(byte conn, byte idx, string & name)
RemoteGetCurrentProgramName(byte conn, string & name)
RemoteGetDeviceInfo(byte conn, string & name, byte & btaddr[], byte & btsignal[], long & freemem)
RemoteGetFirmwareVersion(byte conn, byte & pmin, byte & pmaj, byte & fmin, byte & fmaj)
RemoteGetInputValues(byte conn, InputValuesType & params)
RemoteGetOutputState(byte conn, OutputStateType & params)
RemoteGetProperty(byte conn, byte property, variant & value)
RemoteIOMapRead(byte conn, long id, int offset, int & numbytes, byte & data[])
RemoteIOMapWriteBytes(byte conn, long id, int offset, byte data[])
RemoteIOMapWriteValue(byte conn, long id, int offset, variant value)
RemoteKeepAlive(byte conn)
RemoteLowspeedGetStatus(byte conn, byte & value)
RemoteLowspeedRead(byte conn, byte port, byte & bread, byte & data[])
RemoteLowspeedWrite(byte conn, byte port, byte txlen, byte rxlen, byte data[])
RemoteMessageRead(byte conn, byte queue)
RemoteMessageWrite(byte conn, byte queue, string msg)
RemoteOpenAppendData(byte conn, string filename, byte & handle, long & size)
RemoteOpenRead(byte conn, string filename, byte & handle, long & size)
RemoteOpenWrite(byte conn, string filename, long size, byte & handle)
RemoteOpenWriteData(byte conn, string filename, long size, byte & handle)
RemoteOpenWriteLinear(byte conn, string filename, long size, byte & handle)
RemotePlaySoundFile(byte conn, string filename, bool bloop)
RemotePlayTone(byte conn, unsigned int frequency, unsigned int duration)
RemotePollCommand(byte conn, byte bufnum, byte & len, byte & data[])
RemotePollCommandLength(byte conn, byte bufnum, byte & length)
RemoteRead(byte conn, byte & handle, int & numbytes, byte & data[])
RemoteRenameFile(byte conn, string oldname, string newname)
RemoteResetMotorPosition(byte conn, byte port, bool brelative)
RemoteResetScaledValue(byte conn, byte port)
RemoteResetTachoCount(byte conn, byte port)
RemoteSetBrickName(byte conn, string name)
RemoteSetInputMode(byte conn, byte port, byte type, byte mode)
RemoteSetOutputState(byte conn, byte port, char speed, byte mode, byte regmode, char turnpct, byte runstate, unsigned long tacholimit)
RemoteSetProperty(byte conn, byte prop, variant value)
RemoteStartProgram(byte conn, string filename)
RemoteStopProgram(byte conn)
RemoteStopSound(byte conn)
RemoteWrite(byte conn, byte & handle, int & numbytes, byte data[])
The connection should be specified via a constant so that the macros emit the correct code for these "inline functions". The correct constants to use are from nbcommon.h:

Code: Select all

#define CONN_BT0    0x0 /*!< Bluetooth connection 0 */
#define CONN_BT1    0x1 /*!< Bluetooth connection 1 */
#define CONN_BT2    0x2 /*!< Bluetooth connection 2 */
#define CONN_BT3    0x3 /*!< Bluetooth connection 3 */
#define CONN_HS4    0x4 /*!< RS485 (hi-speed) connection (port 4, all devices) */
#define CONN_HS_ALL 0x4 /*!< RS485 (hi-speed) connection (port 4, all devices) */
#define CONN_HS_1   0x5 /*!< RS485 (hi-speed) connection (port 4, device address 1) */
#define CONN_HS_2   0x6 /*!< RS485 (hi-speed) connection (port 4, device address 2) */
#define CONN_HS_3   0x7 /*!< RS485 (hi-speed) connection (port 4, device address 3) */
#define CONN_HS_4   0x8 /*!< RS485 (hi-speed) connection (port 4, device address 4) */
#define CONN_HS_5   0x9 /*!< RS485 (hi-speed) connection (port 4, device address 5) */
#define CONN_HS_6   0xa /*!< RS485 (hi-speed) connection (port 4, device address 6) */
#define CONN_HS_7   0xb /*!< RS485 (hi-speed) connection (port 4, device address 7) */
#define CONN_HS_8   0xc /*!< RS485 (hi-speed) connection (port 4, device address 8) */
You use SetHsAddress(byte Address) to set your NXT's address to something other than its default value of HS_ADDRESS_ALL (0). The correct constants to use with SetHsAddress are in nbccommon.h:

Code: Select all

#define HS_ADDRESS_ALL 0 /*!< HsAddress all devices */
#define HS_ADDRESS_1   1 /*!< HsAddress device address 1 */
#define HS_ADDRESS_2   2 /*!< HsAddress device address 2 */
#define HS_ADDRESS_3   3 /*!< HsAddress device address 3 */
#define HS_ADDRESS_4   4 /*!< HsAddress device address 4 */
#define HS_ADDRESS_5   5 /*!< HsAddress device address 5 */
#define HS_ADDRESS_6   6 /*!< HsAddress device address 6 */
#define HS_ADDRESS_7   7 /*!< HsAddress device address 7 */
#define HS_ADDRESS_8   8 /*!< HsAddress device address 8 */
One way to use RS485 between two NXT's is via the same mailbox message scheme that you would use via Bluetooth and the standard firmware. This would involve the RemoteMessageRead and RemoteMessageWrite API functions.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Communicating via RS485

Post by HaWe »

My approach would be the following to-do-list):

1st, what is the max number of NXT to be connected to rs485? sth like 32? or 64?
Then it should be possible to address all of those single 32 (or 64?) NXTs by specific IDs , otherwise there's too much restriction.

When there once will exist such a big network, then the main issues of course are (for any NXT to any NXT) :
1) reading all other sensor or sensor mux values and encoder values,
2) controlling any specific motor (start, stop, PID, reach relative or absolute target),
3) sending any interesting variable value (char, int, long, float, or arrays of those values) calculated by any program or subroutine.
4) error detection and addressing acknowledgements are self-evident.

To avoid data stream collision of sending NXTs, they must be restricted by some kind of Token.
If a Token Ring is not appropriate, then maybe some kind of peer-to-peer TCP-IP architecture might do it.

If there is no way of addressing all those single NXTs at firmware level it would be better to add an initial at all the msgs[0] for the address, maybe starting at 0x01 for the first and ending at 0x21 or what ever for the last one, and maybe an Identifier to distinguish between different strings by different senders.
As all the NXTs are functionally indistinguishable, they must have defined their own unic ID in each single local program.

now the user interface:
all rs485 cmds should be of the same syntax like for the local NXT, but additionally with the receiver name as the first parameter, e.g.:

Code: Select all

OnFwd(OUT_AB, -80);                              rs485OnFwd(0x01, OUT_AB, -80);
SetSensorAnalog(S1);                             rs485SetSensorAnalog(0x01, S1);
x=SensorUS(S2);                                  x=rs485SensorUS(0x01, S2);          
RotateMotorEx(OUT_AB, 75, 360, 50, true, true);  rs485RotateMotorEx(0x01,OUT_AB, 75, 360, 50, true, true);
y=MotorRotationCount(OUT_C);                     y=rs485MotorRotationCount(0x01, OUT_C);    
ps
maybe it makes sence to use msg[0] for the address and msg[1] for the sender!

pps
to my opinion HS like in
HS_ADDRESS or CONN_HS_1
is not a good choice for an abbreviation, because HS should be reserved for highspeed i2c.
rs485 is a serial com protocoll which may work at different speed (CMIIW) and so should be abbreviated
by RS like in RS_ADDRESS_..., CONN_RS_1
or by
COM like in COM_ADDRESS_..., CONN_COM_1
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Communicating via RS485

Post by HaWe »

anything like that in development or in planning (by anyone)?
Any code examples e.g. for a net of 4 NXTs at 1 portsplitter available (for the beginning)?
I'd be glad to help or to test it!

Surely would help for alphasucht's issue too!
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest