August 2, 2015

Avocent Alterpath PM10 controlled by Arduino

A while back I had grabbed a very good deal off ebay for a used Avocent Alterpath PM10.


It is an ten port intelligent PDU with a serial port allowing on/off control and status for each of the ten outlets.  I had quite a bit of trouble getting the serial port working with a usb to 232 adaptor as the pinout is not really we defined and the device requires the hardware handshake lines to be connected before you get a response.  However after quite a few hours of trial and error I figured it out and got it working via usb.  Next I grabbed cheap max232 based RS232 to TTL adaptor off ebay for a few bucks and connected it to an Arduino to see if I could get it to control the PM10 as well in hope of then connecting it to my home automation system.

I ended up bypassing the db9 connector as the PM10 uses a rj45 connector.  The wiring is pretty simple, connect the RX/TX lines on the max232 board to pin 3 and 6, then ground to pin 4.  After that connect pin 2,7 and 8 together.  This is the magic that gets the hardware handshaking working as it loops it back on it self.  Without that the PM10 will not respond.  You should see a rx packet as soon as you connect the RJ45 as the device sees the hardware line connect and will send a login prompt.

RJ45 Pinout

1 (N/C)
2 (Connect to pin 7 & 8)
3 (T/X)
4 (Ground)
5 (N/C)
6 (RX)
7 (Connect to pin 2 & 8)
8 (Connect to pin 2 & 7)

With the pinout above I got basic serial communication working on the Arduino using the default software serial example.  With a bit of tweaking I created the following code to send and receive data from the PM10.  Sending a "on 1" or "off 1" will turn on or off outlet 1.  You can look at the instruction guide to see all the serial commands available.  This is a pretty basic example but with the  hard part figured out it will be easy to integrate into my home automation system with a bit of extra tweaking.


#include ;

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()  
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  Serial.println("Starting:");

  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);
  
}

void loop()
{  
  if (mySerial.available()){
    mySerial.read();
  }

  if (Serial.available()){
    char buf[100], obuf[500], i=0;
    memset(buf,0,100);

    while(Serial.available()){
      buf[i] = Serial.read();
      delay(5);
      i++;
    }
    if(i>0){
      memset(obuf,0,500);
      int val = sendPSULogin(buf,obuf,100);
      Serial.print("Login = ");
      Serial.println(val);
      Serial.println(obuf);
    }
  }
}

int sendPSULogin(char* cmd, char* obuf, char len){
    char buf[100], i=0;
    mySerial.write("\r");
    delay(5);
    memset(buf,0,100);
    i=0;
    while(mySerial.available()){
      buf[i] = mySerial.read();
      i++;
    }
    if(strstr(buf, "User")){
      mySerial.write("admin\rpm8\r");
      delay(5);
      memset(buf,0,100);
      i=0;
      while(mySerial.available()){
        buf[i] = mySerial.read();
        i++;
      }
      if(strstr(buf, "pm>;")){
        if(buf){
          mySerial.write(cmd);
          mySerial.write("\r");
          delay(5);
          memset(buf,0,100);
          i=0;
          while(mySerial.available() && i<len){
            obuf[i] = mySerial.read();
            delay(5);
            i++;
          }
          mySerial.write("exit\r");
        }
        return 1;
      } else {
        return -1; 
      }
    } if(strstr(buf, "pm>")){
      if(buf){
        mySerial.write(cmd);
        mySerial.write("\r");
        delay(5);
        memset(buf,0,100);
        i=0;
        while(mySerial.available() && i<len){
          obuf[i] = mySerial.read();
          delay(5);
          i++;
        }
        mySerial.write("exit\r");
      }
      return 0;
    } else {
      return -1; 
    }
    
    return -1;
}