NXC: 1 NXT controls other NXTs - rs485 I/O remote control

Discussion specific to projects ideas and support.
Post Reply
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

NXC: 1 NXT controls other NXTs - rs485 I/O remote control

Post by HaWe »

Thank you again, Matthew, for providing us with your following code for low-level sending byte arrays via rs485:
mattallen37 wrote:I know the RS485 library should work, but I haven't run either of these test programs for a while.

NXC RS485 library

Code: Select all

/*
*  Matthew Richardson
*  matthewrichardson37<at>gmail.com
*  http://mattallen37.wordpress.com/
*  Aug. 11 / 2012
*
*  You may use this code as you wish, provided you give credit where it's due.
*
*  This is library for easy use of RS485 master/slave transactions.
*/

/*
My RS485 protocol data specs:

Sending byte array looks like this (based on how Mark Crosby does it) :
  
byte   type         description
0      MSG_TYPE     The type of message.
1      BYTE_COUNT   The count of bytes in the message body, excluding the header.
2      CHECKSUM     CRC8 checksum computed across the message body.
3      DEST_ADDR    Destination address for the message, or 0 for BROADCAST.
4      SRC_ADDR     Source address of the message sender.
5-n    The data

Returned values

-6 wrong message length
-5 wrong checksum
-4 not even the entire header was received
-3 not my address
-2 timeout
-1 something went wrong
0  Destination address was BROADCAST
1  Destination address was mine

*/

#ifndef _MyRS485Base_h_
#define _MyRS485Base_h_

void MyRS485BaseWriteArray (byte My485Address, byte TargetAddress, byte MsgType, byte SendData[], byte Length = 0){

  byte ByteCount = ArrayLen(SendData);

  if(Length){
    ByteCount = Length;
  }

  byte SendCompleteData[];
  byte SendCompleteDataLength = ByteCount + 5;
  ArrayInit(SendCompleteData, 0, SendCompleteDataLength);

  SendCompleteData[0] = MsgType;
  SendCompleteData[1] = ByteCount;
  SendCompleteData[2] = ArraySum(SendData, NA, ByteCount) & 0xFF;
  SendCompleteData[3] = TargetAddress;
  SendCompleteData[4] = My485Address;
  
  for(byte i = 0; i < ByteCount; i++){
    SendCompleteData[i + 5] = SendData[i];
  }

  RS485Write(SendCompleteData);
  until(HSOutputBufferOutPtr() == SendCompleteDataLength);
}

int MyRS485BaseRead(byte My485Address, byte & MsgType, byte & InArray[], int timeout = 250){
  byte RawInArray[];

  long OrigionalTick = CurrentTick();
  until(RS485DataAvailable()){                                                  // Wait until data has been received
    if(timeout && (CurrentTick() - OrigionalTick >= timeout))return -2;         // return -2 if it timed-out waiting for the responce.
    Yield();
  }
  byte DataAvailable = RS485DataAvailable();
  Wait(1);
  while(DataAvailable < RS485DataAvailable()){                                  // If it's been 1 ms since the last data was received, assume it's the end of the message.
    DataAvailable = RS485DataAvailable();
    Wait(1);
  }
  RS485Read(RawInArray);

  if(DataAvailable < 5)return -4;                                               // Not even the entire header was received.
  
  MsgType        = RawInArray[0];
  byte ByteCount = RawInArray[1];
  byte Checksum  = RawInArray[2];
  byte DestAddr  = RawInArray[3];
  byte SrcAddr   = RawInArray[4];

  if (DestAddr == My485Address || DestAddr == 0)
  {

    if(ByteCount != DataAvailable - 5){
      return -6;
    }

    byte ChecksumComputed = ArraySum(RawInArray, 5, NA) & 0xFF;
    if(Checksum != ChecksumComputed){
      return -5;
    }
    
    ArraySubset(InArray, RawInArray, 5, NA);
    
    return DestAddr?1:0;
  }
  else{
    return -3;
  }
  return -1;
}

void RS485Init(byte speed = HS_BAUD_921600)      // Turn on RS485 and set a baud rate
{
  UseRS485();
  Wait(MS_10);
  SetHSDataMode(DATA_MODE_RAW);
  Wait(MS_10);
  RS485Uart(speed, HS_MODE_DEFAULT);
  Wait(MS_10);
  SetHSInputBufferInPtr(0);
  SetHSInputBufferOutPtr(0);
}

void RS485UnInit(){                              // Turn off the RS485 port of the NXT
  SetSensorType(S4, SENSOR_TYPE_NONE);
  SetHSFlags(HS_UPDATE);
  SetSensorType(S4, SENSOR_TYPE_NONE);
}

#endif
rx test program

Code: Select all

#include "MyRS485Base lib.nxc"

byte Array[];
byte MsgType;

task main(){

  RS485Init(HS_BAUD_115200);

  while(true){
    //NumOut(0, LCD_LINE5, MyRS485BaseRead(2, MsgType, Array, 100));
    int result = MyRS485BaseRead(2, MsgType, Array, 100);
    ClearScreen();
    NumOut(0, LCD_LINE1, result);
    if(result >= 0){
      NumOut(0, LCD_LINE2, MsgType);
      for(byte i = 0; i < ArrayLen(Array); i ++){
        NumOut(50, LCD_LINE1 - (i*8), Array[i]);
      }
    }
  }
}
tx test program

Code: Select all

#include "MyRS485Base lib.nxc"

byte Array[6];
byte MsgType;
byte b1, b2, lx, ly, rx, ry;

task main(){

  RS485Init(HS_BAUD_115200);

  SetSensorLowspeed(S3);
  
  while(true){
  
    ReadSensorMSPlayStation(S3, 0x02, b1, b2, lx, ly, rx, ry);
    Array[0] = b1;
    Array[1] = b2;
    Array[2] = lx;
    Array[3] = ly;
    Array[4] = rx;
    Array[5] = ry;

    MsgType = ButtonPressed(BTNCENTER)?2:0;

    MyRS485BaseWriteArray(1, 2, MsgType, Array);
    
    Wait(10);
  }
}
Based upon this library, now the first step for all communication for 1 NXT to control other NTXs is : how can one control remote sensors and remote motors easily ?

Of course, for this we have to have advanced functions which are built upon these basic low level send-byte-array functions.
So we need to write wraps around Matthew's message procedure with kind of the following functionality and sort of the following syntax ("RS" indicates rs485 opposite to "BT" which could indicate Bluetooth for a later implementation in order to keep the same syntax):

Code: Select all

RemoteSetSensor(RS, 2, S1, SENSOR_TOUCH); // via RS485: config at NXT #2: Touch sensor at S1
RemoteSetSensor(RS, 10, S2, SENSOR_US);  // via RS485: config at NXT #10: US sensor at S2
RemoteSetSensor(RS, 4, S3, SENSOR_RAW);  // via RS485: config at NXT #4: analog sensor (ADC raw) at S3

int x=RemoteSensorTouch(RS, 2, S1);       // via rs485: read at NXT #2: Touch Sensor value at S1
int x=RemoteSensorUS(RS, 10, S2);         // via rs485: read at NXT #10: US Sensor value at S2
int x=RemoteSensorRaw(RS, 4, S3);         // via rs485: read at NXT #4: analog sensor value (ADC raw) at S3


RemoteOnFwd(RS, 3, OUT_A, pwr);             // via rs485: onFwd at NXT #3: OUT_A by pwr
RemoteOnFwdReg(RS, 5, OUT_B, pwr, regmode); // via rs485: onFwdReg at NXT #5: OUT_B by pwr, regmode

long e=RemoteMotRotCounter(RS, 9, OUT_C);   // via rs485: at NXT #9: read motor rotation counter of OUT_C
Unfortunately I currently can't imagine yet how to use these byte-array-send/receive procedures for multiple remote-sensor- and motor-control, not for single devices and especially not for sensors and motors at remote-multiplexers.

So how could one implement these high level functions for easy and trouble-free access?
What would be the code for the master, what for the slaves?

If once being resolved for these examples, then step by step we then can build a remote library for all other NXT compatible sensors.

Any ideas?
Last edited by HaWe on 09 Jun 2013, 09:51, edited 1 time in total.
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC: 1 NXT controls other NXTs - rs485 I/O remote control

Post by HaWe »

it seems still no such thing to be possible yet.
I think I'll wait for the EV3 to make it possible.
(Waiting for Godot...)
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests