Shop OBEX P1 Docs P2 Docs Learn Events
Issue with reading version off of PSC — Parallax Forums

Issue with reading version off of PSC

theMoshitheMoshi Posts: 9
edited 2013-06-12 21:19 in Propeller 1
Hey everyone. I'm very new to the propeller servo controller (28830) and am having an issue reading data from the device.

Basically, I'm able to send data to the device (e.g. move servo x to position y, etc.) just fine. However, when I try to read data back, I'm getting random rubbish / nonsensical bytes.

I sure that my setup is correct because I can control all servos. Furthermore, the PSCI software works just as well. It reports the version being "1.0"

The java code I'm using attempts to read the version off of the device (I'm using Libusb and a java wrapper).
            // import ch.ntb.usb.*;

            Device device = USB.getDevice((short) 0x0403, (short)  0x6001);
            
            //public void open(int configuration, int interface_, int altinterface)
            device.open(1, 0, -1);
            
            final int endPointAddress1 = 0x81;    
            final int endPointAddress2 = 0x02;
            final byte RETURN_CARRIAGE = 0x0D;
            
            String commandStr = "!SCVER?";
            byte[] command = commandStr.getBytes();
            
            byte[] dataOut = {
                command[0],
                command[1],
                command[2],
                command[3],
                command[4],
                command[5],
                command[6],
                RETURN_CARRIAGE
            };
            
            int bytesWritten = device.writeBulk(endPointAddress2, dataOut, dataOut.length, 5000, false);
            System.out.println("Bytes written: " + bytesWritten);
            
            Thread.sleep(1000);
            
            byte[] dataIn = new byte[11];
            int bytesRead = device.readBulk(endPointAddress1, dataIn, dataIn.length, 5000, false);
            
            System.out.println("Bytes read: " + (bytesRead+1));
            for(int i = 0; i < dataIn.length; i++){
                System.out.println("Byte " + i + ": " + dataIn[i]);                
            }
            
            System.out.println("Version: " + new String(dataIn, "UTF-8"));
            device.close();

The code above outputs:
Bytes written: 8
Bytes read: 3
Byte 0: 1
Byte 1: 96
Byte 2: -85
Byte 3: 0
Byte 4: -60
Byte 5: 0
Byte 6: -85
Byte 7: 0
Byte 8: -1
Byte 9: -1
Byte 10: -1
Version: `k ? k ???

I've observed that byte 2 and byte 6 are more than often the same.

After the bulk read, is there anything that needs to be done to the data to make it human readable?

Any suggestions will be greatly appreciated.

Comments

  • SRLMSRLM Posts: 5,045
    edited 2013-06-09 23:20
    A couple of thoughts:
    1. Inside your for loop you are printing out a byte type. You should cast it to char if you want it to be human readable.
    2. Your loop upper bound should be bytesRead, not dataIn.length: the remaining bytes are junk anyway, and you don't care about them.
    3. Likewise with your String constructor from the dataIn array.
    4. For sending your data you should be able to get that all in one line: (new String"!SCVER?\n").getBytes()
    5. You should clear out the receive buffer before you send the command, just to make sure that there isn't any junk hanging around.

    I doubt these will fix your problem, but they will get you closer to the solution. Can you post your full code?
  • theMoshitheMoshi Posts: 9
    edited 2013-06-10 09:20
    Thanks for the suggestions.

    The code provided above is mostly it. Here is the entire class.
    import ch.ntb.usb.*;
    
    public class TestProp {
        
        public static void main(String[] args){
        
            try {
    
                Device device = USB.getDevice((short) 0x0403, (short)  0x6001);
                
                //public void open(int configuration, int interface_, int altinterface)
                device.open(1, 0, -1);
                
                final int endPointAddress1 = 0x81;    
                final int endPointAddress2 = 0x02;
                final byte RETURN_CARRIAGE = 0x0D;
                
                String commandStr = "!SCVER?";
                byte[] command = commandStr.getBytes();
                
                byte[] dataOut = {
                    command[0],
                    command[1],
                    command[2],
                    command[3],
                    command[4],
                    command[5],
                    command[6],
                    RETURN_CARRIAGE
                };
                
                int bytesWritten = device.writeBulk(endPointAddress2, dataOut, dataOut.length, 5000, false);
                System.out.println("Bytes written: " + bytesWritten);
                
                Thread.sleep(1000);
                
                byte[] dataIn = new byte[11];
                int bytesRead = device.readBulk(endPointAddress1, dataIn, dataIn.length, 5000, false);
                
                System.out.println("Bytes read: " + (bytesRead+1));
                for(int i = 0; i < dataIn.length; i++){
                    System.out.println("Byte " + i + ": " + dataIn[i]);                
                }
                
                System.out.println("Version: " + new String(dataIn, "UTF-8"));
                device.close();
            }
            catch(Exception e{
                e.printStackTrace();
            }       
        }
    }
    


    All usb/device API calls are from LibusbJava:
    API: http://libusbjava.sourceforge.net/wp/res/doc/index.html
    Ref: http://libusbjava.sourceforge.net/wp/
  • SRLMSRLM Posts: 5,045
    edited 2013-06-10 10:14
    You should try to implement the changes that I suggested. Another idea is to check your understanding of libusb by using an FTDI chip (such as a Prop plug or a generic serial<->USB dongle) with a loopback (connect RX and TX by a resistor). With that setup you should receive whatever you send, like an echo.

    Edit: I'm not familiar with libusb. It looks like a library to read *any* device that is connected to USB, which is a bit excessive for a serial port. I'm sure it would work, but I'm not so sure that you can just send and receive bytes like you are. Do you have a link to an example of using libusb in this way?
  • theMoshitheMoshi Posts: 9
    edited 2013-06-10 20:57
    I tried the suggestions, but no luck. Thanks though. The code looks more neater now.

    You're correct, Libusb is a bit excessive since its a generic library for communicating with usb devices. Unfortunately, my knowledge of usb technology is immature and this seemed like the route I should take.
    What would you suggest is the easiest way to go about working the the PSC? I don't have a basic stamp and therefore my only means of communicating with PSC is through is its usb port directly. Any particular language/tools?

    I was able to dig out the following class posted on these forums, but am unable to find the exact thread they belong to. It appears that the author is performing some sort of bit wise operations on the version before returning.
    //VER? Command – Identify Firmware Version Number
      //Syntax: “!SCVER?” $0D
      //Reply: “1.3”
      /**
       * Get PSC firmware version.
       * In case of networked boards, this method returns the version of the first board.
       *
       * @return Version number expressed as ASCII characters in an int.
       *         If version is "1.3" then return value is 0x3133
       */
      public int version() {
        if (tx == rx) tx.setDirection(Uart.dirTransmit);
        tx.sendString("!SCVER?\r");
        getReply(8,3);
        return (reply[0]<<8)|(reply[2]&0xFF);
      }
    

    Does this mean anything?

    The entire code:
    package stamp.peripheral.servo.psc;
    import stamp.core.*;
    import stamp.math.*;
    
    /**
     * Class for Parallax Servo Controller (aka PSC).
     * It allows servo angle control which is 0.0 to 180.0 degrees (0.1 degree resolution) for all servos.
     * The fact that different servos may have different position ranges, is hidden in the class.
     *
     * Javelin can connect to PSC via one pin or two pins:
     *
     * One pin:   o---JavPin--------PscPin---o (this pin has 220E pullup resistor with LED)
     *            In this case receive uart equals transmit uart
     *            static Uart pscIO = new Uart(Uart.dirReceive,CPU.pin0,Uart.dontInvert,Uart.speed2400,Uart.stop1);
     *            static psc myPsc = new psc(pscIO,pscIO,3); //single PSC board with 3 servos
     *
     * Two pins:  o---JavRxPin-+----PscPin---o (this pin has 220E pullup resistor with LED)
     *                         |
     *                  [220 ohm resistor]
     *                         |
     *            o---JavTxPin-+
     *            In this case there are seperate receive uart and transmit uart
     *            and anything sent is directly received (local echo)
     *            static Uart pscTx = new Uart(Uart.dirTransmit,CPU.pin0,Uart.dontInvert,Uart.speed2400,Uart.stop1);
     *            static Uart pscRx = new Uart(Uart.dirReceive,CPU.pin1,Uart.dontInvert,Uart.speed2400,Uart.stop1);
     *            static psc myPsc = new psc(pscTx,pscRx,3); //single PSC board with 3 servos
     *
     * In main() initialize servos
     *    myPsc.initChannel(0,250,1250,-750,0);
     *    myPsc.initChannel(1,250,1250,-750,0);
     *    myPsc.initChannel(2,250,1250,-750,0);
     *
     * @version 1.5
     * @author Peter Verkaik (peterverkaik@boselectro.nl)
     * Date 12-dec-2005
     */
    
    public class psc {
    
      public int[] Pmin; //default 250
      public int[] Pmax; //default 1250
      public int[] Phome; //default 750
      private Uart rx;
      private Uart tx;
      private char[] reply = new char[6]; //holds replies
      private boolean networked;
    
      /**
       * Constructor for PSC.
       *
       * @param tx Transmit uart (2400 baud, 8N1)
       * @param rx Receive uart (2400 baud, 8N1)
       * @param channels Number of servos
       *                 1-16 for single PSC board
       *                 17-32 for two networked PSC boards
       */
      public psc(Uart tx, Uart rx, int channels) {
        this.tx = tx;
        this.rx = rx;
        this.Pmin = new int[channels];
        this.Pmax = new int[channels];
        this.Phome = new int[channels];
        this.networked = (channels>16); //two PSC boards are networked if more than 16 channels
      }
    
      /**
       * Initialize PSC channel.
       *
       * @param channel Channel to initialize
       * @param Pmin Minimum position value, default 250.
       *             If your servos appear to strain when commanded to position 250, you should increase
       *             the 250 to 260 or so to prevent the servo from straining itself.
       * @param Pmax Maximum position value, default 1250.
       *             If your servos appear to strain when commanded to position 1250, you should decrease
       *             the 1250 to 1240 or so to prevent the servo from straining itself.
       * @param home Angle (0.1 degree units 0-1800) or position (-Pmin to -Pmax) to identify home position
       * @param ramp Specifies time to get to home position (0-63)
       *             If the ramp parameter is set to 0, ramping is disabled and the pulse width will be set to the
       *             position parameter. Ramp values of 1-63 correspond to speeds from 0.75 second up to 60 seconds
       *             for a full 500uSec to 2.50 mSec excursion.
       */
      public void initChannel(int channel, int Pmin, int Pmax, int home, int ramp) {
        this.Pmin[channel] = Pmin;
        this.Pmax[channel] = Pmax;
        this.Phome[channel] = (home < 0) ? -home : position(channel,home);
        setHome(channel,ramp); //move servo to home position at rate ramp
      }
    
      //VER? Command – Identify Firmware Version Number
      //Syntax: “!SCVER?” $0D
      //Reply: “1.3”
      /**
       * Get PSC firmware version.
       * In case of networked boards, this method returns the version of the first board.
       *
       * @return Version number expressed as ASCII characters in an int.
       *         If version is "1.3" then return value is 0x3133
       */
      public int version() {
        if (tx == rx) tx.setDirection(Uart.dirTransmit);
        tx.sendString("!SCVER?\r");
        getReply(8,3);
        return (reply[0]<<8)|(reply[2]&0xFF);
      }
    
      /**
       * Get PSC firmware version.
       * In case of networked boards, this method returns the version of the second board.
       * Method version() must be called prior to this method.
       *
       * @return Version number expressed as ASCII characters in an int.
       *         If version is "1.3" then return value is 0x3133
       */
      public int version2() {
        return (reply[3]<<8)|(reply[5]&0xFF);
      }
    
      //SBR Command – Set the Baudrate (to either 2400 or 38K4 Baud)
      //Syntax: “!SCSBR” x $0D
      //Reply: “BR” x (where x is either 0 for 2400, or 1 for 38K4)
      //As Javelin may have trouble receiving at 38K4, this is not available
    
      /**
       * Move servo to home position.
       *
       * @param channel Servo to move to home position
       * @param ramp Specifies time to get to position (0-63)
       *             If the ramp parameter is set to 0, ramping is disabled and the pulse width will be set to the
       *             position parameter. Ramp values of 1-63 correspond to speeds from 0.75 second up to 60 seconds
       *             for a full 500uSec to 2.50 mSec excursion.
       */
      public void setHome(int channel, int ramp) {
        setPosition(channel,ramp,Phome[channel]);
      }
    
      //Position Command – Set the Position of a Servo Channel
      //Syntax: “!SC” C R pw.LOWBYTE, pw.HIGHBYTE, $0D
      //Reply: none
      /**
       * Set PSC servo position
       *
       * @param channel Servo channel (0-31)
       * @param ramp Specifies time to get to position (0-63)
       *             If the ramp parameter is set to 0, ramping is disabled and the pulse width will be set to the
       *             position parameter. Ramp values of 1-63 correspond to speeds from 0.75 second up to 60 seconds
       *             for a full 500uSec to 2.50 mSec excursion.
       * @param position Servo position to get to (Pmin to Pmax)
       */
      public void setPosition(int channel, int ramp, int position) {
        if (tx == rx) tx.setDirection(Uart.dirTransmit);
        tx.sendString("!SC");
        tx.sendByte(channel);
        tx.sendByte(ramp);
        tx.sendByte(position);
        tx.sendByte(position>>>8);
        tx.sendByte(0x0D);
        getReply(8,0);
      }
    
      /**
       * Set PSC servo angle
       *
       * @param channel Servo channel (0-31)
       * @param ramp Specifies time to get to position (0-63)
       *             If the ramp parameter is set to 0, ramping is disabled and the pulse width will be set to the
       *             position parameter. Ramp values of 1-63 correspond to speeds from 0.75 second up to 60 seconds
       *             for a full 500uSec to 2.50 mSec excursion.
       * @param angle Servo angle (0.0-180.0 degrees) to get to (value 0-1800)
       */
      public void setAngle(int channel, int ramp, int angle) {
        setPosition(channel,ramp,position(channel,angle));
      }
    
      //RSP Command – Report the Position of a Servo Channel
      //Syntax: “!SCRSP” x $0D
      //Reply: x y z(where x is the channel number, and y:z is the value reported)
      /**
       * Get PSC servo position
       *
       * @param channel Servo channel (0-31)
       * @return Current servo position (Pmin to Pmax)
       */
      public int getPosition(int channel) {
        if (tx == rx) tx.setDirection(Uart.dirTransmit);
        tx.sendString("!SCRSP");
        tx.sendByte(channel);
        tx.sendByte(0x0D);
        getReply(8,3);
        int offset = (channel<16) ? 0 : 3;
        return (reply[offset+1]<<8)|(reply[offset+2]&0xFF);
      }
    
      /**
       * Get PSC servo angle
       *
       * @param channel Servo channel (0-31)
       * @return Current servo angle 0.0-180.0 degrees (value 0-1800)
       */
      public int getAngle(int channel) {
        return angle(channel,getPosition(channel));
      }
    
      //convert position to angle
      //default position range 1250-250=1000 equals 180 degrees, so angle resolution is 0.18 degrees
      //the conversion uses 0.1 degree resolution
      // A = (p-pmin)/(pmax-pmin)*1800
      //   = (p-pmin)*(1800/(pmax-pmin))
      //   = (p-pmin)*(I + F/65536)
      //   = (p-pmin)*I + UnsignedIntMath(p-pmin,F)
      /**
       * Convert positions Pmin to Pmax to degrees 0-180.
       *
       * @param channel Servo channel (0-31)
       * @param position Value Pmin[channel] to Pmax[channel].
       * @return Corresponding angle 0.0-180.0 degrees (value 0-1800).
       */
      public int angle(int channel, int position) {
        //formulae: A = (position-Pmin[channel])/(Pmax[channel]-Pmin[channel])*1800;
        int d = Pmax[channel] - Pmin[channel];
        int I = 1800/d;
        int offset = position - Pmin[channel];
        int frac = UnsignedIntMath.ufrac(1800,d);
        return (offset*I) + UnsignedIntMath.umulf(offset,frac);
      }
    
      //convert angle to position
      //default position range 1250-250=1000 equals 180 degrees, so angle resolution is 0.18 degrees
      //the conversion uses 0.1 degree resolution
      // P = (A/1800)*(pmax-pmin) + pmin
      //   = A * (I + F/65536) + pmin
      // (pmax-pmin)/1800 = I + (F/65536)
      // I = (pmax-pmin)/1800
      // R = pmax - pmin - 1800*I
      // F = 65536 * R / 1800 = 36.40888 * R
      //   = 36*R + 0.40888*R
      //   = ((9*R)<<2) + (26796.94222/65536)*R
      //   = ((9*R)<<2) + UnsignedIntMath.umulf(R,26797)
      // P = A * (I + F / 65536) + pmin
      //   = A*I + UnsignedIntMath(A,F) + pmin
      /**
       * Convert angle 0.0-180.0 degrees (value 0-1800) to position Pmin to Pmax.
       *
       * @param channel Servo channel (0-31)
       * @param angle Angle 0.0-180.0 (value 0 to 1800).
       * @return Corresponding position Pmin[channel] to Pmax[channel].
       */
      public int position(int channel, int angle) {
        //formulae: P = (angle*(Pmax[channel]-Pmin[channel])/1800)+Pmin[channel];
        int range = Pmax[channel]-Pmin[channel];
        int I = range/1800;
        int R = range - (1800*I);
        int F = ((9*R)<<2) + UnsignedIntMath.umulf(R,26797);
        return (angle*I) + UnsignedIntMath.umulf(angle,F) + Pmin[channel];
      }
    
      //If a command causes the PSC to reply, a three-byte reply is sent after a 1.5 mS delay.
      //It is possible to network two PSCs together to control up to 32 servos. Please note that in this configuration
      //when you send a serial command to one unit, you send it to both. Commands that set servo positions are the
      //only commands ignored by the other unit. All other commands invoke a reply.
      //Unit 0 replies first. Unit 1 replies approximately 3 mS later.
      /**
       * Get reply from PSC.
       * This method removes any local echoes that may have been received.
       *
       * @param echoes Number of bytes of the prior command
       * @param databytes Number of bytes in PSC reply
       */
      private void getReply(int echoes, int databytes) {
        int i = 0;
        if (networked) databytes <<= 1; //0 remains 0, 3 becomes 6
        while (!tx.sendBufferEmpty()) ; //wait until transmit buffer empty and nothing is sent
        if (tx == rx) rx.setDirection(Uart.dirReceive);
        else { //local echoes
          while (echoes > 0) {
            reply[0] = (char)rx.receiveByte();
            echoes--;
          }
        }
        while (i < databytes) {
          reply[i++] = (char)rx.receiveByte();
        }
      }
    
    }
    
  • SRLMSRLM Posts: 5,045
    edited 2013-06-12 21:19
    I got this in a PM:
    What language/platform/library would you recommend using for communicating with the PSC? I don't have the basic stamp so I would need to directly communicate with the usb port on the PSC from my pc.

    I think Python is the easiest to use to read a serial port. The pyserial library is simple and cross platform without qualifications. I've used it to make a GUI for a Propeller system (http://www.youtube.com/watch?v=viVVwRabck4). Java is supposed to be cross platform, but it has terrible serial port support. The rxtx library isn't very good either (it's not very active for open source, and it doesn't follow the Java IO stream convention).
Sign In or Register to comment.