Wave a magic wand over my code?

I need help completing my sketch. It is about 90% done, there is a single "feature" that I need to implement.

Nobody does work for free, so I'm willing to come to an agreement. Reply or pm me if you have an hour to spare.

Im not sure if I'm violating board rules here, I hope not.

Hint: its very basic, involves 1 LCD screen, 1 encoder, a sensor and a large array of variables.

post your code and what you want done and some my just write it for you, depends on how you ask, if you use code tags, and can be clear in what you want. In the least you would receive direction in doing what the thing is.

Try the Jobs and Paid Consultancy section of the forums.

#include <Wire.h>
#include <HCSR04.h>
#include <EncButton.h>
EncButton<EB_CALLBACK, 2, 3, 4> enc;   
HCSR04 hc(7, 8); /// trig=7 , echo=8
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
byte x = 0;
byte y = 0;
int pwmout1 = 5;
int pwmout2 = 6;
int const row = 20;
int const col = 3;
int rowSelect;
int alarm = 30;
int colNum;
char line1[21];
int powerlvl[row][col] =
{ {0,  0,  0},
  {1, 13, 13},
  {2, 16, 16},
  {3, 19, 19},
  {4, 21, 21},
  {5, 25, 25},
  {6, 30, 30},
  {7, 35, 35},
  {8, 40, 40},
  {9, 45, 45},
  {10, 50, 50},
  {11, 55, 55},
  {12, 60, 60},
  {13, 65, 65},
  {14, 70, 70},
  {15, 75, 75},
  {16, 80, 80},
  {17, 85, 85},
  {18, 90, 90},
  {19, 99, 99},


};

void setup() {
  pinMode(pwmout1, OUTPUT);
  pinMode(pwmout2, OUTPUT);

//L+R+press tickers
  enc.attach(RIGHT_HANDLER, myRight);
  enc.attach(LEFT_HANDLER, myLeft);
  enc.attach(PRESS_HANDLER, myPress);
  attachInterrupt(0, isr, CHANGE);
  attachInterrupt(1, isr, CHANGE);
  // Serial.begin(9600);  // output

  lcd.init();
  lcd.backlight();



}
//increment or dec. variables
void myRight()
        { rowSelect++;
        }
        void myLeft()
        { rowSelect--;
        }
rowSelect=constrain(rowSelect,0,19); //limit to 0-19 so no wonky characters appear

// change cases
void myPress() {
  colNum++;
  if (colNum == 3) colNum = 0;
}

void isr() {
  enc.tickISR();  // ticker

}



void loop()
{
  enc.tick();

  lcd.setCursor(10, 3); ////lcd.setCursor(col, row)
  lcd.print("  "); // clear digits on row 4, col 4,5,6 if they were left over
  lcd.setCursor(0, 3);
  lcd.print("Litri:");
  lcd.setCursor(6, 3);
  lcd.print(60 - hc.dist(), 1); ///// flip measurment; Serial.print (xxx, 0); for no decimals

 //select colum to edit its value
     switch (colNum) {
    case 0: {
        void myRight();
        
        
        void myLeft();
           
//lots of padding to arrange the prints on the lcd; also clear leftover characters
        lcd.setCursor(0, 0);
        lcd.print("o");
        lcd.setCursor(6, 0);
        lcd.print(" ");
        lcd.setCursor(13, 0);
        lcd.print(" ");
        lcd.setCursor(1, 0);

        lcd.print("P=");
        lcd.setCursor(3, 0);
        lcd.print(powerlvl[rowSelect][0]);

        lcd.setCursor(7, 0);
        lcd.print("B%=");
        lcd.setCursor(10, 0);
        lcd.print(powerlvl[rowSelect][1]);

        lcd.setCursor(14, 0);
        lcd.print("M%=");
        lcd.setCursor(17, 0);
        lcd.print(powerlvl[rowSelect][2]);



        x = map((powerlvl[rowSelect][1]), 0, 99, 0, 255);
        analogWrite(pwmout1, x);

        y = map((powerlvl[rowSelect][2]), 0, 99, 0, 255);
        analogWrite(pwmout2, y / 5);

        break;
      }
    case 1: {



        break;
      }
    case 2: {


        break;
      }


  }


  if (hc.dist() >= alarm)      ////////alarm at 30
  { digitalWrite(9, HIGH);
    lcd.setCursor(12, 3);
    lcd.print("NALIVAI!");
  }
  else
  { digitalWrite(9, LOW);
    lcd.setCursor(12, 3);
    lcd.print("        "); /// clear alarm

  }
  if (rowSelect <= 9)    //////// clear left over digits below powerlvl 9
  { lcd.setCursor(2, 2);
    lcd.print("  ");
    lcd.setCursor(4, 0);
    lcd.print("  ");
    lcd.setCursor(12, 0);
    lcd.print("  ");
    lcd.setCursor(19, 0);
    lcd.print("  ");
  }





  if (rowSelect == 0)    //////// clear left over digits at powerlvl 1
  {
    lcd.setCursor(4, 0);
    lcd.print("   ");
    lcd.setCursor(11, 0);
    lcd.print("  ");
    lcd.setCursor(18, 0);
    lcd.print("  ");

    lcd.setCursor(2, 2);
    lcd.print("  ");
  }


}

What I want it to do is:
In case 0: only display the 3 variables from each row of the array, and cycle through them with the encoder; have a "o" as a cursor, set at 0,0 on the LCD, before "P=". (It already does this but regardless of which case is set)

In case 1: change the position of the "o" to just before "B%=" and increase/decrease the second column's variable with the encoder and assign it to X for pwm output.

In case 2: same as above, just for the third column's variable - editable through the encoder and bound to Y for pwm. Move the "o" to just before "M%=".

And pressing the button just cycles through the cases (columns).

This line is not inside any function and is not a declaration so it is not valid code. I think what you want is:

//increment or dec. variables
void myRight()
{
  rowSelect = constrain(rowSelect+1, 0, 19); //limit to 0-19 so no wonky characters appear
}

void myLeft()
{
  rowSelect = constrain(rowSelect-1, 0, 19); //limit to 0-19 so no wonky characters appear
}

Thanks for the input John. By trial and error I also found that constrain(); works best when it's inside myRight() and myLeft().
But I didnt know you could merge rowSelect++; directly into constraint();, thanks.
Storage-wise this merge takes an additional 10 bytes of memory, judging by the compile output.

However, I noticed a general flaw in the way I increment rowSelect , as in myRight() and myLeft() are called globally, regardless of what case is selected by colNum.
I need to detach rowSelect from both functions and only do business with it inside the cases.

I tried to only invoke a change to rowSelect when the case 0 was in effect. it had no discernable effect, other than making the board lock up when I turn the knob. switching cases still works fine though. here's what I changed:

from

void myRight()
{
  rowSelect = constrain(rowSelect+1, 0, 19);
}

void myLeft()
{
  rowSelect = constrain(rowSelect-1, 0, 19); 
}

to

void myLeft()
{
  while (colNum = 0) {
    rowSelect = constrain(rowSelect - 1, 0, 19);
  }
  while (colNum = 1) {
    (powerlvl[rowSelect][1]--);
  }
  while (colNum = 2) {
    (powerlvl[rowSelect][2]--);
  }
}

void myRight()
{
  while (colNum = 0) {
    rowSelect = constrain(rowSelect + 1, 0, 19);
  }
  while (colNum = 1) {
    (powerlvl[rowSelect][1]++);
  }
  while (colNum = 2) {
    (powerlvl[rowSelect][2]++);
  }
}

and for the case switching:

switch (colNum) {
   case 0: {
print...
print...
print...
x = map((powerlvl[rowSelect][1]), 0, 99, 0, 255);
        analogWrite(pwmout1, x);
y = map((powerlvl[rowSelect][2]), 0, 99, 0, 255);
        analogWrite(pwmout2, y / 5);
        break;
      }
  case 1: { //different prints to discern a change in case
pwm out x;
pwm out y;
  break;
} 

case 2: { //different prints from case 1, to discern a change in case
pwm out x;
pwm out y;
  break;
} 

And colNum is just cycled like 0 -> 1 -> 2 by pressing the knob.

Any ideas why the newly added while conditionals are locking up the board? I tried to substitute them with if(colNum==0) statements but its the same deal. I think if and while are interchangeable in this particular instance? I know there are threads about it, its a rabbit hole.

Two reasons:
'while' is a loop.
'=' is the assignment operator

  while (colNum = 0) {
    rowSelect = constrain(rowSelect + 1, 0, 19);
  }
  while (colNum = 1) {
    (powerlvl[rowSelect][1]++);
  }

This is equivalent to:

  colNum = 0;
  while (0 != 0) {
    // This never executes because 0 == 0
  }
  colNum = 1;
  while (1 != 0) {
    // This loops forever because 1 != 0
  }

You should be using 'if', not 'while' and '==' (comparison) not '=' (assignment).

 if (colNum == 0) {
    rowSelect = constrain(rowSelect + 1, 0, 19);
  }

  if (colNum == 1) {
    // powerlvl[rowSelect][1]++;
    // You probably want to constrain that.
    powerlvl[rowSelect][1] = constrain(
        powerlvl[rowSelect][1] + 1, 0, 99);
  }

  if (colNum == 2) {
    // powerlvl[rowSelect][2]++;
    // You probably want to constrain that.
    powerlvl[rowSelect][2] = constrain(
        powerlvl[rowSelect][2] + 1, 0, 99);
  }

Note: There is a shortcut for a series of 'if' statements that compare an integer variable to a list of integer constants:

  switch (colNum)
  {
    case 0:
      rowSelect = constrain(rowSelect + 1, 0, 19);
      break;

   case 1:
    // powerlvl[rowSelect][1]++;
    // You probably want to constrain that.
    powerlvl[rowSelect][1] = constrain(
        powerlvl[rowSelect][1] + 1, 0, 99);
    break;

  case 2:
    // powerlvl[rowSelect][2]++;
    // You probably want to constrain that.
    powerlvl[rowSelect][2] = constrain(
        powerlvl[rowSelect][2] + 1, 0, 99);
   break;
  }

You can easily merge those last two cases:

  switch (colNum)
  {
    case 0:
      rowSelect = constrain(rowSelect + 1, 0, 19);
      break;

   case 1:
   case 2:
     powerlvl[rowSelect][colNum] = constrain(
        powerlvl[rowSelect][colNum] + 1, 0, 99);
    break;
  }
1 Like

You probably meant == (test for equality) not = (assignment) in this place and many others.

if and while are not interchangeable. Stabbing away at your logic switching around the type of statements you employ is not a recipe for success.

Here, e.g.

  while (colNum == 0) {
    rowSelect = constrain(rowSelect + 1, 0, 19);
  }

is where your program goes to die. Nothing changes colNum, so this will happily rowSelect until you reset or remove power.

(fixed the == thing in the while I extracted from your code.

You might benefit from stepping away from the code and writing out what is supposed to go,on, making a flowchart or otherwise getting your logic straight before you are furiously coding and taking guesses at what might make broken work.

a7

1 Like

Thank both of you gentlemen. It works completely now.
The problem was in fact the single "=". I changed the while for if statements.

it is changed by

void myPress() {
  colNum++;
  if (colNum == 3) colNum = 0;
}

I'm very happy with this result guys. thank you again.

1 Like

Hats off to @johnwasser for the heavy lifting., all I did was through the peephole, overlooked the possibility of a global variable update… you would believe how often a loop is inadvertently made "infinite" by a real mistake there.

I have all your components laid out but not yet wired in the wokwi.com Arduino simulator. Libraries ready to serve.

If you post the now version you happy with, I'll paste it into the code window, wire it up and take a look at what your program actually does… and link it here natch.

TIA

a7

this is the final code:

#include <Wire.h>
#include <HCSR04.h>
#include <EncButton.h>
EncButton<EB_CALLBACK, 2, 3, 4> enc;    //CLK=2; DT=3; BTN=4;
HCSR04 hc(7, 8); /// trig=7 , echo=8
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);  // SDA=4; SCL=5 
byte x = 0;
byte y = 0;
int pwmout1 = 5;
int pwmout2 = 6;
int const row = 20;
int const col = 3;
int rowSelect = 0;
int alarm = 30;
int colNum;
int powerlvl[row][col] =
{ {0,  0,  0},
  {1, 13, 13},
  {2, 16, 16},
  {3, 19, 19},
  {4, 21, 21},
  {5, 25, 25},
  {6, 30, 30},
  {7, 35, 35},
  {8, 40, 40},
  {9, 45, 45},
  {10, 50, 50},
  {11, 55, 55},
  {12, 60, 60},
  {13, 65, 65},
  {14, 70, 70},
  {15, 75, 75},
  {16, 80, 80},
  {17, 85, 85},
  {18, 90, 90},
  {19, 99, 99},


};

void setup() {
  pinMode(pwmout1, OUTPUT);
  pinMode(pwmout2, OUTPUT);


  enc.attach(RIGHT_HANDLER, myRight);
  enc.attach(LEFT_HANDLER,  myLeft);
  enc.attach(PRESS_HANDLER, myPress);
  attachInterrupt(0, isr, CHANGE);
  attachInterrupt(1, isr, CHANGE);
 

  lcd.init();
  lcd.backlight();




}


void myLeft()
{
  if (colNum == 0) {
    rowSelect = constrain(rowSelect - 1, 0, 19);
  }
  if (colNum == 1) {
    powerlvl[rowSelect][1] = constrain(powerlvl[rowSelect][1] - 1, 0, 99);
    if (powerlvl[rowSelect][1] < 10) {
      lcd.setCursor(11, 0);
      lcd.print(" ");
    }
  }
  if (colNum == 2) {
    powerlvl[rowSelect][2] = constrain(powerlvl[rowSelect][2] - 1, 0, 99);
    if (powerlvl[rowSelect][2] < 10) {
      lcd.setCursor(18, 0);
      lcd.print(" ");
    }
  }


}

void myRight()
{
  if (colNum == 0) {
    rowSelect = constrain(rowSelect + 1, 0, 19);
  }
  if (colNum == 1) {
    powerlvl[rowSelect][1] = constrain(powerlvl[rowSelect][1] + 1, 0, 99);
 if (powerlvl[rowSelect][1] < 10) {
      lcd.setCursor(11, 0);
      lcd.print(" ");
    }
  }
  if (colNum == 2) {
    powerlvl[rowSelect][2] = constrain(powerlvl[rowSelect][2] + 1, 0, 99);
 if (powerlvl[rowSelect][2] < 10) {
      lcd.setCursor(18, 0);
      lcd.print(" ");
    }
  }

}

void myPress() {
  colNum++;
  if (colNum == 3) colNum = 0;
}

void isr() {
  enc.tickISR();  // ticker
}



void loop()
{
  enc.tick();

  lcd.setCursor(10, 3);  
  lcd.print("  "); // clear digits on row 4, col 4,5,6 if they were left over
  lcd.setCursor(0, 3);
  lcd.print("Litri:");
  lcd.setCursor(6, 3);
  lcd.print(60 - hc.dist(), 1); ///// flip measurment; Serial.print (xxx, 0); for no decimals

  switch (colNum) {
    case 0: {

        lcd.setCursor(0, 0);
        lcd.print(">");
        lcd.setCursor(6, 0);
        lcd.print(" ");
        lcd.setCursor(13, 0);
        lcd.print(" ");
        lcd.setCursor(1, 0);

        lcd.print("P=");
        lcd.setCursor(3, 0);
        lcd.print(powerlvl[rowSelect][0]);

        lcd.setCursor(7, 0);
        lcd.print("B%=");
        lcd.setCursor(10, 0);
        lcd.print(powerlvl[rowSelect][1]);

        lcd.setCursor(14, 0);
        lcd.print("M%=");
        lcd.setCursor(17, 0);
        lcd.print(powerlvl[rowSelect][2]);

        x = map((powerlvl[rowSelect][1]), 0, 99, 0, 255);
        analogWrite(pwmout1, x);

        y = map((powerlvl[rowSelect][2]), 0, 99, 0, 255);
        analogWrite(pwmout2, y / 5); //this /5 is intentional to determine which is which
        break;
      }

    case 1: {

        lcd.setCursor(0, 0);
        lcd.print(" ");
        lcd.setCursor(6, 0);
        lcd.print(">");
        lcd.setCursor(13, 0);
        lcd.print(" ");
        lcd.setCursor(1, 0);

        lcd.print("P=");
        lcd.setCursor(3, 0);
        lcd.print(powerlvl[rowSelect][0]);

        lcd.setCursor(7, 0);
        lcd.print("B%=");
        lcd.setCursor(10, 0);
        lcd.print(powerlvl[rowSelect][1]);

        lcd.setCursor(14, 0);
        lcd.print("M%=");
        lcd.setCursor(17, 0);
        lcd.print(powerlvl[rowSelect][2]);

        x = map((powerlvl[rowSelect][1]), 0, 99, 0, 255);
        analogWrite(pwmout1, x);

        y = map((powerlvl[rowSelect][2]), 0, 99, 0, 255);
        analogWrite(pwmout2, y / 5); //this /5 is intentional to determine which is which
        break;
      }
    case 2: {

        lcd.setCursor(0, 0);
        lcd.print(" ");
        lcd.setCursor(6, 0);
        lcd.print(" ");
        lcd.setCursor(13, 0);
        lcd.print(">");
        lcd.setCursor(1, 0);

        lcd.print("P=");
        lcd.setCursor(3, 0);
        lcd.print(powerlvl[rowSelect][0]);

        lcd.setCursor(7, 0);
        lcd.print("B%=");
        lcd.setCursor(10, 0);
        lcd.print(powerlvl[rowSelect][1]);

        lcd.setCursor(14, 0);
        lcd.print("M%=");
        lcd.setCursor(17, 0);
        lcd.print(powerlvl[rowSelect][2]);

        x = map((powerlvl[rowSelect][1]), 0, 99, 0, 255);
        analogWrite(pwmout1, x);

        y = map((powerlvl[rowSelect][2]), 0, 99, 0, 255);
        analogWrite(pwmout2, y / 5);  //this /5 is intentional to determine which is which
        break;
      }
  }


  if (hc.dist() >= alarm)      ////////alarm at 30
  { digitalWrite(9, HIGH);
    lcd.setCursor(12, 3);
    lcd.print("OIL LOW!");
  }
  else
  { digitalWrite(9, LOW);
    lcd.setCursor(12, 3);
    lcd.print("        ");

  }
  if (rowSelect <= 9)    //////// clear left over digits below powerlvl 9
  { lcd.setCursor(2, 2);
    lcd.print("  ");
    lcd.setCursor(4, 0);
    lcd.print("  ");
    lcd.setCursor(19, 0);
    lcd.print("  ");
  }

  if (rowSelect == 0)    //////// clear left over digits at powerlvl 1
  {

    lcd.setCursor(11, 0);
    lcd.print(" ");
    lcd.setCursor(18, 0);
    lcd.print(" ");
  }
}

@alto777 thats a great tool!

I did the wokwi with your latest:

The rotary encoder doesn't work as well as it could. Imma chalk it up to a sim shortcoming for now.

On the other hand, libraries can, well, be less than perfect. Do you find your rotary encoder to operate smoothly and monotonically in real life?

Sometimes the last digit flickers on the LCD even if the value is unchanged. I have no energy to look to see if that is a code issue - does the last digit of some of the parameters flicker in real life?

If so, and you find it intolerable or even just annoying, you could write a value to the LCD only if it is different to the one you wrote already.

Now, what the heck does this thing do? A small operator's manual is missing. :wink:

Note: whatever ultrasonic library you get from the usual dance doesn't seem to match, so I used my "club member" privileges that allow me to download a library, then upload it to wokwi. The one I found first works (well, comples) OK. But you should be able to copy and modify the project and I hope the libraries will go along for that ride.

In the sim, the ultrasonic device can be clicked, revealing a slider which can be moved to inform the values that the program will pick up when it interrogates the sensor.

Fun.

a7

Outstanding work @alto777.

I also noticed that about the encoder. It's like it isn't debounced in the simulation. IRL it's snappy with no missed clicks.
As for the flickering digits - it's absolutely the code, and it is caused by this if statement:

 if (rowSelect == 0)    
  {
    lcd.setCursor(11, 0);
    lcd.print(" ");
    lcd.setCursor(18, 0);
    lcd.print(" ");
  }

The reasoning is that on rowSelect=0 , presumably both other digits will also be 0 i.e. a single digit number. but if you manually increase them above 9 on the same rowSelect=0, the numbers get in a fist fight with the if statement above. Its intended to be the OFF setting, so its no biggie.

As for the usage - it's the brains of a "digital carburettor". Output x will control a servo attached to valve to control the flow of fuel, output y is the same but for air. And the powerlvl array is the air-fuel mixture map :slight_smile: . In this instance the graph is mostly linear, but for proper burning it will most likely have to be offset a bit to favor more air or more fuel. Thats why I needed the values to be changeable.

And that leads me towards the next step - saving the whole powerlvl array into EEPROM so the settings are safe whenever it loses power. And read them on every power-on.
Now I'm thinking it might have been a better idea to have separate arrays for the 3 variables. Or have 3 longer rows of 20 columns.
Can I write 2d arrays directly into memory and read them? Or do I have to chop them up somehow? what would be easier as imput to write into EEPROM?

This sounds like a fascinating project. Could you explain a bit more about what it's for and why you are making it?

I ask because I've designed and build an Arduino-based fuel injection system for a model engine. The "fuel" in this case is gas, and I use five variables - engine rpm, engine temperature, throttle opening, rate-of-change of throttle opening, and a manual richer/weaker control (a potentiometer) - to determine the 'on' time for the gas injector.

I've implemented a base map of injection duration against throttle opening, and then that base figure is "tweaked" by the rpm, the rate-of-change of throttle opening and the temperature:

void SetLambda(void)
{
  SetRPMLambda();   //these four each calculate a "tweak" to adjust the injection duration
  SetROCLambda();   //given by the base map
  SetTempLambda();
  SetManualLambda();

  totalLambdaTweak = rpmLambdaTweak * rocLambdaTweak * tempLambdaTweak * manualLambdaTweak;

  gasDuration = (int)(gasDuration * totalLambdaTweak);  //gasDuration is the value from the base map
}

It's far less sophisticated than that. Basically to determine "optimal" burning mixtures of several flammable liquids like petrol, diesel, machine/motor/cooking oils, ethanol, vodka and whatever else that comes in bottles or jugs. "optimal" is a loose term here, since we will be gauging it by the emissions released by the burning. That, and heat output for each fuel type for the same burner, collected via thermal probes over time.
It may or may not be related to alternative heating sources, since I live in the EU and winter is kind of late.

That sounds fascinating! Is this a research project? Or for some practical application?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.