Ok I am back already. The same project, different issue. To recap, I am building a measuring wheel that displays inches, footage and mileage. I have done more coding for this project. After hours of reading, learning, and a few more youtube videos, I think I should now have this measuring wheel to where I push a save button and save the current measurement into eeprom. I should be able to save up to 12 measurements right now. There are main "functions" of the program.
#1 is measuring and displaying the current measurement. I can also reset that measurement to zero. I have got that figured out.
#2 is saving measurements to EEPROM. I am not totally sure about this one yet. I have not tried to save any measurements yet. I have to get the screen working properly.
#3 is displaying and scrolling through the saved measurements with another rotary encoder.
I am working with arduino UNO R3, the wheel encoder is the same TAISS encoder, the LCD is 20 x 04. The menu encoder I believe is a KY080 style. It came in a starter kit. It has 20 detents and 80 pulses.
Here is my new issue to work through. Upon startup/bootup CFG (my company's initials) is briefly displayed, then the screen is set up with "Ready To Measure". If the menu function in the loop is commented out, this works correctly. If the menu function in the loop is not commented, Saved Measurement #12 displays instead of Ready to Measure. BUT rotating the wheel encoder will jump over to Ready To Measure and it will start measuring AND turning the menu encoder will scroll through the saved measurement menu.
I have tried many different combinations of if statements, state changes, etc, and get the same result with all of it. One other piece to the puzzle, I added Serial.prints in both the measuring and menu functions. Immediatley after bootup, the serial monitor is rapidly displaying "in measuring" and "in menu" back and forth, so it is jumping back and forth between those two functions. I just can't figure out why.
Here is the code. And don't forget to tell me where I can tidy it up if needed.
/* My Measuring Wheel Project. Calculations based off of
wheel diameter and pulses per revolution.
Adjust these two value based on actual products used.
*/
#include <EEPROM.h>
#include <Encoder.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// On Arduino UNO, pins 2 and 3 are interrupt pins
Encoder wheelEnc(2, 4);
Encoder menuEnc(3,5);
int detentDivisor=4;/*For this particular menu encoder, divide pulse
counts by 4 to equal the detent poisitons*/
//Menu and EEPROM
int lastPosition=-999;/*Arbitrary number to ENSURE upon start up
that newPosition does not equal lastPosition.*/
int menuIndex=0;
const char* menuItems[] = {
"Measurement 1",
"Measurement 2",
"Measurement 3",
"Measurement 4",
"Measurement 5",
"Measurement 6",
"Measurement 7",
"Measurement 8",
"Measurement 9",
"Measurement 10",
"Measurement 11",
"Measurement 12",
};
const int menuLength = sizeof(menuItems) / sizeof(menuItems[0]);
const int numMeasurements=12;
const int startAddress=0;
int currentMeasurement=0;
struct Measurement {
int inch;
float feet;
float miles;
};
//Measuring
volatile long wheelPulseCount;
long wheelCountStart = 0;
float totalInches = 0.0;
static float lastTotalInches = 0.0;
float inchesDisplayed = 0.0;
float totalFootage = 0.0;
float footageDisplayed = 0.0;
float totalMileage = 0.0;
float mileageDisplayed = 0.0;
const float inchesPerMile=63360.0;
const float inchesPerFoot=12.0;
const float feetPerMile=5280.0;
//Button pins
int save=9;
int reset = 10;
int backlight=11;
unsigned long lastDebounceTime=millis();//Debounce millis delay
int resetButtonDelay=1000;
int backlightButtonDebounceDelay=1000;//Debounce Millis delay
int saveButtonDelay=150;
bool backlightOn=false;
bool measuringActive=false;
bool inMenu=false;
//Display accurate mileage to 3 decimals
int decimals=3;
float truncate(float num, int dec){
int factor=pow(10,dec);
return int(num*factor)/float(factor);
}
// Wheel size variables
float diameter = 15.0;//Change as needed for whichever wheel size
float inchesPerPulse;
float wheelInches;
float pi=M_PI;
int pulsesPerRev = 2400;//Change as needed depending on encoder used
void setup() {
Serial.begin(9600);
Serial.println("In setup()");
lcd.init();
lcd.backlight();
backlightOn=true;
lcd.clear();
lcd.setCursor(8,1);
lcd.print("CFG");
delay (3000);
lcd.clear();
lcd.setCursor (2,0);
lcd.print("READY TO MEASURE");
//Prevent LED13 from automatically turning on
pinMode (13, OUTPUT);
digitalWrite (13,LOW);
//Buttons
pinMode(reset, INPUT_PULLUP);
pinMode(save, INPUT_PULLUP);
pinMode(backlight,INPUT_PULLUP);
Serial.println("After pinModes");
//One time formulas
wheelInches = diameter * pi;//Calculate the circumference of wheel.
inchesPerPulse = wheelInches / pulsesPerRev;//Used to calculate inches covered per pulse.
//Initialize states to ensure proper startup.
measuringActive=true;//Force Measuring mode to start at startup
inMenu=false; //Ensure NOT in menu at startup
menuIndex=0;//Ensure menu starts at 1st menu
lastPosition=menuEnc.read()/detentDivisor;//Initilaize lastPosition
Serial.println("After initializations");
}
void measuring(){
Serial.println("In Measuring()");
if (wheelPulseCount != wheelCountStart) {//Starts the counting process
wheelCountStart = wheelPulseCount;
if (!measuringActive){
measuringActive=true;
lcd.clear();
lcd.setCursor (2,0);
lcd.print("READY TO MEASURE");
}
}
if (measuringActive){
// Calculate the remaining inches within a foot
inchesDisplayed = abs(static_cast<int>(totalInches)) % 12;/*The % sign uses float, but total
inches is an int, so static_cast<int>
turns it back to an integer. ABS means use
absolute value. Prevents inches from being
displayed in negatives.*/
footageDisplayed = totalFootage;//Displays footage
mileageDisplayed = truncate(totalMileage, decimals);//Displays mileage. Truncate displays actual
//value to the specified decimal places. NOT the
//rounded value.
if (totalFootage>99999 || totalFootage<-99999){
wheelEnc.write(0);
}
if (lastTotalInches != totalInches) { /*This if statement prevents constant monitoring of the
serial monitor. It only displays new data when the data
changes*/
lastTotalInches = totalInches; // Resets if statements
lcd.setCursor (0,2);
lcd.print ("IN FT MI");
lcd.setCursor(0,3);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print (inchesDisplayed,0);
lcd.setCursor (4,3);
lcd.print(" ");
lcd.setCursor(5,3);
lcd.print(footageDisplayed,1);
lcd.setCursor(15,3);
lcd.print(" ");
lcd.setCursor(14,3);
lcd.print(mileageDisplayed,3);
}
}
}
void resetButton(){
if (digitalRead(reset) == LOW) {//If RESET button is pressed
if ((millis()-lastDebounceTime)>resetButtonDelay){
if (measuringActive){
wheelEnc.write(0);//Resets encoder counts to zero and displays all zeros on serial monitor
}
}
}
}
void backlightButton() {
if (digitalRead(backlight)==LOW){
if ((millis()-lastDebounceTime)>backlightButtonDebounceDelay){//Debounce timer
lastDebounceTime=millis();
if (backlightOn==true){
lcd.noBacklight();
backlightOn=false;
}
else if (backlightOn==false) {
lcd.backlight();
backlightOn=true;
}
}
}
}
void saveMeasurement(){
if (save==LOW && currentMeasurement<numMeasurements){
if ((millis()-lastDebounceTime)>saveButtonDelay){
lastDebounceTime=millis();
Measurement m={inchesDisplayed, footageDisplayed, mileageDisplayed};
int address=startAddress+currentMeasurement*sizeof(Measurement);
EEPROM.put(address,m);
currentMeasurement++;
}
}
}
void menu(){
Serial.println("In Menu()");
int newPosition=(menuEnc.read()/detentDivisor)-1; /*DetentDivisor ensures menu changes at "clicks"
Subtracting one ensures menustarts at first position*/
if (newPosition != lastPosition){
lastPosition=newPosition;
menuIndex=newPosition % numMeasurements;
if (menuIndex<0){
menuIndex += numMeasurements;
}
displayMeasurement (menuIndex);
measuringActive=false;//Disable measuring abiliity while in menu screens
inMenu=true;
}
}
void displayMeasurement(int index){
int address = startAddress + index * sizeof(Measurement);
Measurement m;
EEPROM.get (address, m);
lcd.clear();
lcd.setCursor (0,0);
lcd.print ("Saved Measurement#");
lcd.setCursor (18,0);
lcd.print (index +1); //Beacause index starts at zero.
lcd.setCursor (0,2);
lcd.print ("IN FT MI");
lcd.setCursor(0,3);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print (m.inch);
lcd.setCursor (4,3);
lcd.print(" ");
lcd.setCursor(5,3);
lcd.print(m.feet);
lcd.setCursor(15,3);
lcd.print(" ");
lcd.setCursor(14,3);
lcd.print(m.miles);
}
void loop() {
wheelPulseCount = wheelEnc.read();//Read the pulses from encoder
wheelInches = diameter * pi;//Calculate the circumference of wheel.
inchesPerPulse = wheelInches / pulsesPerRev;//Used to calculate inches covered per pulse.
totalInches = wheelPulseCount * inchesPerPulse;//Calculate total inches covered
totalFootage = totalInches/inchesPerFoot;//Calculate the footage
totalMileage = totalInches/inchesPerMile;//Calculate the mileage.
measuring();
menu();
resetButton();
backlightButton();
saveMeasurement();
}