processing servo control

Hello,

I'm trying to make Processing control a servo, but it won't even recognise its own methods..

"the function servoAttach(int) does not exist"

This error occurs even with the servo-tutorial file of the arduino library (servo01.pde).

Changing the state of a digital pin works without any problem, so does servo control when I use the firmata test program, so it's definitely an internal Processing issue. I already googled some cases of Processing ignoring parts of its own API, but it's always due to some syntax-sloppiness and stuff.

processing 1.5.1
arduino duemilanove
standard firmata 0022

I'm trying to make Processing control a servo, but it won't even recognise its own methods..

I'm guess that this is another one of those "syntax-sloppiness and stuff" things that you will later be able to google.

processing 1.5.1
arduino duemilanove
standard firmata 0022

And YOUR code?

And YOUR code?

Well lets take the tutorial file I was talking about. But it's the same thing with any code containing servoAttach.

import processing.serial.*;
import cc.arduino.*;

Arduino arduino;
int pos=0; //servo position in degrees (0..180)

void setup() {
  size(360, 200);
  
  arduino = new Arduino(this, Arduino.list()[1], 57600); //your offset may vary
  
  //initialize the servo on pin 9
  arduino.servoAttach(9);
  // set minimum and maximum pulse times
  arduino.servoSetMinPulseTime(9,500);
  arduino.servoSetMaxPulseTime(9,2500);

}

void draw() {
  // read mouseX coordinate
  int newPos = constrain(mouseX/2,0,180); 

  // update bg & servo position if mouseX changed
  if(newPos != pos) {
    background(newPos);
    arduino.servoWrite(9,newPos);
    arduino.servoWrite(10,180-newPos);
    pos=newPos; //update servo position storage variable
  }
}

Output: "the function servoAttach(int) does not exist"

Well lets take the tutorial file I was talking about. But it's the same thing with any code containing servoAttach.

Where did you get the tutorial file you are talking about? Where did you get the idea (other than that tutorial) that servoAttach was a valid method in the Arduino class?

Here: protolab / ArduinoProcessingLibraryReference

It's the only hint on servo controling via processing/firmata I could find so far. If that's useless, please just tell me how to do it right. All I want is to pass an angle to firmata to make my servo move. It has the servo library included, so it shouldn't be a big problem I guess.

I started here:
http://processing.org/
There is a link, Reference, that has a Libraries link that has an Arduino link, to here:
http://www.arduino.cc/playground/Interfacing/Processing

On that page, there is a zip file that contains a src folder containing Arduino.java, that looks like this:

/**
 */
 
package cc.arduino;

import processing.core.PApplet;
import processing.serial.Serial;

/**
 * Together with the Firmata 2 firmware (an Arduino sketch uploaded to the
 * Arduino board), this class allows you to control the Arduino board from
 * Processing: reading from and writing to the digital pins and reading the
 * analog inputs.
 */
public class Arduino {
  /**
   * Constant to set a pin to input mode (in a call to pinMode()).
   */
  public static final int INPUT = 0;
  /**
   * Constant to set a pin to output mode (in a call to pinMode()).
   */
  public static final int OUTPUT = 1;
  /**
   * Constant to set a pin to analog mode (in a call to pinMode()).
   */
  public static final int ANALOG = 2;
  /**
   * Constant to set a pin to PWM mode (in a call to pinMode()).
   */
  public static final int PWM = 3;
  /**
   * Constant to set a pin to servo mode (in a call to pinMode()).
   */
  public static final int SERVO = 4;
  public static final int SHIFT = 5;
  public static final int I2C = 6;

  public static final int LOW = 0;
  public static final int HIGH = 1;
  
  private final int MAX_DATA_BYTES = 32;
  
  private final int DIGITAL_MESSAGE        = 0x90; // send data for a digital port
  private final int ANALOG_MESSAGE         = 0xE0; // send data for an analog pin (or PWM)
  private final int REPORT_ANALOG          = 0xC0; // enable analog input by pin #
  private final int REPORT_DIGITAL         = 0xD0; // enable digital input by port
  private final int SET_PIN_MODE           = 0xF4; // set a pin to INPUT/OUTPUT/PWM/etc
  private final int REPORT_VERSION         = 0xF9; // report firmware version
  private final int SYSTEM_RESET           = 0xFF; // reset from MIDI
  private final int START_SYSEX            = 0xF0; // start a MIDI SysEx message
  private final int END_SYSEX              = 0xF7; // end a MIDI SysEx message

  PApplet parent;
  Serial serial;
  SerialProxy serialProxy;
  
  int waitForData = 0;
  int executeMultiByteCommand = 0;
  int multiByteChannel = 0;
  int[] storedInputData = new int[MAX_DATA_BYTES];
  boolean parsingSysex;
  int sysexBytesRead;

  int[] digitalOutputData = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  int[] digitalInputData  = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  int[] analogInputData   = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

  int majorVersion = 0;
  int minorVersion = 0;
  
  public class SerialProxy extends PApplet {
    public SerialProxy() {
      disposeMethods = new RegisteredMethods();
    }

    public void serialEvent(Serial which) {
      // Notify the Arduino class that there's serial data for it to process.
      while (available() > 0)
        processInput();
    }
  }
  
  public void dispose() {
    this.serial.dispose();
  }
  
  public static String[] list() {
    return Serial.list();
  }

  public Arduino(PApplet parent, String iname) {
    this(parent, iname, 57600);
  }
  
  public Arduino(PApplet parent, String iname, int irate) {
    this.parent = parent;
    this.serialProxy = new SerialProxy();
    this.serial = new Serial(serialProxy, iname, irate);

    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {}
		
    for (int i = 0; i < 6; i++) {
      serial.write(REPORT_ANALOG | i);
      serial.write(1);
    }

    for (int i = 0; i < 2; i++) {
      serial.write(REPORT_DIGITAL | i);
      serial.write(1);
    }
    
    parent.registerDispose(this);
  }
  
  /**
   * Returns the last known value read from the digital pin: HIGH or LOW.
   *
   * @param pin the digital pin whose value should be returned (from 2 to 13,
   * since pins 0 and 1 are used for serial communication)
   */
  public int digitalRead(int pin) {
    return (digitalInputData[pin >> 3] >> (pin & 0x07)) & 0x01;
  }

  /**
   * Returns the last known value read from the analog pin: 0 (0 volts) to
   * 1023 (5 volts).
   *
   * @param pin the analog pin whose value should be returned (from 0 to 5)
   */
  public int analogRead(int pin) {
    return analogInputData[pin];
  }

  /**
   * Set a digital pin to input or output mode.
   *
   * @param pin the pin whose mode to set (from 2 to 13)
   * @param mode either Arduino.INPUT or Arduino.OUTPUT
   */
  public void pinMode(int pin, int mode) {
    serial.write(SET_PIN_MODE);
    serial.write(pin);
    serial.write(mode);
  }

  /**
   * Write to a digital pin (the pin must have been put into output mode with
   * pinMode()).
   *
   * @param pin the pin to write to (from 2 to 13)
   * @param value the value to write: Arduino.LOW (0 volts) or Arduino.HIGH
   * (5 volts)
   */
  public void digitalWrite(int pin, int value) {
    int portNumber = (pin >> 3) & 0x0F;
  
    if (value == 0)
      digitalOutputData[portNumber] &= ~(1 << (pin & 0x07));
    else
      digitalOutputData[portNumber] |= (1 << (pin & 0x07));

    serial.write(DIGITAL_MESSAGE | portNumber);
    serial.write(digitalOutputData[portNumber] & 0x7F);
    serial.write(digitalOutputData[portNumber] >> 7);
  }
  
  /**
   * Write an analog value (PWM-wave) to a digital pin.
   *
   * @param pin the pin to write to (must be 9, 10, or 11, as those are they
   * only ones which support hardware pwm)
   * @param the value: 0 being the lowest (always off), and 255 the highest
   * (always on)
   */
  public void analogWrite(int pin, int value) {
    pinMode(pin, PWM);
    serial.write(ANALOG_MESSAGE | (pin & 0x0F));
    serial.write(value & 0x7F);
    serial.write(value >> 7);
  }

  private void setDigitalInputs(int portNumber, int portData) {
    //System.out.println("digital port " + portNumber + " is " + portData);
    digitalInputData[portNumber] = portData;
  }

  private void setAnalogInput(int pin, int value) {
    //System.out.println("analog pin " + pin + " is " + value);
    analogInputData[pin] = value;
  }

  private void setVersion(int majorVersion, int minorVersion) {
    //System.out.println("version is " + majorVersion + "." + minorVersion);
    this.majorVersion = majorVersion;
    this.minorVersion = minorVersion;
  }

  private int available() {
    return serial.available();
  }

  private void processInput() {
    int inputData = serial.read();
	  int command;
    
    if (parsingSysex) {
      if (inputData == END_SYSEX) {
        parsingSysex = false;
        //processSysexMessage();
      } else {
        storedInputData[sysexBytesRead] = inputData;
        sysexBytesRead++;
      }
    } else if (waitForData > 0 && inputData < 128) {
      waitForData--;
      storedInputData[waitForData] = inputData;
      
      if (executeMultiByteCommand != 0 && waitForData == 0) {
        //we got everything
        switch(executeMultiByteCommand) {
        case DIGITAL_MESSAGE:
          setDigitalInputs(multiByteChannel, (storedInputData[0] << 7) + storedInputData[1]);
          break;
        case ANALOG_MESSAGE:
          setAnalogInput(multiByteChannel, (storedInputData[0] << 7) + storedInputData[1]);
          break;
        case REPORT_VERSION:
          setVersion(storedInputData[1], storedInputData[0]);
          break;
        }
      }
    } else {
      if(inputData < 0xF0) {
        command = inputData & 0xF0;
        multiByteChannel = inputData & 0x0F;
      } else {
        command = inputData;
        // commands in the 0xF* range don't use channel data
      }
      switch (command) {
      case DIGITAL_MESSAGE:
      case ANALOG_MESSAGE:
      case REPORT_VERSION:
        waitForData = 2;
        executeMultiByteCommand = command;
        break;      
      }
    }
  }
}

No servoAttach() method there. The protolab site must be using a different Arduino.java file.

I was able to get a servo to move using this Processing app:

import processing.serial.*;
import cc.arduino.*;
Arduino arduino;
int pos=0; //servo position in degrees (0..180)

void setup()
{
  size(360, 200);

  arduino = new Arduino(this, Arduino.list()[1], 57600); //your offset may vary
  arduino.pinMode(9, 5);
}

void draw()
{
// read mouseX coordinate
  int newPos = constrain(mouseX/2,0,180); // update bg & servo position if mouseX changed
  if(newPos != pos)
  {
    background(newPos);
    arduino.analogWrite(9, newPos);
    println (" newPos = " + newPos );
    pos=newPos; //update servo position storage variable
  }
}

with the StandardFirmata sketch on the Arduino.

I know the Java file says SERVO is 4, but the Arduino sketch didn't seem to agree with that, but 5 worked. If it doesn't work for you, change it back to 4.

This code is quite similar to "arduino_pwm" in the arduino library examples. It makes the servo move, but it's jerking all the time and doesn't stop buzzing until I hit reset. I tried replacing the mouse control part with this simple instruction but it's still like an epileptic seizure.

void draw() {
  arduino.analogWrite(9,140);
  delay(500);
  arduino.analogWrite(9,160);
  delay(500);
}

Have you tried the ServoFirmata sketch on the Arduino?

It works with servoFirmata. But as far as I know you can only adress servos with it and my project requires also a small motor on digitalPin 8..

Firmata is not magic. It is some code on the Arduino to react to serial data that arrives on the serial port (in some tightly defined format). Look at the ServoFirmata.pde and SimpleAnalogFirmata.pde files. The analogWriteCallback() method is called to handle the message that arrived. In one sketch, non-servo pin data is ignored. In the other, non-analog pin data is ignored.

Combine the two sketches, so that one function has two loops that each ignore the wrong kind of data.