Why are my Serial Monitor messaged not finished?

Hi,

As an assigment for school, I have to make a buzzer react to the amount of light that falls on an LDR.
There must also be a possibility for calibration. I have one button connected to a interrupt pin which triggers(upon LOW) the function 'calibrate'.

It works perfectly, but the messages on the Serial Monitor get chopped off.
When I press the button, the function is executed, and the first message should be entirely displayed.
Instead, the monitor only displays 'Block LDR from l'. Only to resume the rest of the first message upon the next button press, followed with only the partial the second message: 'Expose LDR to l'.
Upon the third press, the rest of the message is printed.
The function itself works, but it annoys me(and probably the teacher who'll grade me, too) that the messages aren't displayed properly.

For the sake of readability and simplicity, I have isolated the concerning function that causes the problem, but I'll provide the entire code if desired.

void calibrate(){
  cli(); // Temporary interrupt disabling to prevent restarting calibration during calibration
 
 Serial.println("Block LDR from light sources to set minimum value(0) by presssing the button.");
 
  while(!digitalRead(calibPin))
    ; // wait for user to relieve button
  
  while(digitalRead(calibPin))
    ; // wait for user input
  
  calibMinValue = analogRead(ldrPin); // Save minimum value, ideally just above 0
  Serial.println("Expose LDR to light source to set maximum value(100) by pressing the button.");  
  
  while(!digitalRead(calibPin))
    ; // wait for user to relieve button
  
  while(digitalRead(calibPin))
    ; // wait for user input
  
  calibMaxValue = analogRead(ldrPin); // Save maximum value, ideally just under 1024
  ldrResolution = calibMaxValue - calibMinValue;

  
  while(!digitalRead(calibPin))
    ; // Wait for user to relieve input
    
  sei(); // Re-enable interrupts
}

I have properly debounced my button with an RC construction.(100nF+330Ohm, 10kOhm)
The button pulls the pin to the ground upon press.
My way of handling the button presses with WHILE waiting loops may not be the most professional or efficient way, but it should work following my logic.

So now for my question; Why are the messages on the Serial Monitor not finished before more code gets executed?
I'm curious whether other people experience the same problem with this code, so feel free to try out.
My bets are on the WHILE waiting loop constructions, or else the something with disabling the interrupts.

Thanks for the help champs.

Serial printing uses interrupts. Can you see how that might cause a problem ?

Do you actually need to use an interrupt at all ? Why not detect the button press in loop() and trigger the calibration routine ?

UKHeliBob:
Serial printing uses interrupts. Can you see how that might cause a problem ?

Do you actually need to use an interrupt at all ? Why not detect the button press in loop() and trigger the calibration routine ?

Yeah I had that feeling already.. And I can't keep them enabled in the function or my function will be triggered within itself. Detecting the button press in-loop as alternative, may result in the program missing a button press though. If there's no other option, I think i'll rebuild my RC construction to create some hysteresis, so that the pin will be held high or low for some more time after the user has pressed the button.

One more question, I also tried printing to my LCD (with I2C module) instead of to the serial monitor. Nothing was printed to it though, and the program freezed and didn't react anymore. Does the I2C module of the LCD also work on interrupts as far as you know?

If you are afraid to miss a slow thing like a button press in the loop your loop is just to slow... Just don't use blocking code and you'll be fine :slight_smile:

septillion:
If you are afraid to miss a slow thing like a button press in the loop your loop is just to slow... Just don't use blocking code and you'll be fine :slight_smile:

Yeah that'll probably be true in practice, but I think it's no decent programming if you just assume that in your code. It is still possible that the program misses a button press. And that may get worse when the code is uploaded to another platform, or if additional time consuming code is added by someone else. I know this is way above the level of difficulty of the assignment, but that aside. I like to hold onto my principles.. :stuck_out_tongue: Code must be perfect, resilient, and foolproof.

It's not bad programming at all. Whatever you make, the loop should multiple times a second if you want to be able to respond "real time", no matter to what. And a button press is really hard to miss, we humans are sloooooowwwwwwww. If you poll a button 20 times a second it's already impossible to miss a press.

And about adding "time consuming code by somebody else", not your problem. If somebody wants to mess up the code they can do that no matter what. :wink:

So that might be above the level of the assignment but your statement/principle is is just a bad statement/principle for good programming :wink:

I can't keep them enabled in the function or my function will be triggered within itself

No it won't. Interrupts are automatically disabled on entry to an ISR for that very reason. You do not need to explicitly disable interrupts but you may still have a problem with Serial.print(). Later versions of the IDE apparently allow printing from inside an ISR but it is still usually regarded as bad practice.

I still suggest that you use polling in loop() rather than an interrupt.

You can add Serial.flush() after Serial.println(). It will work even when interrupts are disabled (since IDE 1.5.6).

If you still think polling the button might miss a press, you could just set a flag in the interrupt handler and check for that and call the calibration function in loop().

Serial printing uses interrupts.

  1. The transmit functions (Serial.write(), Serial.print(), and Serial.println()) do not work on interrupts.

  2. The receive function (Serial.read()) works on interrupt protocol by executing
    ISR(USART_RXC_vect) in the background.

  3. The statements of Step-1, 2 have been verified by the including the cli()
    instruction in the setup().

  4. The Serial.begin(4800):
    (a) Initialises frame structure (Bd 4800, 8-Databit, Noparity, 1-Stopbit).
    (b) Enables the receiver interrupt logic and creates a 64-byte wide FIFO type data buffer is
    to store the received data bytes from the receiver-port (UDR0) on interrupt basis.
    (c) Serial.available() function has been made know the number of data bytes
    stored in the FIFO buffer.
    (d) Serial.read() function brings out the contents of the FIFO buffer byte-by-byte and save them
    in a user defined array for further processing.

  1. The transmit functions (Serial.write(), Serial.print(), and Serial.println()) do not work on interrupts.

@GolamMostafa - please stop posting crap.

@AWOL

The execution of the following codes in the given hardware setup will demonstrate if the posting #8(1) was crap or not.

1. The transmit functions (Serial.write(), Serial.print(), and Serial.println()) do not work on interrupts.

I should have written the above line as:

  1. The transmit functions (Serial.write(), Serial.print(), and Serial.println()) do not work on interrupts in the default ArduinoUNO environment.

Apologise for not being careful in putting up my results correctly which I found through experiments.

** In the default ArduinoUNO environment, the interrupt logic of the transmit functions are not effective. Arduino has implemented the transmit functions on polled protocols. If someone wishes, he can always transmit data on vectored interrupt protocols by initialising the relevant interrupt logic and executing the iSR(USART_TXC_vect).**

  1. With cli() instruction included in the setup(), the data sent from the Serial Monitor are not
    received by the Arduino; it is proved by not seeing the data on the LCD. So, receive function works
    on interrupts.

  2. With cli() instruction still included in the setup(), pressing of K1 executes the transmit functions
    and sends data to the Serial Monitor. This demonstrates that the cli() instruction has no affect
    on the functioning of the transmit functions. cli() instruction put LL at the I_bit of SREG-register
    and disables all interrupt logic.

  3. Galileo has said that he does not guess; he measures and then predicts.

  4. Hardware Setup (Test Bench)

  1. Codes
#include <LiquidCrystal.h>
#define K1 8

// APin           PC4 PC5 PC0 PC1 PC2 PC3      
LiquidCrystal lcd(A4,   A5,  A0, A1, A2, A3); //<--- APin
// LCD Signal     D-I/  E    D4  D5  D6  D7
// LCD Pin----->  4    6   11  12  13  14     R-W/(5) = tied to LL       

byte frame1[] = {0x00, 0x03, 0x34, 0xFF};
byte frame2[8];// ={0x30, 0x30, 0x30,0x33, 0x33,0x34, 0x46, 0x46};
byte x1, x2;
void digitToAscii();

void setup() 
{
  Serial.begin (4800);                    //L1a:
  lcd.begin(16, 2);                       //L1b:
  lcd.setCursor (0, 0);
  pinMode(8, INPUT_PULLUP);           //L1c:
  cli();
} 

void loop()
{
  Serial.flush();
  if(digitalRead(K1) == LOW)            //L2:
  {
    while (digitalRead(K1) != HIGH)     //key debouncing
      ;
    
    digitToAscii();  // convert a hex-digit into ASCII 
    Serial.write(frame2, sizeof(frame2));                 //L2A:
    Serial.write('\n');  //CR(0Dh) and LF(0Ah)
    Serial.print(36, 16);
    Serial.write('\n');  //CR(0Dh) and LF(0Ah)
    Serial.println(0x41, 16);  //CR(0Dh) and LF(0Ah)
 }
  
  
  //if (Serial.available() !=0x00)         //L3:
    //lcd.write(Serial.read());             //L4:
  byte x3 = Serial.available();      // x3 is equal to number of data bytes the receive has 
  if (x3 !=0x00)                    //collected from Receive-Port and have stored them in buffer
    {
      byte x4 = Serial.read();  // reads one byte(ASCII) from receiveBuffer not from Physical Port via UDR0
      lcd.print(x4, 16);
      x3 = x3-1;   
    }

 
}           

void digitToAscii()
{
  for(int i = 0, j=0; i<=03; i++, j++)
  {
    x1 = frame1[i]>>4;
    if (x1 <= 0x09)
     frame2[j] = x1+0x30;
    else
     frame2[j] = x1+0x37;
  //----------------------------
   x1 = frame1[i] & 0x0F;
   if (x1 <= 0x09)
    frame2[j=j+1] = x1+0x30;
   else
    frame2[j=j+1] = x1+0x37;
   }
 }

UsartP93q.ino (1.82 KB)

oqibidipo:
If you still think polling the button might miss a press, you could just set a flag in the interrupt handler and check for that and call the calibration function in loop().

That's actually a simple but effective solution! I always tend to think too difficult..

UKHeliBob:
No it won't. Interrupts are automatically disabled on entry to an ISR for that very reason. You do not need to explicitly disable interrupts but you may still have a problem with Serial.print(). Later versions of the IDE apparently allow printing from inside an ISR but it is still usually regarded as bad practice.

I still suggest that you use polling in loop() rather than an interrupt.

Will do.
I'll try @oqibidipo's idea of the flag, and then just detect the flag in-loop. Seems like a good solution to me.

Thanks guys for the help!

then just detect the flag in-loop

If loop() is running freely as it should be then that is almost the same as detecting a button press in loop(). My advice would be to keep things simple.

@GolamMostafa

I should have written the above line as:

  1. The transmit functions (Serial.write(), Serial.print(), and Serial.println()) do not work on interrupts in the default ArduinoUNO environment.

No, you absolutely should not have written that.

Serial transmission has been buffered and interrupt-driven since version 1.0.

Galileo has said that he does not guess; he

reads the source code.

UKHeliBob:
If loop() is running freely as it should be then that is almost the same as detecting a button press in loop(). My advice would be to keep things simple.

Cannot agree more.

@TR_7, you're still thinking to difficult :wink: We humans are SLLLLLLLLOOOOOOWWWWW :wink: