scrooling text speed on I2C and Arduino UNO + other active sensors

Hi all :smiley: ,
My first post, I read all the guidelines and hope didn’t missed something…
I’m trying to make 3 functionality work together.
Arduino UNO + LDC I2C 2004 + YF-201 flow meter + DS18B20 thermal sensor.
The 85 degree at startup problem forced me to search for a code that didn’t use external libraries (apart the liquidcrystal).
All work despite a blink at any cursor movement and this movement is 1 second (too slow for my taste)
Is possible to speed up the scrolling and make really fixed what is not changing?
I tried to exclude the flow code that stated for delay(1000) for the correct calculation of the flow (is necessary this number) , but the scrolling stay still at 1 sec each movement (always with blink).
I’m a newbie and I managed to arrange this code (maybe with errors but working) but I have no idea where to look or where to make changes.
To add some delay for regulate the scroll seems not work ( https://forum.arduino.cc/index.php?topic=83654.0 )
Many thanks for all the suggestions/supports.

/*ReadDS18B20
ver: 6 Jly 2010
THIS IS A FIRST DRAFT.... WORKS, but scheduled for overhaul.


Simple, simple test of reading DS18B20
connected to nuelectronics.com datalogging shield.

See...

http://sheepdogguides.com/arduino/ar3ne1tt.htm

... for explanation of this code.

Code lightly adapted from code from nuelectronics.com*/


#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4);
// set the LCD address to 0x27 for a 20 chars and 4 line display

volatile int NbTopsFan; //measuring the rising edges of the signal
int Calc;
int hallsensor = 2;    //The pin location of the sensor

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// it's a 20x4 LCD so...
int screenWidth = 20;
int screenHeight = 4;

// the two lines
// line1 = scrolling
String line1 = "Lets roll those Bites !!!";
// line2 = static
String line2 = "My computational RIG ";

// just some reference flags
int stringStart, stringStop = 0;
int scrollCursor = screenWidth;
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

void rpm ()     //This is the function that the interupt calls
{
  NbTopsFan++;  //This function measures the rising and falling edge of the hall effect sensors signal
}

#define TEMP_PIN  4 //See Note 1, sheepdogguides..ar3ne1tt.htm




void OneWireResetA(int Pin) // reset.  
{
    digitalWrite(Pin, LOW);
    pinMode(Pin, OUTPUT); // bring low for 500us
    delayMicroseconds(500);
}

void OneWireResetB(int Pin) // reset.  
{
    pinMode(Pin, INPUT);
    delayMicroseconds(500);
}
void OneWireOutByte(int Pin, byte d);
byte OneWireInByte(int Pin);
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>start setup
void setup() {
  digitalWrite(TEMP_PIN, LOW);
  pinMode(TEMP_PIN, INPUT);      // sets the digital pin as input (logic 1)
  
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  pinMode(hallsensor, INPUT); //initializes digital pin 2 as an input
  attachInterrupt(0, rpm, RISING); //and the interrupt is attached
//9600 to match the data rate being used by the
//serial monitor on my system, which is set to
//the Arduino default. (Sample code published
//by nuelectronics used a faster baud rate.)
   delay(100);
   Serial.print("temperature measurement:\n");
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>end setup

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> start loop
void loop(){
    
 int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;


 OneWireReset(TEMP_PIN);
 OneWireOutByte(TEMP_PIN, 0xcc);
 OneWireOutByte(TEMP_PIN, 0x44); // perform temperature conversion, strong pullup for one sec

 OneWireReset(TEMP_PIN);
 OneWireOutByte(TEMP_PIN, 0xcc);
 OneWireOutByte(TEMP_PIN, 0xbe);

 LowByte = OneWireInByte(TEMP_PIN);
 HighByte = OneWireInByte(TEMP_PIN);
 TReading = (HighByte << 8) + LowByte;
 SignBit = TReading & 0x8000;  // test most sig bit
 if (SignBit) // negative
 {
   TReading = (TReading ^ 0xffff) + 1; // 2's comp
 }
 Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

 Whole = Tc_100 / 100;  // separate off the whole and fractional portions
 Fract = Tc_100 % 100;

 // ***************************************************************************calibration values start
float RawHigh = 100;
float RawLow = 0;
float ReferenceHigh = 100;
float ReferenceLow = -3.5; // subbed by termocamera + voltmeter check 
float RawRange = RawHigh - RawLow;
float ReferenceRange = ReferenceHigh - ReferenceLow;
float RawValue = Whole;
float CorrectedValue = (((RawValue - RawLow) * ReferenceRange) / RawRange) + ReferenceLow;
// ***************************************************************************calibration values end
 if (SignBit) // If its negative
 {
    Serial.print("-");
 }
 Serial.print(CorrectedValue);
 Serial.print(".");
 if (Fract < 10)
 {
    Serial.print("0");
 }

 Serial.print(Fract);

     Serial.print("\n");

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> start flow calculation
  NbTopsFan = 0;    //Set NbTops to 0 ready for calculations
  sei();        //Enables interrupts
  delay (1000);    //Wait 1 second for the appropriate calculation
  cli();        //Disable interrupts
  Calc = (NbTopsFan * 60 / 7.5); //(Pulse frequency x 60) / 7.5Q, = flow rate in L/hour 
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> stop flow calculation
       sei();
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> start print flow calulation
  lcd.init();
  lcd.backlight();
  lcd.print ("   Flow ");
  lcd.print (Calc, DEC); //Prints the number calculated above
  lcd.print (" L/hour"); //Prints "L/hour" and returns a  new line
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> end print flow calulation

//********************************************************************************* start thermistor 
    lcd.setCursor(0,1); //go to the second line
  lcd.print("Liquid tank");
  lcd.setCursor(12,1); 
  lcd.print(CorrectedValue);
  lcd.setCursor(17,1);
  lcd.print((char)223); 
  lcd.print("C");
//********************************************************************************** close thermistor 

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> open static line and scrolling line
  lcd.setCursor(scrollCursor, 2);
  lcd.print(line1.substring(stringStart,stringStop));
  lcd.setCursor(0, 3);
  lcd.print(line2);
  delay(300);
 // lcd.clear();
  if(stringStart == 0 && scrollCursor > 0){
    scrollCursor--;
    stringStop++;
  } else if (stringStart == stringStop){
    stringStart = stringStop = 0;
    scrollCursor = screenWidth;
  } else if (stringStop == line1.length() && scrollCursor == 0) {
    stringStart++;
  } else {
    stringStart++;
    stringStop++;
  }
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> close static line and scrolling line
  cli();
  
  Serial.print (Calc, DEC); //Prints the number calculated above
  Serial.print (" L/hour\r\n"); //Prints "L/hour" and returns a  new line
  delay(5000);      // 5 second delay.  Adjust as necessary
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  end loop


void OneWireReset(int Pin) // reset.  Should improve to act as a presence pulse
{
    digitalWrite(Pin, LOW);
    pinMode(Pin, OUTPUT); // bring low for 500 us
    delayMicroseconds(500);
    pinMode(Pin, INPUT);
    delayMicroseconds(500);
}

void OneWireOutByte(int Pin, byte d) // output byte d (least sig bit first).
{
  byte n;

  for(n=8; n!=0; n--)
  {
     if ((d & 0x01) == 1)  // test least sig bit
     {
        digitalWrite(Pin, LOW);
        pinMode(Pin, OUTPUT);
        delayMicroseconds(5);
        pinMode(Pin, INPUT);
        delayMicroseconds(60);
     }
     else
     {
        digitalWrite(Pin, LOW);
        pinMode(Pin, OUTPUT);
        delayMicroseconds(60);
        pinMode(Pin, INPUT);
     }

     d=d>>1; // now the next bit is in the least sig bit position.
  }

}

byte OneWireInByte(int Pin) // read byte, least sig byte first
{
   byte d, n, b;

   for (n=0; n<8; n++)
   {
       digitalWrite(Pin, LOW);
       pinMode(Pin, OUTPUT);
       delayMicroseconds(5);
       pinMode(Pin, INPUT);
       delayMicroseconds(5);
       b = digitalRead(Pin);
       delayMicroseconds(50);
       d = (d >> 1) | (b<<7); // shift d to right and insert b in most sig bit position
   }
   return(d);
}

You only need to initialize the lcd library once so it should only be done in setup() not in loop().
The LCD initialization is relatively time consuming.
Removing it from loop() will make a difference.

I would do that before doing anything else.

If you want a faster library, you could switch to the hd44780 library and the hd44780_I2Cexp i/o class.

— bill

Your Arduino Uno is a 5V Arduino board, and all your sensors are 5V as well. That is good.

You have to fix the sketch. To stay out of trouble you have to use the most common, most normal code and libraries that everyone else is using. Don't grab random code from just any website. There is a lot of bad code out there.

Connect the DS18B20 with the Arduino Uno. Use the Library Manager to install the DallasTemperature. Run a test sketch (or one of the examples) and see if that works. The 1-Wire code in your sketch is too hard to maintain and has to go.

How often do you want to get the temperature from the DS18B20 ? For some applications, once per minute is enough.

Do you want to update the display very often ? When you scroll text and the display is updated too often, then you only see a blur. I prefer that you don't scroll at all. Those LCD display are very slow (the electronics is fast, but the liquid crystals are slow). They can do perhaps 4 updates per second.

Make a seperate test-sketch for the flowmeter. Your code with sei(), delay (1000), cli(), sei() will stop the sketch too much. You use the Wire library and Serial library and those use interrupts. There is better code that uses a interval and use millis() to calculate the exact time in milliseconds of the interval.

If every separate part is working, then you can try to put them together.

:o wow , thanks you guys ! (I had disabled notification by mail , now it should be activated). @Bill : Ok , I'll try to understand the library you suggest but before I'll try to make the change you advised. @Koe : Ok , understood , specially for a newbie like me make the things more complicated could not help much. So if the right and useful procedure is what you mention I'll go for that one. I remember that this 1-Wire code was used for resolve the 85 degree issue at start up of the UNO. It reported wrongly 85 degree after any reboot but not after an upload of the sketch. So I ended there.

How often do you want to get the temperature from the DS18B20 ? For some applications, once per minute is enough.

-The temp would be fine also every 1 minute .

Do you want to update the display very often ?

-Is not necessary very often , just 1 sec is far to slow for read the text is scrolling, One letter for second make age for read the phrase . So maybe one character for half of a sec could be fine? Maybe 1/3 of a sec? I'm not interested in a super fast refresh. Ok , I read around that this display is slow so I have not pretensions. What it will be able do produce will be fine.

I prefer that you don't scroll at all. Those LCD display are very slow (the electronics is fast, but the liquid crystals are slow). They can do perhaps 4 updates per second.

Ok, keeping in in mind that is not a powerful combo would be nice for me to see something moving trough the glass. (I'll post the machine picture after work will be is completed :) ) . So if is not so prohibitive I would try to make this work.

Make a seperate test-sketch for the flowmeter. Your code with sei(), delay (1000), cli(), sei() will stop the sketch too much. You use the Wire library and Serial library and those use interrupts. There is better code that uses a interval and use millis() to calculate the exact time in milliseconds of the interval.

  • Ok understood the guidelines . I'll try to apply all that both of you suggested.

The delay(1000) is necessary for the right calculation of the flow. If I change that number or omit it the flow report totally wrong numbers.

In the next days I'll try to do something (I'll need time for the understanding) . It will not be easy but will be fun :grin: . Thanks again !

giostark: The delay(1000) is necessary for the right calculation of the flow. If I change that number or omit it the flow report totally wrong numbers.

No, that is what millis() is for. The function millis() returns the number of milliseconds that the Arduino board is running.

Suppose you capture this moment: c1 = interruptCounter; t1 = millis();

And sometime later you capture this moment: c2 = interruptCounter; t2 = millis();

The interval could be 1 second or half a second or 2 seconds. When you know the number of interrupts and the time in milliseconds, then you can calculate the rpm.

Instead of taking two captures, let's capture the data just once and use the previous captured data to calculate the difference.

volatile unsigned long interruptCounter;
unsigned long previousCounter;
unsigned long previousMillis;

void myISR()
{
  interruptCounter++;
}

void loop()
{
  // For maximum accuracy, keep the millis value and the counter value together,
  // by disabling the interrupts for a very short time.
  noInterrupts();
  unsigned long currentMillis = millis();
  unsigned long currentCounter = interruptCounter;
  interrupts();

  // Important: the elapsedMillis and elapsedCounter have no
  // rollover problem, because the difference is calculated with a previous one.
  unsigned long elapsedMillis = currentMillis - previousMillis;
  unsigned long elapsedCounter = currentCounter - previousCounter;

  // Interrupts per second is number of interrupts divided by the time.
  // The time is the elapsedMillis value divided by 1000 to get the seconds.
  float interruptsPerSecond = float( elapsedCounter) / (float( elapsedMillis) / 1000.0);
  float rmp = interruptsPerSecond .... // some kind of conversion

  // Use the current value for the next calculation.
  previousMillis = currentMillis;
  previousCounter = currentCounter;

  delay( 1000);
}

With this code, not a single interrupt is wasted. You can add many more things in the loop(), they can even take a long time, and the rpm will still be okay. Suppose the rpm is very low and has jitter, then a low-pass filter can be made in software.

We don't like to wait and waste precious processing time ;) It is possible to remove that delay(1000); at the end of the loop(). The Blink Without Delay shows how to make a timer with millis().

That has major advantages. You can make a millis-timer to update the display 4 times per second. Another millis-timer to capture the rpm every second. Another millis-timer to read the temperatures every minutes. And so on. But let's not rush ahead, start with something basic that you can understand. I was only showing a glimpse of the future :P

Gosh... I'll be honest ... When I started this thing I was shallow. I thought "I'll copy-past some data and I'll get what i want". But the things of this world teach us that this method is just a clever functional/dysfunctional system for people won't face the effort. Maybe they just have not time to spend and it's fine with that , but is not my case , so I must put my processor at good use now. I like your approach :) . Very well , let me try some stuff !

giostark: I thought "I'll copy-past some data and I'll get what i want".

You can, but once you left the main road that everyone is on, everything went haywire and you got stuck in the woods.

DS18B20 : Use the DallasTemperature library. It is in the Library Manager. If there is a problem, fix the problem. The library is good.

Count low frequency pulses : Use a library that is known to be good: FreqMeasure. It is in the Library Manager.

I2C LCD display: You already have that working :)

Avoid fancy things. Make it basic and working. The the KISS principle was invented for these situations. You have to focus on keeping it simple. If it runs okay for a year, and you have become a good Arduino programmer, then you can add fancy things such as scrolling text.

You see, it is not that hard 8)