seanspotatobusiness
Posts: 192
Joined: Tue May 22, 2012 11:19 pm

Sending individual lines to an LCD display, bumping up the previous lines

Sun Apr 21, 2019 6:31 pm

I have a few different python scripts which monitor some websites' APIs and send notifications to my phone when conditions are met. The different scripts run inside separate panes in a tmux window and print to the screen every five minutes the time/date and status. I recently added a 16x2 LCD display to my Pi and now I would like the scripts to also print to the LCD but the problem is that when I print a single 16 character line to the LCD it replaces the previous screen. Since every update will only take 16 characters, I have enough room for the latest two updates. Is there a way I can print a line to the screen which pushes the bottom line up to the top line and the top line off the screen (i.e. the oldest line gets lost/deleted)? Thanks
Raspberry Pi 3 B and B+
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"

pcmanbob
Posts: 7060
Joined: Fri May 31, 2013 9:28 pm
Location: Mansfield UK

Re: Sending individual lines to an LCD display, bumping up the previous lines

Sun Apr 21, 2019 7:12 pm

The 16x2 LCD does not have an automatic scroll feature, so you will have to do it in your program by rewriting the line from the bottom line on to the top line and then writing a new bottom line.

You can control which line you are writing to using the command lcd.cursor_pos

I suggest you read the documentation https://rplcd.readthedocs.io/en/stable/ ... arted.html
We want information… information… information........................no information no help
The use of crystal balls & mind reading are not supported

seanspotatobusiness
Posts: 192
Joined: Tue May 22, 2012 11:19 pm

Re: Sending individual lines to an LCD display, bumping up the previous lines

Mon Apr 22, 2019 12:11 am

Since my scripts are running independently of each other, should I send the output of each one to a log file and then use another script to print the last two lines of the log file to the LCD?

If script one sends a line to the LCD, script two does not know about it in order to rewrite it. I think I could read the characters back off the LCD but then I might have to use a logic level shifter or something because the LCD operates at 5 V logic. Currently the read/write pin of the LCD is tied to ground to prevent it ever sending anything back to the Pi.
Raspberry Pi 3 B and B+
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"

hippy
Posts: 6068
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Sending individual lines to an LCD display, bumping up the previous lines

Mon Apr 22, 2019 1:44 am

You could have your two scripts writing files, or better some FIFO memory buffers or pipes, and have a third which collates both and updates the screen.

That way this collating program knows what it has written to the screen, so can easily move the last up a line, while the other two have no care what's actually on the screen.

It also means you don't have two programs needing to interact with each other, having to cooperate to avoid writing over each other.

The two programs feeding the data simply send their data and move on. The receiving collator simply reads data from each sender until it has a line, updates the display, delays a while so nothing goes by too fast, then waits for the next full line, updates, repeats.

seanspotatobusiness
Posts: 192
Joined: Tue May 22, 2012 11:19 pm

Re: Sending individual lines to an LCD display, bumping up the previous lines

Mon Apr 22, 2019 3:20 am

Thanks very much. I'm attempting to use this example: http://timmurphy.org/2013/11/11/using-fifos-in-python/

My test python file has

Code: Select all

path = "/tmp/16x2_LCD.fifo"
os.mkfifo(path)
The problem I encounter is that when the python file is run again I get the error:

Code: Select all

 $ python /home/pi/Mining/LCDUpdater.py
Traceback (most recent call last):
  File "/home/pi/Mining/LCDUpdater.py", line 7, in <module>
    os.mkfifo(path)
OSError: [Errno 17] File exists
I tried adding ", exist_ok=True" which apparently avoids the error when making directories but that isn't accepted:

Code: Select all

TypeError: mkfifo() takes no keyword arguments
I've read enough to infer that the following is intended to handle the error but it fails when I try to run it:

Code: Select all

path = "/tmp/16x2_LCD.fifo"

try:
    os.mkfifo(path)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise e

Code: Select all

 $ python /home/pi/Mining/LCDUpdater.py
  File "/home/pi/Mining/LCDUpdater.py", line 11
    except OSError as e:
         ^
SyntaxError: invalid syntax
Alternatively

Code: Select all

path = "/tmp/16x2_LCD.fifo"

try: os.mkfifo(path)
except OSError: pass        
        
gets past this hurdle but I think it ignores errors that might be relevant as well as just the error that the file already exists.
Raspberry Pi 3 B and B+
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"

hippy
Posts: 6068
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Sending individual lines to an LCD display, bumping up the previous lines

Mon Apr 22, 2019 10:44 am

This works for me using both Python 2.7 and Python 3. The sender in this example terminates itself after sending 5 numbers, one per second. You have to Ctrl-C break the receiver to end that.

Note you have to open, write, then close the FIFO before the receiver will receive what's been written to it.

Code: Select all

import os
import time

fifoName = "/tmp/test.fifo"

try            : os.mkfifo(fifoName)
except OSError : pass

for n in range(1,6):
  with open(fifoName,"w") as f:
    f.write(str(n)+"\n")
    print("Sender: Sent "+str(n))
  time.sleep(1)
print("Sender: Ended")

Code: Select all

fifoName = "/tmp/test.fifo"

while True:
  with open(fifoName,"r") as f:
    for s in f:
      print("Reader: Read "+s.rstrip())

Code: Select all

[email protected]:~ $ python sender.py &
[1] 490
[email protected]:~ $ python reader.py
Sender: Sent 1
Reader: Read 1
Sender: Sent 2
Reader: Read 2
Sender: Sent 3
Reader: Read 3
Sender: Sent 4
Reader: Read 4
Sender: Sent 5
Reader: Read 5
Sender: Ended
You can start multiple senders but I have not fully investigated what happens if a program tries to opens the FIFO for writing when another has it open. I suspect it blocks, but you would have to test or read the documentation, and might have to add some error trapping.

Code: Select all

[email protected]:~ $ python sender.py &
[1] 598
[email protected]:~ $ python sender.py &
[2] 600
[email protected]:~ $ python reader.py
Sender: Sent 1
Sender: Sent 1
Reader: Read 1
Reader: Read 1
Sender: Sent 2
Reader: Read 2
Sender: Sent 2
Reader: Read 2
etc

seanspotatobusiness
Posts: 192
Joined: Tue May 22, 2012 11:19 pm

Re: Sending individual lines to an LCD display, bumping up the previous lines

Mon Apr 22, 2019 2:38 pm

Thanks very much. You said it's important to close the FIFO but it looks like your code doesn't do that? Do I need "close.fifoName()" at the end of the write and read sections?

Code: Select all

import os
import sys

fifoName = "/tmp/16x2_LCD.fifo"

try            : os.mkfifo(fifoName)
except OSError : pass

for n in range(1,6):
      with open(fifoName,"w") as f:
        f.write(str(n)+"\n")
        print("Sender: Sent "+str(n))
        time.sleep(1)
    print("Sender: Ended")

    while True:
        with open(fifoName,"r") as f:
            for s in f:
                print("Reader: Read "+s.rstrip())

I have to press Ctrl + C to stop the script to get any feedback. When I do that, I get the following:

Code: Select all

$ python3 /home/pi/Mining/LCDUpdater.py
^CTraceback (most recent call last):
  File "/home/pi/Mining/LCDUpdater.py", line 14, in <module>
    with open(fifoName,"w") as f:
KeyboardInterrupt
Raspberry Pi 3 B and B+
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"

hippy
Posts: 6068
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Sending individual lines to an LCD display, bumping up the previous lines

Mon Apr 22, 2019 3:16 pm

seanspotatobusiness wrote:
Mon Apr 22, 2019 2:38 pm
Thanks very much. You said it's important to close the FIFO but it looks like your code doesn't do that? Do I need "close.fifoName()" at the end of the write and read sections?
No, the "with open() as" command handles the open and close, the close happens automatically when you get back to the indentation level of the with.

The following two are effectively the same, excepting any errors I've made through typing here and not testing ...

Code: Select all

with open("myfile.txt","w") as f:
  DoSomething()

Code: Select all

f = open("myfile.txt","w")
DoSomething()
f.close()
seanspotatobusiness wrote:
Mon Apr 22, 2019 2:38 pm

Code: Select all

    print("Sender: Ended")

    while True:
        with open(fifoName,"r") as f:
It looks like you have merged the two programs, sender and reeader, into one. That doesn't work because you end up with what I think is a 'race condition'; the sender not sending until the reader has opened its end of the FIFO.

Though it actually seems a bit more complicated than that. That it Ctrl-C aborts when it gets to the opening for reading point means it must have executed all the sending code, but that open for reading has presumably still blocked.

But you should still have seen the print("Sender: Ended") output. It's not clear though because the indentation of your code seems all over the place.

Not sure exactly what is going on but put the two pieces of code in their own sender.py and reader.py file and run as below and it should all work as it did for me. That ampersand at the end of the first line is important -

Code: Select all

python3 /home/pi/Mining/sender.py &
python3 /home/pi/Mining/reader.py

seanspotatobusiness
Posts: 192
Joined: Tue May 22, 2012 11:19 pm

Re: Sending individual lines to an LCD display, bumping up the previous lines

Mon Apr 22, 2019 8:11 pm

Thanks again; I think it's working after separating it into two programs. Is there some way to limit the FIFO to two lines so that the oldest gets deleted when another is added?

I'm trying to get the reader file to send output to the LCD screen but it's not accepting my first attempt. I know my first attempt is wrong anyway because I don't know how to make the two lines come from the FIFO. Even if my code did work, I think it would just do the same as it was in my OP, replacing the LCD screen entirely, instead of bumping a line up each time.

Code: Select all

import RPi.GPIO as GPIO
from RPLCD.gpio import CharLCD

fifoName = "/tmp/test.fifo"

      
# Initialize display. All values have default values and are therefore optional.
lcd = CharLCD(pin_rs=36, pin_e=38, pins_data=[31, 33, 35, 37],
              numbering_mode=GPIO.BOARD,
              cols=16, rows=2, dotsize=8,
              auto_linebreaks=False,)

while True:
  with open(fifoName,"r") as f:
    for s in f:
      print("Reader: Read "+s.rstrip())
      filcd.write_string("Reader: Read "+s.rstrip() "\r\n2nd line here")

Code: Select all

$ python /home/pi/Mining/reader.py
  File "/home/pi/Mining/reader.py", line 17
    filcd.write_string("Reader: Read "+s.rstrip() "\r\n2nd line here")
                                                                    ^
SyntaxError: invalid syntax
Raspberry Pi 3 B and B+
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"

hippy
Posts: 6068
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Sending individual lines to an LCD display, bumping up the previous lines

Tue Apr 23, 2019 7:15 pm

seanspotatobusiness wrote:
Mon Apr 22, 2019 8:11 pm
Is there some way to limit the FIFO to two lines so that the oldest gets deleted when another is added?
This will simulate doing that -

Code: Select all

pipeName = "/tmp/test.fifo"

top = ""
bot = ""

while True:
  with open(pipeName,"r") as fIn:
    for s in fIn:
      s = s.rstrip()
      if top == "":
        top = s
      elif bot == "":
        bot = s
      else:
        top = bot
        bot = s
      print("Top: "+top)
      print("Bot: "+bot)
      print("")
You will need to replace those print commands with whatever is required to interact with your LCD and I can't help with that I'm afraid.

seanspotatobusiness
Posts: 192
Joined: Tue May 22, 2012 11:19 pm

Re: Sending individual lines to an LCD display, bumping up the previous lines

Tue Apr 23, 2019 9:46 pm

Thanks very much. I think this is all I need now!

Edit: it's working. In case anyone wants the final code, the sender is as follows:

Code: Select all

import os
import time

fifoName = "/tmp/test.fifo"

try            : os.mkfifo(fifoName)
except OSError : pass

for n in range(1,6):
  with open(fifoName,"w") as f:
    f.write("TO Sean___"+str(n)+"\n")
    print("Sender: Sent "+str(n))
  time.sleep(1)
print("Sender: Ended")
The reader is as follows:

Code: Select all

import RPi.GPIO as GPIO
from RPLCD.gpio import CharLCD

fifoName = "/tmp/test.fifo"

top = ""
bot = ""
      
# Initialize display. All values have default values and are therefore optional.
lcd = CharLCD(pin_rs=36, pin_e=38, pins_data=[31, 33, 35, 37],
              numbering_mode=GPIO.BOARD,
              cols=16, rows=2, dotsize=8,
              auto_linebreaks=False,)

lcd.write_string("No events since\r\nsystem start")

while True:
  with open(fifoName,"r") as fIn:
    for s in fIn:
      s = s.rstrip()
      if top == "":
        top = s
      elif bot == "":
        bot = s
      else:
        top = bot
        bot = s
      print("Top: "+top)
      print("Bot: "+bot)
      lcd.clear()
      lcd.write_string(top+"\r\n"+bot)
Raspberry Pi 3 B and B+
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"

hippy
Posts: 6068
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Sending individual lines to an LCD display, bumping up the previous lines

Wed Apr 24, 2019 11:59 am

Excellent news.

It might be good enough as it is but you might be able to lose the clear command and make display scrolling nicer by space padding top and bottom lines to be 16 characters strings, then send a home instead of the clear before the two padded lines.

A time.sleep() after the write_string() will stop any too rapid scrolling or blurring if multiple lines of data did appear simultaneously from multiple senders.

Return to “Python”