Problems with GPIO push button on self build emonPi

Well that’s interesting!

That line is as the original (except for swapping out the hard coded 16 to a variable set to 20)

but it does indeed sound incorrect, for multiple reasons. 1. You cannot pass multiple values to int() and 2. you cannot int() a hex value and current_lcd_i2c is set to a hex value in

I do not know offhand what is expected or what that line’s trying to do as it get stranger! The lcddriver.lcd only accepts an address (not line length)

As a sidenote it also looks like the “2LINE”'s are hardcoded here as well

Right now I can’t even explain how this is normally working! I’m guessing his isn’t going to be straightforward to fathom out and I’m afraid I’m heading out soon and I have another busy week so progress may be incredibly slow (and painful)

It’s also interesting to find that although it shows up to 4 lines implemented in the driver

there are only options to set it to 1 line or 2 lines

???

I appreciate all the help, would get no where without you, the wait is worth it thank you.
As a side note moved it back to 16 following error.

pi@emonpi:~/emonpi/lcd $ sudo python  emonPiLCD1.py
emonPiLCD logging to: /var/log/emonpilcd/emonpilcd.log
I2C LCD DETECTED Ox27
emonSD-30Oct18
Traceback (most recent call last):
  File "emonPiLCD1.py", line 565, in <module>
    main()
  File "emonPiLCD1.py", line 561, in main
    updateLCD() ;
  File "emonPiLCD1.py", line 329, in updateLCD
    lcd[2] = "Page {} line 3".format(page)
  File "emonPiLCD1.py", line 396, in __setitem__
    raise IndexError("line number out of range")
IndexError: line number out of range
pi@emonpi:~/emonpi/lcd $

# Define display sizes
charCount = 16        # set to 16 or 20 chars per line depending on display used
lineCount = 4         # set to 2 or 4 lines depending on display used.

Aha!!!

Sussed it! (I think!)

It’s the comment that is wrong! It led me to change it to 20 for your display!

The “16” IS NOT the number of chars! It is the base for the int() function. We need to change that back to 16 and NOT use charCount eg

        # Init LCD using detected hex I2C address cast to an int
        self.lcd = lcddriver.lcd(int(current_lcd_i2c, 16))

[edit] That explains the int() concerns and arrives at a single address integer to be passed to the driver.

It doesn’t clarify the position over the number of lines active in the driver function.

Changing it like this

        if lcd_status.rstrip() == 'False':
            print ("I2C LCD NOT DETECTED on either 0x" + str(lcd_i2c) + " ...exiting LCD script")
            logger.error("I2C LCD NOT DETECTED on either 0x" + str(lcd_i2c) + " ...exiting LCD script")
            # add file to identify device as emonbase
            open('/home/pi/data/emonbase', 'a').close()
            sys.exit(1)

        # Init LCD using detected I2C address.
        self.lcd = lcddriver.lcd(int(current_lcd_i2c, 16))
        self._display = ['', '', '', '']

    def __setitem__(self, line, string):
        if not 0 <= line <= 3:
            raise IndexError("line number out of range")
        # Format string to exactly the width of LCD.
        string = '{0!s:<{1}.{1}}'.format(string, 16)
        if string != self._display[line]:
            self._display[line] = string
            self.logger.debug("LCD line {0}: {1}".format(line, string))
            self.lcd.lcd_display_string(string, line + 1)

And it changes page 4,5,6,7,8,9…11

Excellent!

The line length is still wrong, you have used “16” in this line

        # Format string to exactly the width of LCD.
        string = '{0!s:<{1}.{1}}'.format(string, 16)

That should be charCount or a hard coded 20 and the line after that should use lineCount. eg

        # Format string to exactly the width of LCD.
        string = '{0!s:<{1}.{1}}'.format(string, charCount)
        self._display = [''] * lineCount

Made those changes. Did notice that changing it from 1 to 4 then the 4 lines work, reverting back to original only 2 lines work

def __setitem__(self, line, string):
        if not 0 <= line <= 1:

to

def __setitem__(self, line, string):
        if not 0 <= line <= 4:
        # Init LCD using detected I2C address.
        self.lcd = lcddriver.lcd(int(current_lcd_i2c, charCount))
        self._display = [''] * lineCount

    def __setitem__(self, line, string):
        if not 0 <= line <= 4:
            raise IndexError("line number out of range")
        # Format string to exactly the width of LCD.
        string = '{0!s:<{1}.{1}}'.format(string, charCount)
        if string != self._display[line]:
            self._display[line] = string
            self.logger.debug("LCD line {0}: {1}".format(line, string))
            self.lcd.lcd_display_string(string, line + 1)

this is how the code stands

emonPiLCD logging to: /var/log/emonpilcd/emonpilcd.log
I2C LCD DETECTED Ox27
Traceback (most recent call last):
  File "emonPiLCD1.py", line 565, in <module>
    main()
  File "emonPiLCD1.py", line 449, in main
    lcd = LCD(logger)
  File "emonPiLCD1.py", line 391, in __init__
    self.lcd = lcddriver.lcd(int(current_lcd_i2c, charCount))
ValueError: invalid literal for int() with base 20: '0x27'

Changing to 16 it works no errors.

# Init LCD using detected I2C address.
        self.lcd = lcddriver.lcd(int(current_lcd_i2c, 16))
        self._display = [''] * lineCount

    def __setitem__(self, line, string):
        if not 0 <= line <= 4:
            raise IndexError("line number out of range")
        # Format string to exactly the width of LCD.
        string = '{0!s:<{1}.{1}}'.format(string, charCount)
        if string != self._display[line]:
            self._display[line] = string
            self.logger.debug("LCD line {0}: {1}".format(line, string))
            self.lcd.lcd_display_string(string, line + 1)

Pushing the button too quickly messes up the display,pushing it a few more times fixes it.

That is actually 5 lines! (on account of the ‘less than or equal to’: 0, 1, 2, 3 & 4).

I can believe that. Python is an interpreted language, so you expect it to be slower than most.

Changed to

if not 0 <= line <= 3:
1 Like

IMO that should be

if not 0 <= line < lineCount:

That works aswell

@pb66 So if i understand all this, ill have to wright to all 4 lines on each page,or the info wont be cleared,when using the push button to a new page?

is there a possibility off adding this to the beginning or end of each page, it will clear all then display what is there?

 self.lcd.lcd_clear()
    elif page == 6:
    # Get uptime
        with open('/proc/uptime', 'r') as f:
            seconds = float(f.readline().split()[0])
        r.set('uptime', seconds)

        lcd[0] = datetime.now().strftime('%b %d %H:%M')
        lcd[1] = 'Uptime %.2f days' % (seconds / 86400)

It looks as if that would cause the display to flicker, because updateLCD( ) is called from the main loop.

I’d suggest you write [space] characters to the empty lines instead. According to the tutorial I’ve been reading:

lcd[2] = " " * charCount
lcd[3] = " " * charCount

(Assuming you have charCount as Paul suggested.)

Or make it even more universal:

for i in range(2, lineCount):
    lcd[i] = ' ' * charCount

I don’t think it would be a good idea to use clear either. Presumably you do want to populate most lines most of the time and using clear then writing to many of the lines is possibly doubling the workload needlessly.

I would also recommend writing a empty line when you want it cleared, although I would do it slightly differently and just use

lcd[2] = ""
lcd[3] = ""

as each line would then get padded out to charCount later in the code

        # Format string to exactly the width of LCD.
        string = '{0!s:<{1}.{1}}'.format(string, charCount)

However, I would only expect you to have blank lines on one or 2 function specific pages, there is no point in having multiple 2 line pages if they can be displayed on one page and reduce the page count, which translates to fewer button presses.

That’s due to the way the button presses are processed. Every button press results in a page being refreshed, press the button 8 times to get to the SSH option and it will have to update each of the 8 screens to get there. Ideally each button press should just increment the page number and there should be a wait for a break in button presses before updating the display. This would make the screen more responsive because it would be able to jump between non-adjacent pages without updating all pages in between with a minimal, perhaps undetectable delay when scrolling through one screen at a time.