RS485 communication woes

Hi,

I'm trying to use my arduino mega ADK to communicate with a pump over RS485 but after 2 weeks of trying to get it going, it still won't respond. I can talk to it via RS232 fine but, due to limitations on the number of RX/TX pins, I need an RS485 bus. I have recently started trying to communicate with another device (compressor) over the RS485 bus but, haven't had any luck there either. The compressor and pump use different protocols so, I'm thinking that it may be a physical issue or due to the port being set up incorrectly.

I've attached a picture of the RS485 part of my schematic. Supply voltage is 5V.

The details:

  • I'm using a max487 between the arduino and the devices
  • My cable has 2 twisted pairs: one for ground, the other for TX/RX+, TX/RX-
  • The cable has a nominal resistance of 120 ohm
  • The termination resistors in the pump are used. The manual recommends 120Ohm termination at the other end, which I have.
  • Pin 7 on the pumps Sub D connector is TX/RX+, pin 8 is TX/RX-
  • Baud rate is 19200
  • The voltage levels for the pump are as follows:
    Logic 0 - transmitter: 1.5- 5 V, receiver > 0.3V
    Logic 1 - transmitter: -1.5- -5 V, receiver <= 0.3V
    I have attached a picture of my signal trace It shows the differential signal (Tx/RX+) - (Tx/Rx-)

The communication requires 1 start bit, 1 stop bit, and even parity.
I am initialising the Serial as follows

const unsigned long FOSC = 16000000;// Clock Speed

setup(){
    RS485_Init ( 19200 );

    pinMode(RTS, OUTPUT);
    
    digitalWrite(RTS,HIGH);
}

void RS485_Init( unsigned long baud){

  unsigned int ubrr;
  ubrr = (unsigned int)(FOSC/16/baud - 1);
  Serial.print("UBRR ");
  Serial.println(ubrr);
  ubrr &= 0x0FFF; // making sure that the 4 MSBs are 0
  // using TX/RXD2

  // set character size to 8
  // asynchronous normal mode USART
  // parity mode to even
  UCSR2C = 0;
  UCSR2C |=  _BV(UPM21) | _BV(UCSZ21) | _BV(UCSZ20);
  setEvenParity();
    
  /* Set baud rate */
  UBRR2H = (unsigned char)(ubrr>>8);
  UBRR2L = (unsigned char)ubrr;
   
  /* Enable receiver and transmitter */
  noInterrupts();
  UCSR2B = 0;
  UCSR2B |= _BV(RXEN2)|_BV(TXEN2) | _BV(RXCIE2);
  //UCSR2B &= 0xF8; //write the Reserved Bits in MSPI mode to zero
  interrupts();

} 

void tp_sendMessage(byte access_type, unsigned int parameterNum, unsigned long paramVal, byte parameterIndex){
  //Serial.println("#SEND");
  digitalWrite(RTS,HIGH);
  setEvenParity();
  tp_sentParamNum = parameterNum;
  tp_sentParamIndex = parameterIndex;

  byte turbopump_message[TURBOPUMP_MESSAGE_LENGTH + 1]; // need length one longer than required to store a NULL so arduino doesnt read over the end of array
  tp_makePacket( access_type, parameterNum, parameterIndex,  paramVal, turbopump_message);
  Serial.print("#SENDING --");
  

  
  for ( int t = 0; t < TURBOPUMP_MESSAGE_LENGTH; t++){
    Serial.print(turbopump_message[t], HEX);
     Serial.print(" ");
  }
  
  Serial.println("--");
  Serial2.write(turbopump_message, TURBOPUMP_MESSAGE_LENGTH);
  Serial2.flush();
  
}

// the turbopump needs even parity and the compressor needs no parity
void setEvenParity(){
     // todo ensure nothing is being sent or received
 
  UCSR2C &=  B11001111;  // clear parity bits
  UCSR2C |=  B00100000;  // set even parity 
}

When I look at the signal trace, I can see my signal being sent but, I receive no reply. The tp_makePacket(), tp_sendMessage(), tp_receiveMessage() functions all work over RS232 (with Serial2 exchanged for Serial 3) so I don't think the problem is there.

Do you have any idea what I could be doing wrong? Do you hae any general RS485 trouble shooting tips that may help?

Thanks in advance

TX/RX+, pin 8 is TX/RX-

What is this?

There is A and B, that's it.

I can talk to it via RS232 fine but, due to limitations on the number of RX/TX pins, I need an RS485 bus.

This I don't understand either. How does changing to 485 help with pins?

What signal is that a trace of?

EDIT: Where is the tp_makePacket() function? I see you are printing out turbopump_message, is it correct?


Rob

R3 and R2 are wrong. It should be 1 120ohm resistor across A and B. What type of RS485 network is it? Are you trying to transmit and receive from one MAX487?

Hi ,

Thanks for the replies.

Graynomad:
TX/RX+ is A, in the case of the Max487 its the Noninverting Receiver Input and Noninverting Driver Output
TX/RX- is B, the Inverting Receiver Input and Inverting Driver Output

I can talk to it via RS232 fine but, due to limitations on the number of RX/TX pins, I need an RS485 bus.

By this I meant that, the number of Serial ports is limited on the Arduino. I am also communicating with other devices which use up my other Serial ports. I need to communicate with the pump over RS485 which I can't do at the moment but, I can communicate with it over RS232.

Where is the tp_makePacket() function? I see you are printing out turbopump_message, is it correct?

I didn't put it in as it works fine, I use it when communicating over RS232. The data being printed out when I send is correct. In the case of word data (16 or 32 bit word length) the high
bit is transmitted first (Motorola standard).

Here's the code

void addByteToRS485Message(byte info, byte* message){
  message[turbopumpMsgIndex] = info;
  turbopumpCheckSum ^= info;
  turbopumpMsgIndex ++;
}

void add32BitToRS485Message(long number, byte* message){
  
  unsigned int msw = (number  & 0xffff0000) >> 16;
  addIntToRS485Message( msw, message);
  unsigned int lsw = number  & 0x0000ffff;
  addIntToRS485Message( lsw, message);
}

void addIntToRS485Message(int number, byte* message){
  byte msb = (number  & 0xff00) >> 8;
  addByteToRS485Message( msb, message);
  byte lsb = number  & 0x00ff;
  addByteToRS485Message( lsb, message);

}

unsigned int tp_makePKE(byte access_type, unsigned int parameterNum){

  unsigned int pke = parameterNum & 0x7FF; //B0000011111111111;
  pke |= (access_type << 12);
  return pke;


}

void tp_makePacket(byte access_type, unsigned int parameterNum, byte parameterIndex,  unsigned long paramVal, byte * turbopump_message){



  turbopump_message[TURBOPUMP_MESSAGE_LENGTH] = NULL; // Ensure last value is null so arduino doesnt read over the end of the array
  turbopumpMsgIndex = 0;
  turbopumpCheckSum = 0;

  addByteToRS485Message(B00000010, turbopump_message); // start byte
  addByteToRS485Message(B00010110, turbopump_message); //Length of the payload data block in bytes. (bytes 3 to 22) + 2: 22
  addByteToRS485Message(turbopumpAddr, turbopump_message); // Address of the frequency converter RS485 0-15
  addIntToRS485Message(tp_makePKE(access_type,parameterNum), turbopump_message); //Parameter number and type of access
  addByteToRS485Message(B00000000, turbopump_message); // reserved
  addByteToRS485Message(parameterIndex, turbopump_message);// Parameter index 
  add32BitToRS485Message(paramVal, turbopump_message);  // parameter value
  addIntToRS485Message(tp_controlWord, turbopump_message); //control word when sending, status when receiving
  addIntToRS485Message(0, turbopump_message); //current rotor frequency = P3
  addIntToRS485Message(0, turbopump_message); //current frequency converter temp = P11
  addIntToRS485Message(0, turbopump_message);
  addIntToRS485Message(0, turbopump_message);
  addIntToRS485Message(0, turbopump_message);

  turbopump_message[turbopumpMsgIndex] = turbopumpCheckSum;
  turbopumpMsgIndex ++;

}

The signal trace I posted shows the differential signal on the RS485 bus. It is obtained using the math function on my oscilloscope, Signal = A - B.
When I look at the signal trace, I can see the data being sent is correct. I put it up thinking someone might say "You're voltage levels are wrong" etc

Pavilion1984:

R3 and R2 are wrong. It should be 1 120ohm resistor across A and B.

Whats the difference between having 2 60 Ohm resistros in series vs 1 120 Ohm resistor. I put the Cap in there as documents I read said it helped with noise. I tried it out before I put the Cap in and the signal didn't seem to look any different so, I left it in.

What type of RS485 network is it?

Its a 3 wire interface: GND, A, B. The Arduino is the master, the pump is a slave. The Arduino queries/commands the pump and the pump answers.

Are you trying to transmit and receive from one MAX487?

Yes

// the turbopump needs even parity and the compressor needs no parity

You have a setEvenParity() function but there's nowhere the parity is set back to none.

We still don't have all the code so maybe it's elsewhere.


Rob

I can talk to it via RS232 fine but, due to limitations on the number of RX/TX pins, I need an RS485 bus.

communicate with the pump over RS485 which I can't do at the moment but, I can communicate with it over RS232.

I still don't get this. If you swap an RS-232 transceiver with and RS-485 transceiver you don't get any more RX/TX pins. Also there is NO change required to the software, therefore it must be a hardware problem.

But then you say

I need an RS485 bus.

That implies a very different thing, a bus is not a point-to-point link.

Do you have both devices hanging off the same line?

Also, if these gadgets were designed for RS-232 how are they going to work with RS-485?


Rob

Hi,

I've been a bit unclear on the ports thing. Here's what I have
Serial0 - USB communication with computer. Works fine
Serial1 - communication with a display. Works fine
Serial2 - Connected to the max487 for Communcation with pump and compressor. I can't get this working
Serial3 - Connected to a max3232 for communcation with a temperature monitor. I can communciate with the pump over this or, the temperature monitor.

The pump has a port for RS485 and one for RS232. The compressor has a single port but, by changing the address It can act as an 232 or 485 port. Hopefully that clears things up :slight_smile:

there is NO change required to the software, therefore it must be a hardware problem.

The only change is changing the stuff to do with the serial ports from serial3 (RS232) to Serial 2(rs485). I agree, I believe it is a hardware problem but, I am unable to find the problem.

I'll post the code in another post. The message length is too long

Thanks for the help

Here's the full code for the turbopump only minus the Error and message type constants as the post is too long. Eventually I would like to have the compressor and pump on the same RS485 line, connected to the same MAX487. I'd like to be able to communicate with each individually.

#include "Arduino.h"

const double TP_THRESHOLD_PRESSURE = 4.0;
const unsigned long FOSC = 16000000;// Clock Speed
const int TURBOPUMP_MESSAGE_LENGTH = 24;
const int RS485_BUFFER_LENGTH = TURBOPUMP_MESSAGE_LENGTH + 1;
const int RTS  = 32;
const byte turbopumpAddr = 2; // address of the turbopump


int turbopumpMsgIndex = 0;
int turbopumpCheckSum = 0;


byte rs485_rcvBuf[RS485_BUFFER_LENGTH];
int rs485_rcvBufIndex = 0;

unsigned int tp_sentParamNum = 0;
byte tp_sentParamIndex = 0;
unsigned long tp_receivedParam = 0; // bits stored here. need to interpret them as appropriate for each param

unsigned int tp_controlWord = 0x400; // 0x280;
unsigned int tp_statusWord  = 0;
int tp_freqConverterTemp = 0;
unsigned int tp_setpointMotorCurrent = 0;
int tp_bearingTemp = 0;
int tp_freqConveterVS = 0;
boolean tp_error = false; // abool stating if there is an error
double pumpRotSpeed = 0;


void setup(){
  tp_Init();
  Serial.begin(57600);
}

void tp_Init(){
  Serial2.begin(19200);

  RS485_Init ( 19200 );

  pinMode(RTS, OUTPUT);
  digitalWrite(RTS,HIGH);
  tp_turnOffPump();


}

void RS485_Init( unsigned long baud){

  UCSR2C |=  _BV(UPM21) | _BV(UCSZ21) | _BV(UCSZ20);

} 


void addByteToRS485Message(byte info, byte* message){
  message[turbopumpMsgIndex] = info;
  turbopumpCheckSum ^= info;
  turbopumpMsgIndex ++;
}

void add32BitToRS485Message(long number, byte* message){
  unsigned int msw = (number  & 0xffff0000) >> 16;
  addIntToRS485Message( msw, message);
  unsigned int lsw = number  & 0x0000ffff;
  addIntToRS485Message( lsw, message);
}

void addIntToRS485Message(int number, byte* message){
  byte msb = (number  & 0xff00) >> 8;
  addByteToRS485Message( msb, message);
  byte lsb = number  & 0x00ff;
  addByteToRS485Message( lsb, message);

}

unsigned int tp_makePKE(byte access_type, unsigned int parameterNum){

  unsigned int pke = parameterNum & 0x7FF; //B0000011111111111;
  pke |= (access_type << 12);
  return pke;
}

void tp_makePacket(byte access_type, unsigned int parameterNum, byte parameterIndex,  unsigned long paramVal, byte * turbopump_message){

  turbopump_message[TURBOPUMP_MESSAGE_LENGTH] = NULL; // Ensure last value is null so arduino doesnt read over the end of the array
  turbopumpMsgIndex = 0;
  turbopumpCheckSum = 0;

  addByteToRS485Message(B00000010, turbopump_message); // start byte
  addByteToRS485Message(B00010110, turbopump_message); //Length of the payload data block in bytes. (bytes 3 to 22) + 2: 22
  addByteToRS485Message(turbopumpAddr, turbopump_message); // Address of the frequency converter RS485 0-15
  addIntToRS485Message(tp_makePKE(access_type,parameterNum), turbopump_message); //Parameter number and type of access
  addByteToRS485Message(B00000000, turbopump_message); // reserved
  addByteToRS485Message(parameterIndex, turbopump_message);// Parameter index TODO wat happens when reading?
  add32BitToRS485Message(paramVal, turbopump_message);  // parameter value
  addIntToRS485Message(tp_controlWord, turbopump_message); //control word when sending, status when receiving
  addIntToRS485Message(0, turbopump_message); //current rotor frequency = P3
  addIntToRS485Message(0, turbopump_message); //current frequency converter temp = P11
  addIntToRS485Message(0, turbopump_message);
  addIntToRS485Message(0, turbopump_message);
  addIntToRS485Message(0, turbopump_message);

  turbopump_message[turbopumpMsgIndex] = turbopumpCheckSum;
  turbopumpMsgIndex ++;

}

void tp_sendMessage(byte access_type, unsigned int parameterNum, unsigned long paramVal, byte parameterIndex){

  setEvenParity();
  tp_sentParamNum = parameterNum;
  tp_sentParamIndex = parameterIndex;

  byte turbopump_message[TURBOPUMP_MESSAGE_LENGTH + 1]; 
  tp_makePacket( access_type, parameterNum, parameterIndex,  paramVal, turbopump_message);
  Serial.print("#PUMP SENDING --");



  for ( int t = 0; t < TURBOPUMP_MESSAGE_LENGTH; t++){

        Serial.print(" ");
        Serial.print(turbopump_message[t], HEX);  
        Serial2.write(turbopump_message[t] );    
  }
  Serial.println("--");
  //Serial2.write(turbopump_message, TURBOPUMP_MESSAGE_LENGTH);

}

and the rest

// returns 0 if all is well
// 1 if The frequency converter can not run the command
// 2 During a write access: no permission to write
// 3 unknown response type
// 4 response param num or index not the same as the query
// 5 check sums did not match
// 6 did not receive one of the first starting 3 characters
int tp_receiveMessage(){


  if (Serial2.available() >= TURBOPUMP_MESSAGE_LENGTH){
    Serial.println("#FULL MSG AVAILABLE");

    // copy message to circular buffer
    
    byte firstChar;
    byte secondChar;
    byte thirdChar;
    
    firstChar = Serial2.read();
    while (firstChar != B00000010 && Serial2.available()){
      firstChar = Serial2.read();
    }
    if (firstChar != B00000010){
      return 6;
    }
    secondChar = Serial2.read();
    while (secondChar != B00010110 && Serial2.available()){
      secondChar = Serial2.read();
    }
    if (secondChar != B00010110){
      return 6;
    }
    thirdChar = Serial2.read();
    while (thirdChar != turbopumpAddr && Serial2.available()){
      thirdChar = Serial2.read();
    }
    if (thirdChar != turbopumpAddr){
      return 6;
    }
    rs485_rcvBuf[0] = firstChar;
    rs485_rcvBuf[1] = secondChar;
    rs485_rcvBuf[2] =thirdChar;

    for (int i = 3; i < TURBOPUMP_MESSAGE_LENGTH; i++) {

      rs485_rcvBuf[i] = Serial2.read();
      


    }

    // calculate check sum
    byte checkSum;
    checkSum = rs485_rcvBuf[0];
    for (int i  = 1; i < TURBOPUMP_MESSAGE_LENGTH - 1; i++){
      checkSum ^= rs485_rcvBuf[i ];
    }

    // ensure sent and calculated checksums are the same
    // if not the same, don't copy the data and ???? TODO and what?
    if (checkSum != rs485_rcvBuf[ TURBOPUMP_MESSAGE_LENGTH-1]){


      return 5;
    }
    else{

      // if the same, check that the response is for the request that you think it is
      // TODO i dont think thats necessary as i will send and receive straight after


      // get system status info
      tp_statusWord  = (rs485_rcvBuf[ 11] << 8) | rs485_rcvBuf[12 ];
      pumpRotSpeed = (rs485_rcvBuf[13] << 8) | rs485_rcvBuf[14 ];
      tp_freqConverterTemp = (rs485_rcvBuf[15] << 8) | rs485_rcvBuf[16 ];
      tp_setpointMotorCurrent = (rs485_rcvBuf[17 ] << 8) | rs485_rcvBuf[18 ];
      tp_bearingTemp = (rs485_rcvBuf[19] << 8) | rs485_rcvBuf[20];
      tp_freqConveterVS = (rs485_rcvBuf[21 ] << 8) | rs485_rcvBuf[22];

      if (tp_statusWord & 0x60C8){
        tp_error = true;
      }
      else{
        tp_error = false;
      }

      // get response type and param number
      unsigned int pke;
      byte response_type;
      unsigned int paramNum; 
      pke = (rs485_rcvBuf[3 ] << 8) | rs485_rcvBuf[4];
      response_type = (pke & 0xF000) >> 12;
      paramNum = pke & 0x0FFF;

      byte paramIndex = rs485_rcvBuf[6 ];
      
      if ( paramNum != tp_sentParamNum || paramIndex != tp_sentParamIndex){
        return 4;  
      }

      // TODO do i need this big if else? cant i just check for errors and treat the others the same?
      // whats the difference beteen response types when sending and receiving
      if ( response_type == tp_r_no_response){
        Serial.println("#tp_r_no_response");
        tp_receivedParam  = 0;
        return 0;
      }
      else if ( response_type == tp_r_sent_16){
        tp_receivedParam = (rs485_rcvBuf[9 ] << 8) | rs485_rcvBuf[10 ];

        return 0;
      } 
      else if ( response_type == tp_r_sent_32){
      
        tp_receivedParam = (rs485_rcvBuf[7 ] << 24) | (rs485_rcvBuf[8 ] << 16 )| (rs485_rcvBuf[9 ] << 8) | rs485_rcvBuf[10 ];
       
        return 0;
      } 
      else if ( response_type == tp_r_sent_field_16){
        Serial.println("#tp_r_sent_field_16");
        tp_receivedParam = (rs485_rcvBuf[9] << 8) | rs485_rcvBuf[10];
        return 0;
      } 
      else if ( response_type == tp_r_sent_field_32){
        Serial.println("#tp_r_sent_field_32");
        tp_receivedParam = (rs485_rcvBuf[7 ] << 24) | (rs485_rcvBuf[8 ] << 16 )| (rs485_rcvBuf[9 ] << 8) | rs485_rcvBuf[10 ];
        return 0;
      } 
      else if ( response_type == tp_r_sent_field_num){
        Serial.println("#tp_r_sent_field_num");
        tp_receivedParam = (rs485_rcvBuf[7 ] << 24) | (rs485_rcvBuf[8 ] << 16 )| (rs485_rcvBuf[9 ] << 8) | rs485_rcvBuf[10 ];
        return 0;
      } 
      else if ( response_type == tp_r_cmd_failed){
        return 1;
      } 
      else if ( response_type == tp_r_no_permissions){
        return 2;
      } 
      else{
        return 3;
      }

    }

  }

}
boolean tp_isError(){
  return tp_error;
}

void tp_checkWarnings(){
  if (tp_statusWord & 0x60C8){  // if the status word indicates an error C84B
    Serial.print("#PUMP ERROR - STATUS ");
    Serial.println(tp_statusWord,BIN);

    // find out what the error is
    

    Serial.print("# - PARAM 227 ");
    Serial.println(tp_getParameter(227),BIN);
    if (tp_receivedParam & 0x0001){
      raiseError(TP_MOTOR_HIGH_TEMP);

    }
    else if (tp_receivedParam & 0x0002){
      raiseError(TP_CONVERTER_TEMP);

    }
    else if (tp_receivedParam & 0x0008){
      raiseError(TP_MOTOR_LOW_TEMP);

    }
    else if (tp_receivedParam & 0x0040){
      raiseError(TP_MOTOR_OVERSPEED);

    }
    else if (tp_receivedParam & 0x0800){
      raiseError(TP_MOTOR_OVERLOAD);

    }
    else if (tp_receivedParam & 0x4000){
      raiseError(TP_LOW_SUPPLY_VOLTAGE);

    }
    else if (tp_receivedParam & 0x8000){
      raiseError(TP_FAN_VOLTAGE);

    }
    else if (tp_receivedParam == 0){
      // check error codes, param 171
      unsigned long error = tp_getParameter(947);
      Serial.print("#Error code ");
      Serial.println(error);
      raiseError(error);
      // todo handle this



    }

    if (getError() > 0 && getError() < 8 && getError() != 3){
      // stop pump
      tp_turnOffPump();
      // reset error
      tp_errorReset();

    }


  }

}

void tp_errorReset(){
  tp_controlWord |= 0x0080;
  tp_getParameter(227);
  tp_controlWord &= 0xFF7F;

}

// TODO this function sux. should be a wait state in the FSM, not a while loop
void tp_waitUntilStationary(){
  tp_sendMessage(tp_a_request,0, 0L, 0);
  while (pumpRotSpeed !=0){
    tp_sendMessage(tp_a_request,0, 0L, 0);
    Serial.print("#Waiting for turbopump to stop. Frequency is ");
    Serial.print(pumpRotSpeed);
    Serial.println(" Hz");

  }
}


}

void tp_turnOnPump(){

  tp_controlWord |= 0x401;
  tp_setFan(true);

}

void tp_turnOffPump(){
  tp_controlWord &= 0xFFFE;
  tp_sendMessage(tp_a_request, 947, 0L, 0);
  tp_receiveMessage();
  tp_setFan(false);
}


unsigned long tp_getParameter(unsigned int parameterNum){
  //Serial.println("#GET");

  tp_sendMessage(tp_a_request, parameterNum, 0L, 0);

  int errorCode;
  errorCode = tp_receiveMessage();

  if (errorCode  == 4){
    // receive 5 messages, hoping one is the right msg
    for (int i = 0; i < 5; i++){
      errorCode = tp_receiveMessage();
      if (errorCode != 4){
        break;
      }
      tp_sendMessage(tp_a_request, parameterNum, 0L, 0);
      errorCode = tp_receiveMessage();
    }
  } 
  if (errorCode == 5 || errorCode == 3){
    tp_sendMessage(tp_a_request, parameterNum, 0L, 0);
    errorCode = tp_receiveMessage();
  }

  return tp_receivedParam;
  //TODO finish me
}

void tp_setParameter(unsigned int parameterNum, int paramVal){
  
  // TODO ensure I get back the parameter i set
  
  unsigned long val = 0x0000FFFF & paramVal;
  tp_sendMessage(tp_a_write_16, parameterNum, val, 0);
  tp_receiveMessage();
}

unsigned long getTurbopumpFieldParameter(int paramNum, int paramIndex){
  // todo handle error code from receive fn
  tp_sendMessage(tp_a_request, paramNum, 0L, paramIndex);
  int errorCode;
  errorCode = tp_receiveMessage();
  //TODO remove all delays, place with timers
  if (errorCode  == 4){
    // receive 5 messages, hoping one is the right msg
    for (int i = 0; i < 5; i++){
      errorCode = tp_receiveMessage();
      if (errorCode != 4){
        break;
      }
      tp_sendMessage(tp_a_request, paramNum, 0L, paramIndex);
      errorCode = tp_receiveMessage();
    }
  } 
  if (errorCode == 5 || errorCode == 3){
    tp_sendMessage(tp_a_request, paramNum, 0L, paramIndex);
    errorCode = tp_receiveMessage();
  }

  return tp_receivedParam;
}

//true on, false off
void tp_setFan(boolean onOff){
  if (onOff){
    tp_sendMessage(tp_a_write_16, 134, 19, 0);
  }
  else{
    tp_sendMessage(tp_a_write_16, 134, 0, 0);
  }

  tp_receiveMessage();
}

boolean tp_isRunning(){
  return(tp_statusWord & 0x04);
}

// the turbopump needs even parity and the compressor needs no parity
void setEvenParity(){
  // todo ensure nothing is being sent or received

  UCSR2C &=  B11001111;  // clear parity bits
  UCSR2C |=  B00100000;  // set even parity 
}

void loop(){
  digitalWrite(RTS,HIGH);

  tp_controlWord = 0x401;
  //tp_setFan(true);

  tp_sendMessage(tp_a_request, 1, 0L, 0);

  delay(100); // make sure something is in the buffer, if something is even received. 
  tp_receiveMessage();
}

Eventually I would like to have the compressor and pump on the same RS485 line, connected to the same MAX487. I'd like to be able to communicate with each individually.

I see that as a big problem. Unless the two devices are designed to hang off a bus I don't see how they can work together.

I'll have a scan through the code but as you say it works with RS232 but not with RS485. However as you appear to be using 485 in half-duplex mode this is possibly not the same as the 232 link. For example unless you are disabling the receiver every byte you send will also be received. Does your code handle that? I can't see that it does but maybe the enable pins are hardwired. If not I'm not sure what happens if the RX buffer fills and is never emptied.


Rob

Hi,

I think the devices should be able to work on the same bus.

Here's what the compressor manual says :
The CP2800 uses SMDP (Sycon Multi Drop Protocol) for data transportation.
The SMDP protocol is a multi slave, single master protocol. The CP2800 supports
the multi slave on two-wire RS485 as well as single slave (point-to-point) on
RS232. In the extreme case, there can be many (up to 138) CP2800s on one RS485
two-wire network. In the simplest case, there can be one CP2800 connected
directly to a PC via a null-modem cable (female-female DB9 2+3 switched).

I can't find anything aboutthe pump being able to/not being able to work on a bu system at the moment but, I have read somewhere that it can.

About the transmitter/receiver enable pins. They are tied together and driven from one pin (RTS in the code). The reciver enable is inverted so that this is possible. In the code I posted, it appears I have deleted the line lowering the pin before the receive as I had commented it out while trying to figure out what's wrong.

Since then I have added an interrupt that lowers the pin once the send buffer is empty. eg

void setup(){
  Serial2.begin(19200);
  // set character size to 8
  // asynchronous normal mode USART
  // parity mode to even
  // 
  
  UCSR2C |=  _BV(UPM21) | _BV(UCSZ21) | _BV(UCSZ20);

  // enable TX complete interrupt
  UCSR2B |= 0B01000000; 
  sei();

}

ISR (USART2_TX_vect){
  digitalWrite(RTS,LOW);
}

It seems to work quite well. But, still no reply from the pump.

Also, I'm not able to communicate with the compressor either. Making me believe even more that it is a hardware issue. I've read in a few places on the net that RS485 can require a lot of experimentation and/or experience to set up. Do you have any wisdom you could share about the hardware aspects?

Thanks

Oh, and another thing. The pump termination is a "Fail-Safe"(?) termination The termination at the other end is 120 Ohms between A and B, could this be a problem?

a pic of the fail safe termination:

UPDATE: No, it shouldn't be. Z is still 120 Ohms between A and B. The other resistors are just pull up/down resistors so that if something should happen, the lines aren't floating. See here http://www.ti.com/lit/an/slla272b/slla272b.pdf , page 4

pumpTErmination.JPG

I think that termination is fine, the fail safe resistors are normally about 560R but that shouldn't matter.

If you've tied TE and RE together and are driving that signal correctly it should be OK as well.

It seems to work quite well.

How have you verified this?

BTW the code would be easier to follow if you didn't use all those binary numbers, for example

 addByteToRS485Message(B00000010, turbopump_message); // start byte

could be

#define STX 0x02
 addByteToRS485Message(STX, turbopump_message); // start byte

and

addByteToRS485Message(B00010110, turbopump_message); //Length of the payload data block in bytes.

You have to reach for the programmer's calculator just to see how many bytes in the payload.

Now you have

  addByteToRS485Message(turbopumpAddr, turbopump_message); // Address of the frequency converter RS485 0-15

And yet the SMDP documentation says

Address field. This is a one byte field. The range of values of a valid address are
10 hex to FE hex (16 to 254 decimal). Addresses less than 10 hex are not allowed
as they may be mistaken for framing characters.

I would do a HEX dump of turbopump_message after you have populated it and start counting the byte positions and applying a sanity test to the values.


Rob

It seems to work quite well.

How have you verified this?

I looked at the signal trace with an oscilloscope. The whole message is sent and immediately after the last stop bit, the differential Signal drops to 0V to allow for the pump to take control of the line. Previously there had been a delay where the differential signal had stayed high for a short amount of time before the arduino released the line. Now the differential signal is always 0V except when the arduino is sending data.

The compressor uses the SMDP protocol, not the pump. The code I supplied was only for the pump. I'e attached some info on the pump protocol. Also, it works when communicating with the pump over 232.

RS485 TD400 .pdf (228 KB)