strange characters in LCD

Hi all,
I have a 20x4 LCD connected to an Arduino Uno. The following sketch displays all characters as expected:

#include <LiquidCrystal.h>;
 
LiquidCrystal lcd(12, 11, 5, 4, 1, 0);
 
void setup() {
    lcd.begin(20,4);
  lcd.setCursor(0,0);
  lcd.print("Solltemp. (\337C):");
  lcd.setCursor(0,1);
  lcd.print("Isttemp.  (\337C):");
  lcd.setCursor(0,2);
  lcd.print("Sollzeit (min):");
  lcd.setCursor(0,3);
  lcd.print("Restzeit (min):");
}
 
void loop() {
}

Then I inserted this code into my project:

// Tasten zum Einstellen eines Wertes, der im EEPROM abgelegt wird
//
// Matthias Busse Version 1.0 vom 28.10.2014
#include <LiquidCrystal.h>
#include <EEPROM.h>
LiquidCrystal lcd(12, 11, 5, 4, 1, 0);

int LED=13, lampe=6, temp_soll=130, caladr=1, speichern=0;
volatile unsigned long alteZeit=0, entprellZeit=20, cal=0;

void setup() {
  Serial.begin(38400);
  pinMode(LED, OUTPUT);    // LED Pin
  pinMode(2, INPUT);       // Pin 2 ist INT0
  digitalWrite(2, HIGH);   // interner Pull up Widerstand auf 5V
  attachInterrupt(0, interruptRoutineTemp, LOW);
  // Pin 2 (INT 0) geht auf LOW (0V) dann interruptRoutine aufrufen
  pinMode(A0,INPUT);          // Taste 0 hoch  
  digitalWrite(A0,HIGH);      // interner pullup Widerstand
  pinMode(A1,INPUT);          // Taste 1 runter
  digitalWrite(A1,HIGH);
  temp_soll=eepromReadInt(caladr); // Startwert aus dem EEPROM lesen
  lcd.begin(20,4);
  lcd.setCursor(0,0);
  lcd.print("Solltemp. (\337C):");
  lcd.setCursor(0,1);
  lcd.print("Isttemp.  (\337C):");
  lcd.setCursor(0,2);
  lcd.print("Sollzeit (min):");
  lcd.setCursor(0,3);
  lcd.print("Restzeit (min):");


}

void loop() {
      lcd.setCursor(16,0);

  if(cal==1) { 
    if(readButton(0)) temp_soll+=1; // temp_soll höher
    if(readButton(1)) temp_soll-=1; // temp_soll niedriger
    lcd.print(temp_soll);
    if(temp_soll > 150) temp_soll=150;    // die obere Grenze
    if(temp_soll < 20) temp_soll=20;        // die untere Grenze
    speichern=1; // Wert verändert, bei verlassen der cal > speichern
  }
  if(speichern==1) {            // Wert soll gespeichert werden
    eepromWriteInt(caladr, temp_soll);
    speichern=0;                // Wert wurde gespeichert
  }
  delay(100); 
}

int readButton(int pin) {     // Taste einlesen
  if(analogRead(pin) < 500) { // Analog Eingang abfragen
    delay(entprellZeit);            
    if(analogRead(pin) < 500)
      return 1;               // war gedrückt
  }
  return 0;                   // war nicht gedrückt
}

void interruptRoutineTemp() {
  if((millis() - alteZeit) > entprellZeit) { 
    // innerhalb der entprellZeit nichts machen
    digitalWrite(LED, !digitalRead(LED)); // LED umschalten
    alteZeit = millis(); // letzte Schaltzeit merken 
    cal = !cal; // Einstellmodus wechseln   
  }
}

int eepromReadInt(int adr) {  
// Integer aus dem EEPROM lesen
byte low, high;
  low=EEPROM.read(adr);
  high=EEPROM.read(adr+1);
  return low + ((high << 8)&0xFF00);
} //eepromReadInt

void eepromWriteInt(int adr, int wert) {
// Integer in das EEPROM schreiben
byte low, high;
  low=wert&0xFF;
  high=(wert >> 8)&0xFF;
  EEPROM.write(adr, low); // dauert 3,3ms
  EEPROM.write(adr+1, high);
  return;
} //eepromWriteInt

The 4 lines of the first sketch should appear in the LCD,
then, at position 16,0 the value of temp_soll should be displayed. The rest of the project is not ready yet, but it should work until here.

But all I get are 14 strange characters in the first row (see attachment). When I press the button of D2 the interrupt is called, I notice it because the led lights up.
When I press button 1 or 2 (when called the interrupt), the first characters are changing.
I have no idea what's going wrong here. Any ideas?

Thank you,
-richard

OK, I wrote an extensive reply to the other post of this and it was lost when that post was deleted.

I give up!

Sorry,
I think there was a misunderstanding (on my side?) of which post should be deleted.
I have experimented a little with the code and found that commenting the line

 Serial.begin(38400);

is eliminating the error. How interferes "Serial.begin(38400);" with the LCD??

OK, that was one of my points.

Do not use pin 0 or pin 1 to connect anything to. These are the serial interface pins. Move your connections to other pins, and alter the code accordingly.

Also - don't use interrupts. That is asking for trouble and means you have not thought out your problem.

Why are you reading buttons - or indeed anything here - with analogRead?

Don't put lcd.setCursor(16,0); where it is executed every cycle of loop(). Only set the cursor when you want to actually change a displayed value and only change a displayed value when it is different to the last value!

Probably other things ...

Paul__B:
Do not use pin 0 or pin 1 to connect anything to. These are the serial interface pins. Move your connections to other pins, and alter the code accordingly.

ACK

Also - don't use interrupts. That is asking for trouble and means you have not thought out your problem.

I use the interrupt for jumping to a procedure if a button is pressed (here: changing the value of a nominal temperature). What is the problem with interrupts?

Why are you reading buttons - or indeed anything here - with analogRead?

I am software-debouncing the contacts of the button. If a button is badly pressed, it has a resistance > 1 so the value of the input is something between digital HIGH an digital LOW. That's my explanation, though I might be wrong.

Don't put lcd.setCursor(16,0); where it is executed every cycle of loop(). Only set the cursor when you want to actually change a displayed value and only change a displayed value when it is different to the last value!

Probably other things ...

OK, I'll think about that point.

Thank you!

-richard

r_a_mueller:
I use the interrupt for jumping to a procedure if a button is pressed (here: changing the value of a nominal temperature). What is the problem with interrupts?

That is not what interrupts are for!

A common "newbie" misunderstanding is that an interrupt is a mechanism for altering the flow of a program - to execute an alternate function. Nothing could be further from the truth.

An interrupt is a mechanism for performing an action which can be executed in "no time at all" with an urgency that it must be performed immediately or else data - information - will be lost or some harm will occur.

Now these criteria are in a microprocessor time scale - microseconds. This must not be confused with a human time scale of tens or hundreds of milliseconds. A switch operation is in this latter category and a mechanical operation perhaps several milliseconds; the period of a 6000 RPM shaft rotation is ten milliseconds.

Unless it is a very complex procedure, you would expect the loop() to cycle many times per millisecond. If it does not, there is most likely an error in code planning, while the delay() function is provided for testing purposes, its action goes strictly against effective programming methods. The loop() will be successively testing a number of contingencies as to whether each requires action, only one of which may be whether a particular timing criteria has expired. Unless an action must be executed in the order of microseconds, it will be handled in the loop().

So what sort of actions do require such immediate attention? Well, generally those which result from the computer hardware itself, such as high speed transfer of data in UARTs(, USARTs) or disk controllers.

An alternate use of interrupts, for context switching in RTOSs, is rarely relevant to this category of microprocessors as it is more efficient to write cooperative code as described above.

r_a_mueller:
I am software-debouncing the contacts of the button. If a button is badly pressed, it has a resistance > 1 so the value of the input is something between digital HIGH an digital LOW. That's my explanation, though I might be wrong.

The pins as inputs, have Schmitt trigger functionality - that determination is already made for you by the hardware.

You'll do me a great favour if you could give me the central points of your deleted message. I can feel your disappointment, I just had the same experience with a questionnaire of a school book editor. When going back to the unfinished sheet, everything was gone.

No problem! I believe I have mentioned all the relevant points now. While this situation was caused by a moderator edit, it has happened in the past due to foul-ups in the forum software itself; one needs to take precautions (when composing a detailed posting) but gets lax when it appears to have been working reliably for some time. It seems I reported the duplicate to the moderators and so did you, but they deleted the longer/ later thread - I did not expect that.

Paul__B:
That is not what interrupts are for!

A common "newbie" misunderstanding is that an interrupt is a mechanism for altering the flow of a program - to execute an alternate function. Nothing could be further from the truth.

An interrupt is a mechanism for performing an action which can be executed in "no time at all" with an urgency that it must be performed immediately or else data - information - will be lost or some harm will occur.

That is one one interpretation of how to use interrupts but definitely not the only one.
I've done many realtime systems in a variety of products over the past 40+ years and while interrupts are often used for short time critical things, there are other use cases where it can be very useful for executing alternate functions and in some cases using it for thread context switching. In some cases the code may be time critical and lengthy so it can still make sense to run all of it in the ISR context.
i.e. There is nothing wrong with executing lengthy code in an ISR as long as it doesn't cause issues for the system as a whole.

There are situations where it very much makes sense and can simplify the overall design/architecture and maintenance of the s/w.
On some of the CISC processors that have multiple levels of interrupts that can nest vs a single non nesting one like on the AVR, you can use the interrupt priorities essentially as threads where the higher priority interrupts can interrupt the lower priority interrupts.
i.e. perhaps you get 7 levels of interrupts plus foreground. In that environment it can be useful to do all the work in the ISR vs having to create all kinds of flags that are polled in the foreground and pray that somebody doesn't have a function that takes too long. Running the code in the ISR really simplifies things and also ensures that it runs when it needs to and can interrupt the lower priority tasks, regardless of how long the ISR code takes.

There are also ways to fudge up the processor stack to effectively do a context switch to allow the ISR code to run at non interrupt level.

There are many ways to skin a cat and it always makes me bristle when I see such rigid talk about the only way to handle interrupts is to do as little as possible in the ISR. That isn't the only way to make an embedded system work.

--- bill

Curiously enough, almost nothing you have said there contradicts me. :grinning:

We are not talking here about a CISC or context switching in a RTOS, we are talking about an AVR processor - I deliberately made that quite clear.

Causing issues for the system as a whole is exactly why I warn against indiscriminate attempts to use interrupts, along with attempts to use code which cannot execute within an interrupt. Generating barrages of interrupts from bouncing contacts is a prime example. :astonished:

It remains that many or most such expeditions into the use of interrupts are nothing more more less than a failure to plan the task. So often the XY problem. :slightly_frowning_face:

Paul__B:
We are not talking here about a CISC or context switching in a RTOS, we are talking about an AVR processor - I deliberately made that quite clear.

While using an AVR is likely a safe assumption, I didn't see any mention of specifically using an AVR or an AVR based Arduino in this thread.

I agree with your comments about in appropriate uses for interrupts and the ever common XY problem issue.

However, I think we may diverge a bit on what types of code makes sense to put in an ISR.
When I said: "... using it for thread context switching" I didn't mean using an RTOS but rather running code in the ISR or the ISR thread context.

So, IMO, using interrupts and running the code at ISR level, it isn't necessarily always about ensuring that the code runs immediately, but there can be valid design and maintenance issues that need to be considered as well.

In the Arduino world, IMO, I see an over use of polling. I.e. use cases where polling is being used, in some cases polling a flag that an ISR sets, where running the code inside the ISR context would be appropriate and potentially simplify things and potentially even make the overall system more reliable as it would ensure that the code to process the event gets timely processed.

This is likely because the Arduino user base tends to less technical and therefore uses less sophisticate programming methods.

But yes, like you, I've seen people really get themselves in trouble trying to use interrupts and run things in the ISR context as there are many things that must be considered when deciding to run code in the ISR context that tends to trip up lots of people as most people tend to think about things as single threaded and don't understand lots of the low level mechanics involved.

As an example, I ran into a issue with digitalWrite() the second day I used Arduino about 10 years ago that related to atomicity for the AVR port registers when used in an ISR.
The core arduino development team (Dave Mellis) didn't understand it, even when fully explained.
He never could understand how foreground code - not the ISR. was what was really causing the register corruption.
It took over 6 months to convince them to finally accept that it was an issue and correct the code in the core library with the provided fix. I still don't think he ever fully grasped the actual issue.
To me that was shocking but definitely shows how many people really don't grasp threads, concurrency, atomicity, and specific issues related to RISC architecture instructions as it relates to memory/io updates.

--- bill

bperrybap:
While using an AVR is likely a safe assumption, I didn't see any mention of specifically using an AVR or an AVR based Arduino in this thread.

First line, OP! :astonished:

Paul__B:
First line, OP! :astonished:

Wow. Not sure how I missed that. I guess I really have gone blind. Getting old sucks...
--- bill

Tell me about it!

Thanks for the interesting discussion. I gave the project a new start without using interrupts and without software debouncing (thanks for the hint concerning Schmitt triggers, Paul_B!). I started to code it, and that's what came around until now:

//The sketch contains variables etc. for later use, I did not want to delete them
#include <LiquidCrystal.h>  

const int rs = 12, en = 11, d4 = 10, d5 = 9, d6 = 8, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
const int ButtGoDown = 6;  //button pins
const int ButtGoUp = 5;
const int ButtChTemp = 4;
const int ButtChTime = 3;
const int ButtStart = 2;

int GoDown = 0;   //variables for reading the buttons (button states)
int GoUp = 0;
int ChTemp = 0;
int ChTime = 0;
int Start = 0;
int temp_nom;
int save_temp = 0;
int cal_temp_addr = 1;
int cal_Temp=0, cal_Time=0;
unsigned long  timer = 30;          //will later be manipulated with millis(), therefore unsigned long 

void setup() {
  pinMode(ButtGoDown, INPUT);       // decrement value
  pinMode(ButtGoUp, INPUT);         // increment value
  pinMode(ButtChTemp, INPUT);
  pinMode(ButtChTime, INPUT);
  pinMode(ButtStart, INPUT);
  lcd.begin(20,4);
  lcd.setCursor(0,0);
  lcd.print("Solltemp. (\337C):");  //nominal temperature
  lcd.setCursor(0,1);
  lcd.print("Isttemp.  (\337C):");  //measured temperature
  lcd.setCursor(0,2);
  lcd.print("Sollzeit (min):");     // timer
  lcd.setCursor(0,3);
  lcd.print("Restzeit (min):");     // remaining time
}

void loop() {
   ChTemp = digitalRead(ButtChTemp);
   ChTime = digitalRead(ButtChTime);

   /* setting nominal temperature */
  if (ChTemp == HIGH) {
    GoUp = digitalRead(ButtGoUp);
    if (GoUp == HIGH) {++temp_nom;}
    GoDown = digitalRead(ButtGoDown);
    if (GoDown = HIGH) {--temp_nom;}
    lcd.setCursor(16,0);
    lcd.print(temp_nom);
  }


   /* setting timer */
  if (ChTime == HIGH){
    GoUp = digitalRead(ButtGoUp);
    if (GoUp == HIGH) {++timer;}
    GoDown = digitalRead(ButtGoDown);
    if (GoDown = HIGH) {--timer;}
    lcd.setCursor(16,2);
    lcd.print(timer);
   }
}

The sketch does not behave as expected. As soon as I press ButtChTemp (without pressing any other button), the LCD shows at 16,0 a negative number which decreases (i.e. |value| getting larger). The same with ButtChTime (at 16,2).
I'm sorry, I don't understand this. Pressing ButtChTemp should bring the correspondig input to HIGH, but should not change any value.

Can someone explain, please?

Thanks,
-richard

PS Can I change the subject or shall I open a new thread?

r_a_mueller:
PS Can I change the subject or shall I open a new thread?

Possibly; please don't!

Sorry, too late at night to examine code just now.

if (GoDown = HIGH) {--temp_nom;}
if (GoDown = HIGH) {--timer;}

==

Or as AWOL would say "oops"

Typo. Corrected, but behaviour still the same.

How are your buttons wired? Have you verified with a simple digitalRead() test sketch that all is correct and that they give HIGH and LOW as expected?

If you have indeed fixed

GoDown = digitalRead(ButtGoDown);
       // if (GoDown = HIGH) {
           if (GoDown == HIGH) {
          --temp_nom;
        }

Your sketch is behaving as if the GoDown button is reading HIGH when it is not pressed.

Please verify your hardware setup.