HELP!! Using rotary encoder to measure Laser Cutter height and display it

Help needed please. I am a new recruit to Arduino and learning slowly. I have adopted oled display routines to show the height of a laser nozzle above the bed using a rotary encoder attatched to the up/down leadscrew.

Works a treat except that it forgets its position when turned off and leaving it on leads to display burn in. I want to use EEPROM.update to save the position. This works but reading it back on boot up doesn`t. I suspect its to do with the interrupt routine (borrowed off the net) not being called so not updating. Any help appreciated.

/**************************************************************************
 This is an example for our Monochrome OLEDs based on SSD1306 drivers

 Pick one up today in the adafruit shop!
 ------> http://www.adafruit.com/category/63_98

 This example is for a 128x32 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 Adafruit invests time and resources providing this open
 source code, please support Adafruit and open-source
 hardware by purchasing products from Adafruit!

 Written by Limor Fried/Ladyada for Adafruit Industries,
 with contributions from the open source community.
 BSD license, check license.txt for more information
 All text above, and the splash screen below must be
 included in any redistribution.
 **************************************************************************/



// Rotary encoder oled flipped using interrupt routines. I want it to save the current rotary value to eeprom as an update and read it back after reset
//  Thus remembering the current position after reset.
 #include <SPI.h>
#include <Wire.h>
#include <RotaryEncoder.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
Adafruit_SSD1306 display(4);

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
float z=0.0;// set up floating point variable
// Setup a RotaryEncoder for pins A2 and A3  on encoder output A and B use A2 andA3
RotaryEncoder encoder(A2, A3);

void setup()
{
  int value;
int newPos;
 value = EEPROM.read(0);   //read position from eeprom
if(value != 0)  //  test if there was a reset and if so use this value
 {
 newPos = value; 
 }
 
 // Serial.begin(9600); //for monitoring output debugging
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.setRotation(2); //rotate screen 180 degrees optional
  // You may have to modify the next 2 lines if using other pins than A2 and A3
  PCICR |= (1 << PCIE1);    // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
  PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);  // This enables the interrupt for pin 2 and 3 of Port C.
}


// The Interrupt Service Routine for Pin Change Interrupt 1
// This routine will only be called on any signal change on A2 and A3: exactly where we need to check.
ISR(PCINT1_vect) {
  encoder.tick(); // just call tick() to check the state.
}
// Read the current position of the encoder and print out when changed

void loop()
{
  

  static int pos = 0;
 display.display();
 
 
 
  int newPos = encoder.getPosition();
  if (pos != newPos) {
    Serial.print(newPos);
    display.clearDisplay();
    Serial.println();
     display.setTextSize(1);      // Normal 1:1 pixel scale
   display.setTextColor(WHITE);
  
   display.setCursor(4,1);
display.println("Laser Focus Distance");
   display.setTextSize(2); 
 display.setCursor(15, 14);

float(z)=newPos/5.0;// scalefactor is 5.0 for my 4mm pitch leadscrew
   display.println(float(z)); // display height as 2 dec places
    display.setCursor(80, 14);
   display.println("mm");
  // Serial.println(float(z));
   display.display(); //finalises and displays changes
    pos = newPos;
    EEPROM.update(0,pos);

    
  } 
} 

// The End

More suspect would be that you do not have a home position for the lead screw and a switch to tell the Arduino when it gets there and the code to find the home position when power is applied.

Paul

Edit your post and put your code inside code tags

[code]your code here [/code]

Paul. Once the laser focus is set, by dropping the nozzle onto the bed, the display shows height relative to this zero. I agree that I need to add a button to clear the eeprom and reset to zero when say changing the lens and that is my next step once I sort the save and load eeprom routine out. I don't want to have to zero it on startup as there would be no need.

**Here is my solution I arrived at after much thought. It works well for positive integers 0 to 255. Next I would like to extend it to larger than 8 bit numbers so that Iit works for more turns of the leadscrew and so that I can save negative numbers as well. Thats the next task. :slight_smile: **

/**************************************************************************
 This is an example for our Monochrome OLEDs based on SSD1306 drivers

 Pick one up today in the adafruit shop!
 ------> http://www.adafruit.com/category/63_98

 This example is for a 128x32 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 Adafruit invests time and resources providing this open
 source code, please support Adafruit and open-source
 hardware by purchasing products from Adafruit!

 Written by Limor Fried/Ladyada for Adafruit Industries,
 with contributions from the open source community.
 BSD license, check license.txt for more information
 All text above, and the splash screen below must be
 included in any redistribution.
 **************************************************************************/



// Rotary encoder oled flipped using interrupt routines. I want it to save the current rotary value to eeprom as an update and read it back after reset
//  Thus remembering the current position after reset. Works well for positive integers 0 to 255.   Would like to extend this to 16 bit numbers giving
//  more scope for larger numbers I could split to give negative numbers also.
 #include <SPI.h>
#include <Wire.h>
#include <RotaryEncoder.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
Adafruit_SSD1306 display(4);

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
float z=0.0;// set up floating point variable
// Setup a RotaryEncoder for pins A2 and A3  on encoder output A and B use A2 andA3
RotaryEncoder encoder(A2, A3);
  int count=0;
int value;
  int total;
void setup()
{

int newPos;

 
 // Serial.begin(9600); //for monitoring output debugging
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.setRotation(2); //rotate screen 180 degrees optional
  // You may have to modify the next 2 lines if using other pins than A2 and A3
  PCICR |= (1 << PCIE1);    // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
  PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);  // This enables the interrupt for pin 2 and 3 of Port C.
}


// The Interrupt Service Routine for Pin Change Interrupt 1
// This routine will only be called on any signal change on A2 and A3: exactly where we need to check.
ISR(PCINT1_vect) {
  encoder.tick(); // just call tick() to check the state.
}
// Read the current position of the encoder and print out when changed

void loop()
{
  

  static int pos = 0;



 
 
  int newPos = encoder.getPosition();
  if (pos != newPos) {
   
    if (count == 0){
 
value = EEPROM.read(0);
 
 
 newPos = newPos + value;
 count++;

 }

  
    display.clearDisplay();
   
     display.setTextSize(1);      // Normal 1:1 pixel scale
   display.setTextColor(WHITE);
 
   display.setCursor(4,1);
display.println("Laser Focus Distance");
   display.setTextSize(2);
 display.setCursor(15, 14);

newPos = newPos + value;
float(z) = newPos/5.0;// scalefactor is 5.0 for my 4mm pitch leadscrew
   display.println(float(z)); // display height as 2 dec places
    display.setCursor(80, 14);
   display.println("mm");
  
   display.display(); //finalises and displays changes
    pos = newPos;
    EEPROM.update(0,pos);

  }
}

// The End

Another option might be to clear the LCD when not in use. That should eliminate "burn in".

Hi John I looked at the option of sleeping the oled but wanted a simple method where when I turned the laser off the Arduino and oled display would also be off. I have now managed to just about sort it and used 16 bit variables so I don’t run out of travel. It loads the old distance when reset. Only problem left is how it deals with negative integers if I move the head up from zero it rolls over. Looking at splitting the 16 bit integer and fiddling with the numbers to make negative readings. Could also just not save to Eeprom if it goes past zero. Technically I could bring the bed up to zero after a lens clean and drop the lens holder onto the bed to set its start point. New code below.

/**************************************************************************
 This is an example for our Monochrome OLEDs based on SSD1306 drivers

 Pick one up today in the adafruit shop!
 ------> http://www.adafruit.com/category/63_98

 This example is for a 128x32 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 Adafruit invests time and resources providing this open
 source code, please support Adafruit and open-source
 hardware by purchasing products from Adafruit!

 Written by Limor Fried/Ladyada for Adafruit Industries,
 with contributions from the open source community.
 BSD license, check license.txt for more information
 All text above, and the splash screen below must be
 included in any redistribution.
 **************************************************************************/



// Rotary encoder oled flipped using interrupt routines. I want it to save the current rotary value to eeprom as an update and read it back after reset
//  Thus remembering the current position after reset. Works well for positive integers 0 to 255.   Would like to extend this to 16 bit numbers giving
//  more scope for larger numbers I could split to give negative numbers also.
 #include <SPI.h>
#include <Wire.h>
#include <RotaryEncoder.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
Adafruit_SSD1306 display(4);

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
float z=0.0;// set up floating point variable
// Setup a RotaryEncoder for pins A2 and A3  on encoder output A and B use A2 andA3
RotaryEncoder encoder(A2, A3);
   uint16_t count=0;
  uint16_t lastValueRecorded;
  
  int lsByte;
  int msByte;
void setup()
{
//pinMode(7,INPUT_PULLUP);
int newPos;

 
  Serial.begin(9600); //for monitoring output debugging
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.setRotation(2); //rotate screen 180 degrees optional
    display.clearDisplay();
  display.display();
  // You may have to modify the next 2 lines if using other pins than A2 and A3
  PCICR |= (1 << PCIE1);    // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
  PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);  // This enables the interrupt for pin 2 and 3 of Port C.
}


// The Interrupt Service Routine for Pin Change Interrupt 1
// This routine will only be called on any signal change on A2 and A3: exactly where we need to check.
ISR(PCINT1_vect) {
  encoder.tick(); // just call tick() to check the state.
}
// Read the current position of the encoder and print out when changed

void loop()

{

  static uint16_t pos = 0;



 
 
  uint16_t newPos = encoder.getPosition(); //need 16 bit integers
  if (pos != newPos) { // has rotor moved
   
    if (count == 0){ // if true then this was after a cold reset
    lsByte = EEPROM.read(0);// after cold reset load the last save values back
    msByte = EEPROM.read(1); 
lastValueRecorded = lsByte + (256 * msByte);// join the lsbyte and msbyte to make 16 bit integer 
 
 
 
 count++; // after this eeprom will not be read again till cold reset

 }

  
    display.clearDisplay();
   
     display.setTextSize(1);      // Normal 1:1 pixel scale
   display.setTextColor(WHITE);
 
   display.setCursor(4,1);
display.println("Laser Focus Distance");
   display.setTextSize(2);
 display.setCursor(15, 14);

newPos = newPos + lastValueRecorded; //new movement value is added to value from eeprom to get absolute value
float(z) = newPos/5.0;// scalefactor is 5.0 for my 4mm pitch leadscrew
   display.println(float(z)); // display height as 2 dec places
    display.setCursor(90, 14);
   display.println("mm");
  
   display.display(); //finalises and displays changes
    pos = newPos;
    lsByte=lowByte(pos);// split 16 bit integer into 2 bytes most significant and least significant
    msByte=highByte(pos);
    EEPROM.update(0,lsByte);//save to eeprom each byte in locations 0 and 1 
 EEPROM.update(1,msByte);
  }
}

// The End