Fun with Rotary Encoders

Hi there. I'm relatively new to Arduino and to Processing, and programming in general. I have a rotary encoder, and what I want to do is play an image sequence with every click of the encoder when its turned. I've gotten pretty close I think. Problem is, that I'm pretty sure that the signal getting sent to Processing isn't quite the same as what's printed in the serial monitor back in Arduino. I also get the error "Could not find a method to load ImageSequence.02 and .03 and .04 and so on with every click of the rotary encoder. What's the story behind this? And is there any easier way to just send the variable "count" from arduino to processing? I really appreciate the help.

This is my Arduino code:

#include <QuadEncoder.h>

int qe1Move=0;
QuadEncoder qe(8,9);

int count = 0;

void setup() {
    Serial.begin(9600);
}

void loop() {
  qe1Move=qe.hb();
  if (qe1Move=='>')
  {
  count = count + 1;
  Serial.println(count);
  if (count>46)
  {
    count = 0;
  }
  }
  if (qe1Move=='<')
  {
  count = count - 1;
  Serial.println(count);
  if (count<2)
  {
    count = 48;
  }
  }
    //Serial.print(char(qe1Move));
  //else if (qe1Move=='<')
    
    //Serial.print(char(qe1Move));
    //delay(100);
}

This is my code in processing where I'm trying to take the data and make it playback the image sequence:

import processing.serial.*;


Serial myPort;  // Create object from Serial class
int val;      // Data received from the serial port
PImage img;

void setup() 
{
  size(1280, 1280);
  // I know that the first port in the serial list on my mac
  // is always my  FTDI adaptor, so I open Serial.list()[0].
  // On Windows machines, this generally opens COM1.
  // Open whatever port is the one you're using.
  String portName = Serial.list()[0];
  myPort = new Serial(this, portName, 9600);
  img = loadImage("ImageSequence_01.png");
}

void draw() {
  image(img, 0, 0);
  if ( myPort.available() > 0) {  // If data is available,
     val = myPort.read();
     loadImage("ImageSequence_" + val); 
  }
}

I've gotten pretty close I think. Problem is, that I'm pretty sure that the signal getting sent to Processing isn't quite the same as what's printed in the serial monitor back in Arduino.

You're not trying to do both at once, are you?

  count = count + 1;

Have you ever wondered why the language used to program the Arduino is not called C=C+1?

And is there any easier way to just send the variable "count" from arduino to processing?

Easier than the one line of code you are using? No.

I also get the error "Could not find a method to load ImageSequence.02 and .03 and .04 and so on with every click of the rotary encoder. What's the story behind this?

What is the first image called?

  img = loadImage("ImageSequence_01.png");

So, why do the other images use extensions like .01, .02, etc?

     loadImage("ImageSequence_" + val);

Are you missing the .png extension?

How are the files actually numbered? The Arduino is sending '1', 2', '3', ..., '10', '11', etc. You seem to be expecting '01', '02', ..., '10', etc.

You're not trying to do both at once, are you?

Haha, no.

Have you ever wondered why the language used to program the Arduino is not called C=C+1?

I sense there is a hilarious joke here somewhere I'm not picking up on.

What is the first image called?

Ah yes, you were correct there was a disconnect in what processing was asking for and what I was trying to give. I've now renamed all files as FriendlyFire_0.png, FriendlyFire_1.png, and so on until the double digits. Then it's FriendlyFire_10.png, etc.

So, why do the other images use extensions like .01, .02, etc?

Well spotted. This was a spelling error in my post, I apologize for the inconsistency. All files use the filename + underscore + number format. I've double checked.

Are you missing the .png extension?

I was indeed!

With your help I've gotten rid of the old error messages and if been rewarded with brand new ones. Feels like progress.
Here are the new errors I'm getting with every click of the rotary encoder. The following is the result of two clicks forward and then two clicks backwards:

WARNING:  RXTX Version mismatch
	Jar version = RXTX-2.2pre1
	native lib Version = RXTX-2.2pre2
The file "FriendlyFire_52.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.
The file "FriendlyFire_48.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.
The file "FriendlyFire_52.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.
The file "FriendlyFire_49.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.
The file "FriendlyFire_52.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.
The file "FriendlyFire_48.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.
The file "FriendlyFire_51.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.
The file "FriendlyFire_57.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.

As you can see, often I get two pulses with one click of the encoder. I don't know why this would be with the result im getting off the arduino is so consistent. The encoder itself has 4 intervals in every click, but I've managed to fix that in code. So I don't know what to say. Also I have no idea why it wont go under 48. Here's my code as of now:

import processing.serial.*;

Serial myPort;  // Create object from Serial class
int val;      // Data received from the serial port
PImage img;

void setup() 
{
  size(1000, 1000);
  // I know that the first port in the serial list on my mac
  // is always my  FTDI adaptor, so I open Serial.list()[0].
  // On Windows machines, this generally opens COM1.
  // Open whatever port is the one you're using.
  String portName = Serial.list()[0];
  myPort = new Serial(this, portName, 9600);
  img = loadImage("FriendlyFire_1.png");
}

void draw() {
  image(img, 0, 0);
  if ( myPort.available() > 0) {  // If data is available,
     val = myPort.read();
     loadImage("FriendlyFire_" + val + ".png"); 
  }
}

Thanks so much for your help!

I sense there is a hilarious joke here somewhere I'm not picking up on.

What is the language called? Do you see an operator in the name that you are not using?

Also I have no idea why it wont go under 48.

  Serial.println(count);

Sends a character string like "3" to the serial port.

  if ( myPort.available() > 0) {  // If data is available,
     val = myPort.read();

Reads one byte from the serial port, as an int. The three bytes you sent were '3'. '\n', and '\r'. The ASCII values for these characters are 51, 10, and 13. The ASCII value for '0' is, not surprisingly, 48.

You need to use Serial.write() on the Arduino side, not Serial.println(), and you need to make count a byte, not an int.

This will result, when count is 3, in 3 being sent to the serial port, not "3".

Ohh, that makes sense. I've done as you've suggested and I must be close now. There are no error messages other than there being a RXTX Version mismatch.

However now it simply does not work. It opens the first image as it is asked to do in Setup, but twisting the encoder gets no response.

Here's my new arduino code:

#include <QuadEncoder.h>

int qe1Move=0;
QuadEncoder qe(8,9);

byte count = 0;

void setup() {
    Serial.begin(9600);
}

void loop() {
  qe1Move=qe.hb();
  if (qe1Move=='>')
  {
  count = count + 1;
  Serial.write(count);
  if (count>46)
  {
    count = 0;
  }
  }
  if (qe1Move=='<')
  {
  count = count - 1;
  Serial.write(count);
  if (count<2)
  {
    count = 48;
  }
  }
    //Serial.print(char(qe1Move));
  //else if (qe1Move=='<')
    
    //Serial.print(char(qe1Move));
    //delay(100);
}

You've used Serial.print() to print stuff to the Serial Monitor for debugging, right?

Processing has print() and println() functions that do the same thing, writing to the same portion of the screen where you the the mismatch error message.

I'd suggest that you add some messages to the Processing sketch to see what you are getting.

One thing you may not know is that, unlike on the Arduino, the draw() method in Processing is NOT called over and over again, as fast as possible.

It is called over and over again, but not as fast as possible. It is called at specific intervals, defined by the frame rate you are using. There is a default one (that you can change), if you have no idea what I am talking about.

Therefore, reading serial data in draw() is not a good idea. Serial data should be read in a serailEvent(Serial port)) method, instead, and the data stored in global variables that draw() can access.

Hey guys, me again. I've been working on the code some more and here's where I'm at now. Taking Paul S's advice I've taken to spitting out bytes from my Arduino and I've created a serialEvent to handle the incoming data. I'll post my code below. But first the errors.

When I run the code I get an endless stream of incoming errors that look like this:

The file "FriendlyFire_null.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.

If I spin the encoder, I get this:

The file "FriendlyFire_
.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.

In the serial port, when I spin the encoder, I get this printed:

7
8
9
1
0
1
1
1
2
1
3

Formatted just like that.

Thanks so much for your help so far. What's my next step?

Here's my code currently:

ARDUINO

#include <QuadEncoder.h>

byte qe1Move=0;
QuadEncoder qe(8,9);

byte count = 0;

void setup() {
    Serial.begin(9600);
}

void loop() {
  qe1Move=qe.hb();
  if (qe1Move=='>')
  {
  count = count + 1;
  Serial.println(count);
  if (count>46)
  {
    count = 0;
  }
  }
  if (qe1Move=='<')
  {
  count = count - 1;
  Serial.println(count);
  if (count<2)
  {
    count = 48;
  }
  }
    //Serial.print(char(qe1Move));
  //else if (qe1Move=='<')
    
    //Serial.print(char(qe1Move));
    //delay(100);
}

PROCESSING

import processing.serial.*;

Serial myPort;  // The serial port
PImage img;
String val;

void setup() {
  size(1280, 1280);
  img = loadImage("FriendlyFire_0.png");
  // List all the available serial ports
  println(Serial.list());
  // Open the port you are using at the rate you want:
  myPort = new Serial(this, Serial.list()[0], 9600);
 
}

void draw() {
  image(img, 0, 0);
  
}

void serialEvent(Serial p) { 
    // Expand array size to the number of bytes you expect
  byte[] inBuffer = new byte[7];
  while (myPort.available() > 0) {
    inBuffer = myPort.readBytes();
    myPort.readBytes(inBuffer);
    if (inBuffer != null) {
      String myString = new String(inBuffer);
      println(myString);
      val = myString;
    }
  }
}
  Serial.println(count);

is going to send something like '1', '7', '', and '' to the serial port.

void serialEvent(Serial p) {

is, because there is no Serial::bufferUntil() call, going to be called every time serial data arrives (as in when the '1' arrives, when the '7' arrives, when the '' arrives, and when the '' arrives).

  while (myPort.available() > 0) {
    inBuffer = myPort.readBytes();
    myPort.readBytes(inBuffer);

Only one of the readBytes() methods should be called. The second one, where the buffer is supplied is preferred. The first one creates a new array. The second one populates the array that you created.

It is populated with whatever data is available. When you print the data with another carriage return and line feed, stuff really spreads out.

Use the bufferUntil() method, so you only get called once when the '' or once when the '' arrives (I may have the cr/lf order backwards).

Use only one of the methods of reading the data.

Strip off the carriage return and line feed (or don't send them; send a ! after the data and bufferUntil() the ! arrives).

Don't create a new String and then assign that String instance to val. Assign the output of new String to val, instead.

What do you mean by rotary encoder? a switch with many positions? a quadrature encoder (used with motors) is not the same as a rotary encoder.

Mark

Holmes4: Here is the exact encoder I am using: http://www.sgbotic.com/index.php?dispatch=products.view&product_id=1093

PaulS: Thanks a bunch, I'll do my best to implement your suggestions this instant. I understand a good portion of it, but we're progressing a bit out of my ability to completely grasp it. If you could show me what you mean in code, that would be greatly appreciated. But if you can't be bothered, I totally understand. I'll start plugging away right now.

Hey, quick update. My processing code right now:

// Example by Tom Igoe

import processing.serial.*;

Serial myPort;  // The serial port
PImage img;
String val;
int lf = 10;
int cr = 13;

void setup() {
  size(1280, 1280);
  img = loadImage("FriendlyFire_0.png");
  // List all the available serial ports
  // Open the port you are using at the rate you want:
  myPort = new Serial(this, Serial.list()[0], 9600);
  myPort.bufferUntil(lf);
}

void draw() {
  image(img, 0, 0);
  //loadImage("FriendlyFire_" + val + ".png");  
}

void serialEvent(Serial p) { 
    // Expand array size to the number of bytes you expect
  byte[] inBuffer = new byte[7];
  while (myPort.available() > 0) {
    myPort.readBytes(inBuffer);
    if (inBuffer != null) {
      String myString = new String(inBuffer);
      println(myString);
      val = myString;
    }
  }
}

My input in the serial monitor:

47

1

2

3

What it looks like when I try to load images:
The file "FriendlyFire_null.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.
The file "FriendlyFire_null.png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.
22
The file "FriendlyFire_22
** .png" is missing or inaccessible, make sure the URL is valid or that the file has been added to your sketch and is readable.**

Etc.

I apologize for my over newness. How do I assign the output of myString to val?

So, now your serialEvent() method is only called when there is a line feed in the serial buffer. This should be the last character of the packet.

In the serialEvent() method, you then read everything in the buffer, including the carriage return and line feed, and use them as part of the file name.

You need to replace the readBytes() method with readBytesUntil(), which will let you NOT read the carriage return and line feed.

  byte[] inBuffer = new byte[7];
  while (myPort.available() > 0) {
    myPort.readBytesUntil(cr, inBuffer);

Be sure to add a call to readBytes() to strip the CR and LF from the buffer, or they will simply remain there, and be in front of the data next time.

How do I assign the output of myString to val?

    if (inBuffer != null) {
      String myString = new String(inBuffer);
      println(myString);
      val = myString;
    }

could, and should, be

    if (inBuffer != null) {
      val = new String(inBuffer);
      print("val = [");
      print(val);
      println("]");
    }

This should show

val = [47]
val = [1]
val = [2]

in the window, without all the blank lines.

Hi there, i bought this encoder (Rotary Encoder - Illuminated (Red/Green) - COM-10596 - SparkFun Electronics) that have 7 pins. I'm following this tutorial to make it work with my Arduino Uno (http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino), but i'm confusing because the one on the guide have less pins than mine. Datasheet on sparkfun site is orrible :0! Can you tell me how to connect this to Arduino ? thanks a lot

4 of the pins on that device are the encoder pins. The others are for the switch and LED. Ignore those three pins.