i2c 4 pin, LED 20x4 and OLED 128x64 python scrips for H2 stats

Post Reply
puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

i2c 4 pin, LED 20x4 and OLED 128x64 python scrips for H2 stats

Unread post by puremind » Mon Jul 01, 2019 12:48 am

:geek: Hi All:

If anybody has got the i2c 20 pin and installed 2 drives, I've modified the hello_world.py file to display the wake/sleep status for those. Original git from author https://github.com/djjproject/odroidh2_i2c_lcd

Also, I've modified the TX/RW Mbps to UP/DL and MB/s , plus some date time adjustment to EU vs US m-d format and removal of decimals for CPU and MEM % consumption.

See pic sample below if anyone interested simply update your /home/odroidlcd/hello_world.py file to the below code, then restart service ( service odroidlcd restart ) or reboot machine:
(it might help others to do their own)

If anybody has other options to share or other ideas on how to use this LCD display it would be great :)

Updated:
1. Start of the script will display your hostname on LCD
2. You can define what sdx drives you want displayed
3. CPU and MEM moved to left side lines 2 & 3 where Upload and DownLoad to the right so it's all easier to read

7 Aug Updated
1. Coeficients
2. Wait 0.96 vs 1.00
3. Fix for Kernel 5.x


17 Aug added OLED script at post 38

Code: Select all

# import modules
import lcddriver as lcd
import stat
import os
import psutil as ps
from time import time
from time import sleep
from datetime import datetime

sleep(10)

# test code
#lcd.display_string("11111111111111111111222222222222222222223333333333333333333344444444444444444444", 1)


# load lcd
lcd = lcd.lcd()

lcd.display_string("                    ",1)
lcd.display_string(str.center(str(os.popen('hostname').readline())[:-1],20,' '),2)
lcd.display_string("      STARTING      ",3)
lcd.display_string("                    ",4)



#change here to sda sdb sdc etc
#disk1_to_check="sda"
#disk2_to_check="sdb"

disk1_to_check="sda"
disk2_to_check="sdb"


sleep(1)





#check if disk exists function
def disk_exists(path):
     try:
             return stat.S_ISBLK(os.stat(path).st_mode)
     except:
             return False


# network status get function
def get_bytes(t, iface='enp2s0'):
    with open('/sys/class/net/' + iface + '/statistics/' + t + '_bytes', 'r') as f:
        data = f.read();
        return int(data)


while True:

  # foot powered coding
  #cpuUsage=str(round(float(os.popen('''grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage }' ''').readline()),2))
  #tot_m, used_m, free_m = map(int, os.popen('free -t -m').readlines()[-1].split()[1:])
  #percentMem=str(round(used_m/tot_m))


  # check if disk sda exists
  
  
  #hddexists1=str(os.popen('ls /dev/'+disk1_to_check).readline())[:][:-1]
  
  
  if disk_exists("/dev/"+disk1_to_check)!=True:
    hddstr1="NONE"
  else :

   # read hdd state from hdparm
   hddstate1=str(os.popen('hdparm -C /dev/'+disk1_to_check+' | grep state | cut -f 2 -d :').readline())[2:][:-1]

   if hddstate1=="standby":
    hddstr1="SLEEP"
   else :
    hddstr1="WAKE"


  

  if disk_exists("/dev/"+disk2_to_check)!=True:
    hddstr2="NONE"
  else :

   # read hdd state from hdparm
   hddstate2=str(os.popen('hdparm -C /dev/'+disk2_to_check+' | grep state | cut -f 2 -d :').readline())[2:][:-1]

   if hddstate2=="standby":
    hddstr2="SLEEP"
   else :
    hddstr2="WAKE"



 # line 1 print (hddstate, friendly name)
  lcd.display_string(disk1_to_check+":"+hddstr1.rjust(5)+"  "+disk2_to_check+":"+hddstr2.rjust(5), 1)





  # get network speed for 1sec
  tx1 = get_bytes('tx')
  rx1 = get_bytes('rx')
  sleep(0.96)
  tx2 = get_bytes('tx')
  rx2 = get_bytes('rx')
  tx_speed = (tx2 - tx1)/1048576.0
  rx_speed = (rx2 - rx1)/1048576.0



  # line 2 print (cpu, memory)
  # use psutil to get CPU usage and Memory Usage
  # line 3 print (network speed)
  # bps -> mpbs (*8)

  lcd.display_string("CPU:"+str(round(ps.cpu_percent())).rjust(3)+"%"+" UP:"+str(round(tx_speed)).rjust(3)+" MB/s", 2)
  lcd.display_string("MEM:"+str(round(ps.virtual_memory()[2])).rjust(3)+"%"+" DL:"+str(round(rx_speed)).rjust(3)+" MB/s", 3)

  # line 4 print (cputemp, date, time)
  # use lm-sensors to get cpu temp / get time data from python internal method
  dateString = datetime.now().strftime('%d-%b')
  timeString = datetime.now().strftime('%H:%M:%S')
 lcd.display_string("T"+os.popen('sensors | grep "id 0:" | cut -d+ -f2 | cut -c1-2').read()[:-1]+"C "+dateString+" "+timeString, 4)

  # line 4 print (cputemp, uptime)
  # use /proc/uptime and python divmod method
  #uptime=os.popen('cat /proc/uptime | cut -d " " -f1').read()
  #uptimeDay=divmod(float(uptime),86400)
  #uptimeHour=divmod(uptimeDay[1],3600)
  #uptimeMin=divmod(uptimeHour[1],60)
  #uptimeString=str(int(uptimeDay[0]))+"D:"+str(int(uptimeHour[0]))+"H:"+str(int(uptimeMin[0]))+"M"
  #lcd.display_string("TEMP:"+os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()[:-1]+"C "+uptimeString.zfill(3), 4)


  # sleep(1)


Image-1.jpg
Image-1.jpg (187.46 KiB) Viewed 1781 times



ADDING NEW SCRIPT IN A NEW OPST ON THIS THREAD
Last edited by puremind on Sun Aug 18, 2019 1:25 am, edited 8 times in total.
These users thanked the author puremind for the post:
domih (Mon Jul 22, 2019 9:53 am)
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

User avatar
odroid
Site Admin
Posts: 31852
Joined: Fri Feb 22, 2013 11:14 pm
languages_spoken: English
ODROIDs: ODROID
Has thanked: 89 times
Been thanked: 255 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by odroid » Mon Jul 01, 2019 1:55 pm

Thank you for sharing a very nice and useful Python code. :)

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Sat Jul 06, 2019 8:49 pm

In the absence of disk writes "wake"

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Sat Jul 06, 2019 9:51 pm

RomaT wrote:
Sat Jul 06, 2019 8:49 pm
In the absence of disk writes "wake"
correct
I am rewriting this with a nicer look and will post back
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Sat Jul 06, 2019 10:45 pm

First post updated with script and new picture to fix the no device detected also added:

1. Start of the script will display your hostname on LCD
2. You can define what sdx drives you want displayed
3. CPU and MEM moved to left side lines 2 & 3 where Upload and DownLoad to the right so it's all easier to read
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Sun Jul 07, 2019 12:25 am

Generally a good idea to use LCD ...
In my case, I had to connect power to +5V
from 3.3V as described in Wiki, the display (Winstar WH2004D-YYK-CT) did not show anything.
and there is an error in the description sudo apt install python3-psutil lm-sensor hdparm must be lm-sensors
.
P7171024s.jpg
P7171024s.jpg (108.7 KiB) Viewed 1004 times
Last edited by RomaT on Thu Jul 25, 2019 3:55 pm, edited 1 time in total.

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Sun Jul 07, 2019 12:53 am

Lol, I see you've taken pieces from here and there ;)

Note the LCD I got has the blue background/white letters and yours have a green background with black letters, I wonder why the difference ?

This is the one I bought:
https://www.hardkernel.com/shop/i2c-lcd-module/
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Sun Jul 07, 2019 12:58 am

Ok, I see, there's a lot of them ... would be nice to see other alternatives that work with the H2, specially the OLED models look very nice.
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Sun Jul 07, 2019 1:11 am

I think you can use any LCD with interface 6800 or I2C ...
I bought the display in my country with the support of my language (in the first line LCD of my photo it can be seen).

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Sun Jul 07, 2019 9:47 pm

Would one of these work ?
https://www.ebay.com/itm/0-96-I2C-4PIN- ... SwKdZbYtEW

The look pretty damn cheap
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Sun Jul 07, 2019 10:35 pm

It will work, only it is a graphic, it will need another library of drivers and font library ...
In the subject we are considering a character LCD, already with a character set.

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Thu Jul 11, 2019 6:23 am

Everybody,
I've made some changes here and there if anybody wants to try...

Managed, Celsius vs Fahrenheit with custom chars
Moving Bars for CPU & RAM. Two drives status with storage capacity.

Same as the first post, simply update the hello_world.py as instructed, then edit the config section on the script for full customisation, including 3/4 characters for the first drive to monitor.

Animation really nice on the percentage section.

Code: Select all

# -*- coding: utf-8 -*-
"""
Compiled, mashed and generally REmutilated 2019 by puremind
Made available under GNU GENERAL PUBLIC LICENSE

# added bits and pieces from various sources
# 2019-07-07, ver 0.1
# Add your own custom stuff with custom characters

"""
from time import sleep
import lcddriver as lcd


lcd = lcd.lcd()

lcd.display_string("                    ",1)
lcd.display_string("                    ",2)
lcd.display_string("                    ",3)
lcd.display_string("                    ",4)


sleep(1)

lcd.clear()



#set to False for Celsius, True for Fahrenheit
fahrenheit=False


#change here to sda sdb sdc etc
#disk1_to_check="sda"
#disk2_to_check="sdb"

disk1_to_check="nvme0n1p3"

#change here to partition full name for pct usage, check with "df -h"
path_disk1="/"


disk2_to_check="sda"
path_disk2="/mnt/data"

#Time to sleep between checks. Defaults 3
delay=3
delay=delay-0.04

#offsets 1 char to the right if the first disk starts with "nvme"
if disk1_to_check[0:4]!="nvme":
 nvmeoffset=0
else :
 nvmeoffset=1




# import modules
#import RPi_I2C_driver
import stat
import os
import psutil as ps
import math
from time import time

from datetime import datetime

sleep(5)



# -*- coding: utf-8 -*-
"""
Compiled, mashed and generally mutilated 2014-2015 by Denis Pleic
Made available under GNU GENERAL PUBLIC LICENSE

# Modified Python I2C library for Raspberry Pi
# as found on http://www.recantha.co.uk/blog/?p=4849
# Joined existing 'i2c_lib.py' and 'lcddriver.py' into a single library
# added bits and pieces from various sources
# By DenisFromHR (Denis Pleic)
# 2015-02-10, ver 0.1

"""
#
#
import smbus
from time import *

class i2c_device:
   def __init__(self, addr, port=1):
      self.addr = addr
      self.bus = smbus.SMBus(port)

# Write a single command
   def write_cmd(self, cmd):
      self.bus.write_byte(self.addr, cmd)
      sleep(0.0001)

# Write a command and argument
   def write_cmd_arg(self, cmd, data):
      self.bus.write_byte_data(self.addr, cmd, data)
      sleep(0.0001)

# Write a block of data
   def write_block_data(self, cmd, data):
      self.bus.write_block_data(self.addr, cmd, data)
      sleep(0.0001)

# Read a single byte
   def read(self):
      return self.bus.read_byte(self.addr)

# Read
   def read_data(self, cmd):
      return self.bus.read_byte_data(self.addr, cmd)

# Read a block of data
   def read_block_data(self, cmd):
      return self.bus.read_block_data(self.addr, cmd)



# LCD Address
#ADDRESS = 0x3F
ADDRESS = 0x27

# I2C bus
BUS = 1

# commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80

# flags for display entry mode
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00

# flags for display on/off control
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00

# flags for display/cursor shift
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00

# flags for function set
LCD_8BITMODE = 0x10
LCD_4BITMODE = 0x00
LCD_2LINE = 0x08
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00

# flags for backlight control
LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00

En = 0b00000100 # Enable bit
Rw = 0b00000010 # Read/Write bit
Rs = 0b00000001 # Register select bit

class lcd:
   #initializes objects and lcd
   def __init__(self):
      self.lcd_device = i2c_device(ADDRESS, BUS)

      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x02)

      self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
      self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
      sleep(0.15)


   # clocks EN to latch command
   def lcd_strobe(self, data):
      self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
      sleep(.0005)
      self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
      sleep(.0001)

   def lcd_write_four_bits(self, data):
      self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
      self.lcd_strobe(data)

   # write a command to lcd
   def lcd_write(self, cmd, mode=0):
      self.lcd_write_four_bits(mode | (cmd & 0xF0))
      self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))

   # write a character to lcd (or character rom) 0x09: backlight | RS=DR<
   # works!
   def lcd_write_char(self, charvalue, mode=1):
      self.lcd_write_four_bits(mode | (charvalue & 0xF0))
      self.lcd_write_four_bits(mode | ((charvalue << 4) & 0xF0))
  

   # put string function
   def lcd_display_string(self, string, line):
      if line == 1:
         self.lcd_write(0x80)
      if line == 2:
         self.lcd_write(0xC0)
      if line == 3:
         self.lcd_write(0x94)
      if line == 4:
         self.lcd_write(0xD4)

      for char in string:
         self.lcd_write(ord(char), Rs)

   # clear lcd and set to home
   def lcd_clear(self):
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_RETURNHOME)

   # define backlight on/off (lcd.backlight(1); off= lcd.backlight(0)
   def backlight(self, state): # for state, 1 = on, 0 = off
      if state == 1:
         self.lcd_device.write_cmd(LCD_BACKLIGHT)
      elif state == 0:
         self.lcd_device.write_cmd(LCD_NOBACKLIGHT)

   # add custom characters (0 - 7)
   def lcd_load_custom_chars(self, fontdata):
      self.lcd_write(0x40);
      for char in fontdata:
         for line in char:
            self.lcd_write_char(line)         
         
         
   # define precise positioning (addition from the forum)
   def lcd_display_string_pos(self, string, line, pos):
    if line == 1:
      pos_new = pos
    elif line == 2:
      pos_new = 0x40 + pos
    elif line == 3:
      pos_new = 0x14 + pos
    elif line == 4:
      pos_new = 0x54 + pos

    self.lcd_write(0x80 + pos_new)

    for char in string:
      self.lcd_write(ord(char), Rs)
      
      







#check if disk exists function
def disk_exists(path):
     try:
             return stat.S_ISBLK(os.stat(path).st_mode)
     except:
             return False


# network status get function
def get_bytes(t, iface='enp2s0'):
    with open('/sys/class/net/' + iface + '/statistics/' + t + '_bytes', 'r') as f:
        data = f.read();
        return int(data)






sleep(3)

# load lcd
lcd = lcd()

sleep(1)

# 5 by 8 custom char creation
#https://www.quinapalus.com/hd44780udg.html

#More info here with full char set
#http://www.electronic-engineering.ch/microchip/datasheets/lcd/charset.gif

# Define some static characters

fontdata = [
         # Char 0 - Celsius
        [ 0x8,0x14,0x8,0x3,0x4,0x4,0x4,0x3],
 		# Char 1 1/5 block 
 		[ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10],
 		# Char 2 2/5 block 
 		[ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18],
 		# Char 3 3/5 block 
 		[ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c],
 		# Char 4 4/5 block 
 		[ 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e],
        # Char 5 - Arrow up
        [ 0x0,0x4,0xe,0x1f,0x4,0x4,0x4,0x0],
        # Char 6 - Arrow down
        [ 0x0,0x4,0x4,0x4,0x1f,0xe,0x4,0x0],
        # Char 7 ZZ
        [ 0x1e,0x4,0x8,0x1e,0xf,0x2,0x4,0xf]

]


lcd.lcd_load_custom_chars(fontdata)

sleep(1)

lcd.lcd_display_string("                    ",1)
lcd.lcd_display_string(str.center(str(os.popen('hostname').readline())[:-1],20,' '),2)
lcd.lcd_display_string("      STARTING      ",3)
lcd.lcd_display_string("                    ",4)

sleep(2)

lcd.lcd_display_string(disk1_to_check[0:3+nvmeoffset]+" ? ??% "+disk2_to_check[0:3]+" ? ??%",1)

#Set 0 as speed for network first cycle
#Fixed elements
#TX / RX Arrows

lcd.lcd_display_string("CPU ??% "+chr(126)+"      "+chr(127)+" ??"+chr(6),2)
lcd.lcd_display_string("RAM ??% "+chr(126)+"      "+chr(127)+" ??"+chr(5),3)

#Celsius
if fahrenheit!=True:
 lcd.lcd_display_string_pos("??"+chr(0),4,17)
else:
 lcd.lcd_display_string_pos("??F",4,17)

sleep(1)
steps3 = [1, 2, 3]
steps = [0, 1, 2, 3, 4, 5]

while True:


  # check if disk  exists
  
  
  # get network speed for delay sec
  tx1 = get_bytes('tx')
  rx1 = get_bytes('rx')

  
  if disk_exists("/dev/"+disk1_to_check)!=True:
    hddstr1=chr(6)
  else :

   # read hdd state from hdparm
   hddstate1=str(os.popen('hdparm -C /dev/'+disk1_to_check+' | grep state | cut -f 2 -d :').readline())[2:][:-1]

   if hddstate1=="standby":
    hddstr1=chr(7)
   else :
    hddstr1=chr(5)


  

  if disk_exists("/dev/"+disk2_to_check)!=True:
    hddstr2=chr(6)
  else :

   # read hdd state from hdparm
   hddstate2=str(os.popen('hdparm -C /dev/'+disk2_to_check+' | grep state | cut -f 2 -d :').readline())[2:][:-1]

   if hddstate2=="standby":
    hddstr2=chr(7)
   else :
    hddstr2=chr(5)



 # line 1 print (hddstate, friendly name)

  #lcd.lcd_display_string(disk1_to_check+":"+hddstr1.rjust(5)+"  "+disk2_to_check+":"+hddstr2.rjust(5), 1)
  lcd.lcd_display_string_pos(hddstr1, 1,4+nvmeoffset)  
  lcd.lcd_display_string_pos(hddstr2, 1,15)  


  obj_Disk1 = ps.disk_usage(path_disk1)
  lcd.lcd_display_string_pos(str(round(obj_Disk1.percent)), 1,6+nvmeoffset)  

  obj_Disk2 = ps.disk_usage(path_disk2)
  lcd.lcd_display_string_pos(str(round(obj_Disk2.percent)), 1,17)  


  # line 2 print (cpu, memory)
  # use psutil to get CPU usage and Memory Usage
  # line 3 print (network speed)
  # bps -> mpbs (*8)


 

  for z in range(2) :
   sleep(delay/2)
   cpupct=ps.cpu_percent()
   rampct=ps.virtual_memory()[2]
  

   lcd.lcd_display_string_pos(str(math.floor(cpupct)).rjust(3),2,3)
   lcd.lcd_display_string_pos(str(math.floor(rampct)).rjust(3),3,3)

#  for y in range(101):
#    sleep(0.1)
#    cpupct=y
#    lcd.lcd_display_string_pos(str(y),3,10)

   for x in steps:
     if (math.floor(cpupct+1)>x*(100/6)) and (math.floor(cpupct)>(x+1)*(100/6)) or cpupct>98:
      lcd.lcd_display_string_pos(chr(255),2,9+x)
     else:
      if ((math.floor(cpupct)>x*(100/6)+1) and (math.floor(cpupct)<(x+1)*(100/6)) and cpupct>2):
       lcd.lcd_display_string_pos(chr(min(4,math.floor((cpupct-(x*100/6))/((100/6/4))+1))),2,9+x)
      else:  
       lcd.lcd_display_string_pos(" ",2,9+x)

     if (math.floor(rampct+1)>x*(100/6)) and (math.floor(rampct)>(x+1)*(100/6)) or rampct>98:
      lcd.lcd_display_string_pos(chr(255),3,9+x)
     else:
      if (math.floor(rampct)>x*(100/6)+1) and (math.floor(rampct)<(x+1)*(100/6)) :
       lcd.lcd_display_string_pos(chr(min(4,math.floor((rampct-(x*100/6))/((100/6/4))+1))),3,9+x)
      else:  
       lcd.lcd_display_string_pos(" ",3,9+x)




  # line 4 print (cputemp, date, time)
  # use lm-sensors to get cpu temp / get time data from python internal method
  dateString = datetime.now().strftime('%d-%b')
  timeString = datetime.now().strftime('%H:%M:%S')

#  lcd.lcd_write(0xC0)
#  lcd.lcd_write_char(1)	
  

#  lcd.lcd_display_string_pos(chr(0),4,0)
  
#  lcd.lcd_display_string_pos(os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()[:-1].rjust(3), 4,16)
  if fahrenheit!=True:
   lcd.lcd_display_string_pos(dateString+" "+timeString+" "+os.popen('sensors | grep "id 0:" | cut -d+ -f2 | cut -c1-2').read()[:-1].rjust(3), 4,0)
  else:
   lcd.lcd_display_string_pos(dateString+" "+timeString+" "+str(round(int(os.popen('sensors | grep "id 0:" | cut -d+ -f2 | cut -c1-2').read()[:-1])*9/5+32,0)).rjust(3)[0:3], 4,0)


 
  tx2 = get_bytes('tx')
  rx2 = get_bytes('rx')
  tx_speed = (tx2 - tx1)/1048576.0/delay
  rx_speed = (rx2 - rx1)/1048576.0/delay
  
  lcd.lcd_display_string_pos(str(round(rx_speed)).rjust(3),2,16)
  lcd.lcd_display_string_pos(str(round(tx_speed)).rjust(3),3,16)



  # line 4 print (cputemp, uptime)
  # use /proc/uptime and python divmod method
  #uptime=os.popen('cat /proc/uptime | cut -d " " -f1').read()
  #uptimeDay=divmod(float(uptime),86400)
  #uptimeHour=divmod(uptimeDay[1],3600)
  #uptimeMin=divmod(uptimeHour[1],60)
  #uptimeString=str(int(uptimeDay[0]))+"D:"+str(int(uptimeHour[0]))+"H:"+str(int(uptimeMin[0]))+"M"
  #lcd.lcd_display_string("TEMP:"+os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()[:-1]+"C "+uptimeString.zfill(3), 4)


  # sleep(1)



Samples:
Untitled.jpg
Untitled.jpg (54.47 KiB) Viewed 1587 times
2019-07-10 at 22.16.30.jpg
2019-07-10 at 22.16.30.jpg (54.16 KiB) Viewed 1587 times
Last edited by puremind on Wed Aug 07, 2019 9:30 pm, edited 3 times in total.
These users thanked the author puremind for the post:
domih (Mon Jul 22, 2019 9:55 am)
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

User avatar
odroid
Site Admin
Posts: 31852
Joined: Fri Feb 22, 2013 11:14 pm
languages_spoken: English
ODROIDs: ODROID
Has thanked: 89 times
Been thanked: 255 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by odroid » Thu Jul 11, 2019 10:22 am

It is beautiful and informative. :o
Especially I love the CGRAM generated font °C

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Thu Jul 11, 2019 2:16 pm

odroid wrote:
Thu Jul 11, 2019 10:22 am
It is beautiful and informative. :o
Especially I love the CGRAM generated font °C
Thanks :) That one is custom-made. :)
You could do your own chars very easily if you'd feel creative (or change the ones I got there), there's instructions in the script.

I've ordered one of those cheap OLED's 128x64 on eBay and will have some fun with it when it arrives. :twisted:
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

User avatar
odroid
Site Admin
Posts: 31852
Joined: Fri Feb 22, 2013 11:14 pm
languages_spoken: English
ODROIDs: ODROID
Has thanked: 89 times
Been thanked: 255 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by odroid » Thu Jul 11, 2019 5:11 pm

I'm not creative at all. :oops:

BTW, ODROID-N2 users also enjoyed various display modules with CoreELEC OS image.
https://discourse.coreelec.org/t/odroid ... river/4995
These users thanked the author odroid for the post:
puremind (Thu Jul 11, 2019 6:37 pm)

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Thu Jul 11, 2019 6:37 pm

Nice. I’ll check those libs once I get the part
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Sat Jul 13, 2019 4:49 am

Edited the script to sleep(10) at start otherwise weird characters coming up, looks nice at restart now.
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Sat Jul 13, 2019 5:06 am

In BIOS, set the frequency of I2C - 100 KHz, base frequency I2C 400 kHz in the chip PCA8574
because The LCD Module has a chip PCF8574T base frequency I2C 100 kHz
.
20x4LCDboard.jpg
20x4LCDboard.jpg (75.27 KiB) Viewed 1381 times
.
Although it works at 400KHz, but 100KHz is better.
.
pcf8574t.png
pcf8574t.png (82.62 KiB) Viewed 1435 times

User avatar
odroid
Site Admin
Posts: 31852
Joined: Fri Feb 22, 2013 11:14 pm
languages_spoken: English
ODROIDs: ODROID
Has thanked: 89 times
Been thanked: 255 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by odroid » Mon Jul 15, 2019 9:59 am

Yes! 100Khz must be more stable and enough to handle 20x4 characters.

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Sun Jul 21, 2019 8:07 pm

Strange you have actions:

Code: Select all

  tx_speed = (tx2 - tx1)/1000000.0
  rx_speed = (rx2 - rx1)/1000000.0
  ...
  # line 3 print (network speed)
  # bps -> mpbs (*8)
  ...tx_speed*8*1.048/10...
  ...rx_speed*8*1.048/10...
there is data already in bytes, not in bits - coefficient by 8 is not necessary.
strange some kind of coefficient = 8*1.048/10 = 0.8384
if in Bytes, when he should be = 1.048576, if in bits, then 8.388608
and 1KBytes = 1024 Bytes,
this implies 1MBytes = 1024 * 1024 = 1048576 Bytes
Respectively:

Code: Select all

tx_speed = (tx2 - tx1)/1048576.0
rx_speed = (rx2 - rx1)/1048576.0
and no further perversions are needed, simply:

Code: Select all

...str(round(rx_speed))...
...str(round(tx_speed))...
result in megabytes,
if you want in megabits, you can multiply by 8

Code: Select all

...str(round(rx_speed*8))...
...str(round(tx_speed*8))...
or

Code: Select all

tx_speed = (tx2 - tx1)/131072‬.0
rx_speed = (rx2 - rx1)/131072‬.0
but need to make a pause of less than 1 second, because Reading takes time, during which the traffic increases - sleep(0.96)

Code: Select all

  # get network speed for 1sec
  tx1 = get_bytes('tx')
  rx1 = get_bytes('rx')
  sleep(0.96)
  tx2 = get_bytes('tx')
  rx2 = get_bytes('rx')
Last edited by RomaT on Mon Jul 29, 2019 8:18 pm, edited 3 times in total.

fvolk
Posts: 285
Joined: Sun Jun 05, 2016 11:04 pm
languages_spoken: english
ODROIDs: C2, HC1, H2
Has thanked: 0
Been thanked: 10 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by fvolk » Mon Jul 22, 2019 4:11 am

Would connecting two 20x4 displays to the H2 also work?
I see there are two I2C buses, but only one +3.3V pin - putting both displays on the same +3.3V for VCC is ok?

domih
Posts: 108
Joined: Mon Feb 11, 2019 4:48 pm
languages_spoken: English, French
ODROIDs: UX4, HC2, N2, H2.
Has thanked: 34 times
Been thanked: 23 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by domih » Mon Jul 22, 2019 10:06 am

puremind wrote:
Thu Jul 11, 2019 6:23 am
Everybody,
I've made some changes here and there if anybody wants to try...

Managed, Celsius vs Fahrenheit with custom chars
Moving Bars for CPU & RAM. Two drives status with storage capacity.

Same as the first post, simply update the hello_world.py as instructed, then edit the config section on the script for full customisation, including 3/4 characters for the first drive to monitor.

Animation really nice on the percentage section.

Code: Select all

# -*- coding: utf-8 -*-
"""
Compiled, mashed and generally REmutilated 2019 by puremind
Made available under GNU GENERAL PUBLIC LICENSE

# added bits and pieces from various sources
# 2019-07-07, ver 0.1
# Add your own custom stuff with custom characters

"""
from time import sleep
import lcddriver as lcd


lcd = lcd.lcd()

lcd.display_string("                    ",1)
lcd.display_string("                    ",2)
lcd.display_string("                    ",3)
lcd.display_string("                    ",4)


sleep(1)

lcd.clear()



#set to False for Celsius, True for Fahrenheit
fahrenheit=False


#change here to sda sdb sdc etc
#disk1_to_check="sda"
#disk2_to_check="sdb"

disk1_to_check="nvme0n1p3"

#change here to partition full name for pct usage, check with "df -h"
path_disk1="/"


disk2_to_check="sda"
path_disk2="/mnt/data"

#Time to sleep between checks. Defaults 3
delay=3


#offsets 1 char to the right if the first disk starts with "nvme"
if disk1_to_check[0:4]!="nvme":
 nvmeoffset=0
else :
 nvmeoffset=1




# import modules
#import RPi_I2C_driver
import stat
import os
import psutil as ps
import math
from time import time

from datetime import datetime

sleep(5)



# -*- coding: utf-8 -*-
"""
Compiled, mashed and generally mutilated 2014-2015 by Denis Pleic
Made available under GNU GENERAL PUBLIC LICENSE

# Modified Python I2C library for Raspberry Pi
# as found on http://www.recantha.co.uk/blog/?p=4849
# Joined existing 'i2c_lib.py' and 'lcddriver.py' into a single library
# added bits and pieces from various sources
# By DenisFromHR (Denis Pleic)
# 2015-02-10, ver 0.1

"""
#
#
import smbus
from time import *

class i2c_device:
   def __init__(self, addr, port=1):
      self.addr = addr
      self.bus = smbus.SMBus(port)

# Write a single command
   def write_cmd(self, cmd):
      self.bus.write_byte(self.addr, cmd)
      sleep(0.0001)

# Write a command and argument
   def write_cmd_arg(self, cmd, data):
      self.bus.write_byte_data(self.addr, cmd, data)
      sleep(0.0001)

# Write a block of data
   def write_block_data(self, cmd, data):
      self.bus.write_block_data(self.addr, cmd, data)
      sleep(0.0001)

# Read a single byte
   def read(self):
      return self.bus.read_byte(self.addr)

# Read
   def read_data(self, cmd):
      return self.bus.read_byte_data(self.addr, cmd)

# Read a block of data
   def read_block_data(self, cmd):
      return self.bus.read_block_data(self.addr, cmd)



# LCD Address
#ADDRESS = 0x3F
ADDRESS = 0x27

# I2C bus
BUS = 1

# commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80

# flags for display entry mode
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00

# flags for display on/off control
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00

# flags for display/cursor shift
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00

# flags for function set
LCD_8BITMODE = 0x10
LCD_4BITMODE = 0x00
LCD_2LINE = 0x08
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00

# flags for backlight control
LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00

En = 0b00000100 # Enable bit
Rw = 0b00000010 # Read/Write bit
Rs = 0b00000001 # Register select bit

class lcd:
   #initializes objects and lcd
   def __init__(self):
      self.lcd_device = i2c_device(ADDRESS, BUS)

      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x02)

      self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
      self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
      sleep(0.15)


   # clocks EN to latch command
   def lcd_strobe(self, data):
      self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
      sleep(.0005)
      self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
      sleep(.0001)

   def lcd_write_four_bits(self, data):
      self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
      self.lcd_strobe(data)

   # write a command to lcd
   def lcd_write(self, cmd, mode=0):
      self.lcd_write_four_bits(mode | (cmd & 0xF0))
      self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))

   # write a character to lcd (or character rom) 0x09: backlight | RS=DR<
   # works!
   def lcd_write_char(self, charvalue, mode=1):
      self.lcd_write_four_bits(mode | (charvalue & 0xF0))
      self.lcd_write_four_bits(mode | ((charvalue << 4) & 0xF0))
  

   # put string function
   def lcd_display_string(self, string, line):
      if line == 1:
         self.lcd_write(0x80)
      if line == 2:
         self.lcd_write(0xC0)
      if line == 3:
         self.lcd_write(0x94)
      if line == 4:
         self.lcd_write(0xD4)

      for char in string:
         self.lcd_write(ord(char), Rs)

   # clear lcd and set to home
   def lcd_clear(self):
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_RETURNHOME)

   # define backlight on/off (lcd.backlight(1); off= lcd.backlight(0)
   def backlight(self, state): # for state, 1 = on, 0 = off
      if state == 1:
         self.lcd_device.write_cmd(LCD_BACKLIGHT)
      elif state == 0:
         self.lcd_device.write_cmd(LCD_NOBACKLIGHT)

   # add custom characters (0 - 7)
   def lcd_load_custom_chars(self, fontdata):
      self.lcd_write(0x40);
      for char in fontdata:
         for line in char:
            self.lcd_write_char(line)         
         
         
   # define precise positioning (addition from the forum)
   def lcd_display_string_pos(self, string, line, pos):
    if line == 1:
      pos_new = pos
    elif line == 2:
      pos_new = 0x40 + pos
    elif line == 3:
      pos_new = 0x14 + pos
    elif line == 4:
      pos_new = 0x54 + pos

    self.lcd_write(0x80 + pos_new)

    for char in string:
      self.lcd_write(ord(char), Rs)
      
      







#check if disk exists function
def disk_exists(path):
     try:
             return stat.S_ISBLK(os.stat(path).st_mode)
     except:
             return False


# network status get function
def get_bytes(t, iface='enp2s0'):
    with open('/sys/class/net/' + iface + '/statistics/' + t + '_bytes', 'r') as f:
        data = f.read();
        return int(data)






sleep(3)

# load lcd
lcd = lcd()

sleep(1)

# 5 by 8 custom char creation
#https://www.quinapalus.com/hd44780udg.html

#More info here with full char set
#http://www.electronic-engineering.ch/microchip/datasheets/lcd/charset.gif

# Define some static characters

fontdata = [
         # Char 0 - Celsius
        [ 0x8,0x14,0x8,0x3,0x4,0x4,0x4,0x3],
 		# Char 1 1/5 block 
 		[ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10],
 		# Char 2 2/5 block 
 		[ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18],
 		# Char 3 3/5 block 
 		[ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c],
 		# Char 4 4/5 block 
 		[ 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e],
        # Char 5 - Arrow up
        [ 0x0,0x4,0xe,0x1f,0x4,0x4,0x4,0x0],
        # Char 6 - Arrow down
        [ 0x0,0x4,0x4,0x4,0x1f,0xe,0x4,0x0],
        # Char 7 ZZ
        [ 0x1e,0x4,0x8,0x1e,0xf,0x2,0x4,0xf]

]


lcd.lcd_load_custom_chars(fontdata)

sleep(1)

lcd.lcd_display_string("                    ",1)
lcd.lcd_display_string(str.center(str(os.popen('hostname').readline())[:-1],20,' '),2)
lcd.lcd_display_string("      STARTING      ",3)
lcd.lcd_display_string("                    ",4)

sleep(2)

lcd.lcd_display_string(disk1_to_check[0:3+nvmeoffset]+" ? ??% "+disk2_to_check[0:3]+" ? ??%",1)

#Set 0 as speed for network first cycle
#Fixed elements
#TX / RX Arrows

lcd.lcd_display_string("CPU ??% "+chr(126)+"      "+chr(127)+" ??"+chr(6),2)
lcd.lcd_display_string("RAM ??% "+chr(126)+"      "+chr(127)+" ??"+chr(5),3)

#Celsius
if fahrenheit!=True:
 lcd.lcd_display_string_pos("??"+chr(0),4,17)
else:
 lcd.lcd_display_string_pos("??F",4,17)

sleep(1)
steps3 = [1, 2, 3]
steps = [0, 1, 2, 3, 4, 5]

while True:


  # check if disk  exists
  
  
  # get network speed for delay sec
  tx1 = get_bytes('tx')
  rx1 = get_bytes('rx')

  
  if disk_exists("/dev/"+disk1_to_check)!=True:
    hddstr1=chr(6)
  else :

   # read hdd state from hdparm
   hddstate1=str(os.popen('hdparm -C /dev/'+disk1_to_check+' | grep state | cut -f 2 -d :').readline())[2:][:-1]

   if hddstate1=="standby":
    hddstr1=chr(7)
   else :
    hddstr1=chr(5)


  

  if disk_exists("/dev/"+disk2_to_check)!=True:
    hddstr2=chr(6)
  else :

   # read hdd state from hdparm
   hddstate2=str(os.popen('hdparm -C /dev/'+disk2_to_check+' | grep state | cut -f 2 -d :').readline())[2:][:-1]

   if hddstate2=="standby":
    hddstr2=chr(7)
   else :
    hddstr2=chr(5)



 # line 1 print (hddstate, friendly name)

  #lcd.lcd_display_string(disk1_to_check+":"+hddstr1.rjust(5)+"  "+disk2_to_check+":"+hddstr2.rjust(5), 1)
  lcd.lcd_display_string_pos(hddstr1, 1,4+nvmeoffset)  
  lcd.lcd_display_string_pos(hddstr2, 1,15)  


  obj_Disk1 = ps.disk_usage(path_disk1)
  lcd.lcd_display_string_pos(str(round(obj_Disk1.percent)), 1,6+nvmeoffset)  

  obj_Disk2 = ps.disk_usage(path_disk2)
  lcd.lcd_display_string_pos(str(round(obj_Disk2.percent)), 1,17)  


  # line 2 print (cpu, memory)
  # use psutil to get CPU usage and Memory Usage
  # line 3 print (network speed)
  # bps -> mpbs (*8)


 

  for z in range(2) :
   sleep(delay/2)
   cpupct=ps.cpu_percent()
   rampct=ps.virtual_memory()[2]
  

   lcd.lcd_display_string_pos(str(math.floor(cpupct)).rjust(3),2,3)
   lcd.lcd_display_string_pos(str(math.floor(rampct)).rjust(3),3,3)

#  for y in range(101):
#    sleep(0.1)
#    cpupct=y
#    lcd.lcd_display_string_pos(str(y),3,10)

   for x in steps:
     if (math.floor(cpupct+1)>x*(100/6)) and (math.floor(cpupct)>(x+1)*(100/6)) or cpupct>98:
      lcd.lcd_display_string_pos(chr(255),2,9+x)
     else:
      if ((math.floor(cpupct)>x*(100/6)+1) and (math.floor(cpupct)<(x+1)*(100/6)) and cpupct>2):
       lcd.lcd_display_string_pos(chr(min(4,math.floor((cpupct-(x*100/6))/((100/6/4))+1))),2,9+x)
      else:  
       lcd.lcd_display_string_pos(" ",2,9+x)

     if (math.floor(rampct+1)>x*(100/6)) and (math.floor(rampct)>(x+1)*(100/6)) or rampct>98:
      lcd.lcd_display_string_pos(chr(255),3,9+x)
     else:
      if (math.floor(rampct)>x*(100/6)+1) and (math.floor(rampct)<(x+1)*(100/6)) :
       lcd.lcd_display_string_pos(chr(min(4,math.floor((rampct-(x*100/6))/((100/6/4))+1))),3,9+x)
      else:  
       lcd.lcd_display_string_pos(" ",3,9+x)




  # line 4 print (cputemp, date, time)
  # use lm-sensors to get cpu temp / get time data from python internal method
  dateString = datetime.now().strftime('%d-%b')
  timeString = datetime.now().strftime('%H:%M:%S')

#  lcd.lcd_write(0xC0)
#  lcd.lcd_write_char(1)	
  

#  lcd.lcd_display_string_pos(chr(0),4,0)
  
#  lcd.lcd_display_string_pos(os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()[:-1].rjust(3), 4,16)
  if fahrenheit!=True:
   lcd.lcd_display_string_pos(dateString+" "+timeString+" "+os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()[:-1].rjust(3), 4,0)
  else:
   lcd.lcd_display_string_pos(dateString+" "+timeString+" "+str(round(int(os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()[:-1])*9/5+32,0)).rjust(3)[0:3], 4,0)


 
  tx2 = get_bytes('tx')
  rx2 = get_bytes('rx')
  tx_speed = (tx2 - tx1)/1000000.0/delay
  rx_speed = (rx2 - rx1)/1000000.0/delay
  
  lcd.lcd_display_string_pos(str(round(rx_speed*8*1.048/10)).rjust(3),2,16)
  lcd.lcd_display_string_pos(str(round(tx_speed*8*1.048/10)).rjust(3),3,16)



  # line 4 print (cputemp, uptime)
  # use /proc/uptime and python divmod method
  #uptime=os.popen('cat /proc/uptime | cut -d " " -f1').read()
  #uptimeDay=divmod(float(uptime),86400)
  #uptimeHour=divmod(uptimeDay[1],3600)
  #uptimeMin=divmod(uptimeHour[1],60)
  #uptimeString=str(int(uptimeDay[0]))+"D:"+str(int(uptimeHour[0]))+"H:"+str(int(uptimeMin[0]))+"M"
  #lcd.lcd_display_string("TEMP:"+os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()[:-1]+"C "+uptimeString.zfill(3), 4)


  # sleep(1)



Samples:

Untitled.jpg

2019-07-10 at 22.16.30.jpg
:o :mrgreen: :P

Oh my, very nice, you are way ahead with the LC 20 x 4. I had just started to do some research on documents. In case it helps, I noticed these:

- https://github.com/johnrickman/LiquidCrystal_I2C
- https://en.wikipedia.org/wiki/Hitachi_H ... controller
- https://www.sparkfun.com/datasheets/LCD/HD44780.pdf

Best,

Dominique

User avatar
odroid
Site Admin
Posts: 31852
Joined: Fri Feb 22, 2013 11:14 pm
languages_spoken: English
ODROIDs: ODROID
Has thanked: 89 times
Been thanked: 255 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by odroid » Mon Jul 22, 2019 7:19 pm

fvolk wrote:
Mon Jul 22, 2019 4:11 am
Would connecting two 20x4 displays to the H2 also work?
I see there are two I2C buses, but only one +3.3V pin - putting both displays on the same +3.3V for VCC is ok?
Yes, it must be okay.

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Mon Jul 29, 2019 7:52 pm

I added myself mapping speed hard drives, on the principle of the script from ODROID CloudShell (iostat -y -m 1 1)
.
P7201036s.jpg
P7201036s.jpg (130.7 KiB) Viewed 951 times

User avatar
odroid
Site Admin
Posts: 31852
Joined: Fri Feb 22, 2013 11:14 pm
languages_spoken: English
ODROIDs: ODROID
Has thanked: 89 times
Been thanked: 255 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by odroid » Tue Jul 30, 2019 9:39 am

Nice LCD mounting in the transparent cases!
I like the Russian characters on the LCD since I'm still loving to play classic Tetris games. :D

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Thu Aug 01, 2019 3:02 pm

Today was a kernel update Ubuntu 18.04.2 LTS amd64 from Kernel 4.18.0-25 to Kernel 5.0.0-23

Code: Select all

Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 5.0.0-23-generic x86_64)
at the same time, the "sensors" utility data has changed

Code: Select all

# sensors
coretemp-isa-0000
Adapter: ISA adapter
Package id 0:  +43.0°C  (high = +105.0°C, crit = +105.0°C)
Core 0:        +40.0°C  (high = +105.0°C, crit = +105.0°C)
Core 1:        +40.0°C  (high = +105.0°C, crit = +105.0°C)
Core 2:        +41.0°C  (high = +105.0°C, crit = +105.0°C)
Core 3:        +41.0°C  (high = +105.0°C, crit = +105.0°C)
you must change the line
lcd.display_string("T"+os.popen('sensors | grep "temp1:" | cut -d+ -f2 | cut -c1-2').read()[:-1]+"C "+dateString+" "+timeString, 4)
on
lcd.display_string("T"+os.popen('sensors | grep "id 0:" | cut -d+ -f2 | cut -c1-2').read()[:-1]+"C "+dateString+" "+timeString, 4)
"temp1:" replace with "id 0:"

User avatar
odroid
Site Admin
Posts: 31852
Joined: Fri Feb 22, 2013 11:14 pm
languages_spoken: English
ODROIDs: ODROID
Has thanked: 89 times
Been thanked: 255 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by odroid » Thu Aug 01, 2019 4:03 pm

Thank you for sharing the updates.

Was there any point release 18.04.3?
It seems to be today or tomorrow.
https://wiki.ubuntu.com/BionicBeaver/ReleaseSchedule

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Thu Aug 01, 2019 4:10 pm

automatic upgrade immediately switched to 5 Kernel, maybe this is preparation for 18.04.3 :)

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Thu Aug 01, 2019 7:05 pm

odroid
There is another nuance, when you reboot the OS, the I2C bus jumps from 2/3 to 5/6, during subsequent reboot back.
changes not as often how is the sata controller lost, but it happens, regardless of which kernel (4.18.0-25 or 5.0.0-23).
(when you check the BIOS, pay attention)

User avatar
odroid
Site Admin
Posts: 31852
Joined: Fri Feb 22, 2013 11:14 pm
languages_spoken: English
ODROIDs: ODROID
Has thanked: 89 times
Been thanked: 255 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by odroid » Fri Aug 02, 2019 8:48 am

We will try to reproduce the issue next week with the PCIe-SATA lost problem together.

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Wed Aug 07, 2019 9:22 pm

Updated
1. Coeficients
2. Wait 0.96 vs 1.00
3. Fix for Kernel 5.x
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Wed Aug 07, 2019 9:31 pm

puremind wrote:
Wed Aug 07, 2019 9:22 pm
Updated
Do you sometimes, when you reboot, the I2C bus number is not jump from 2/3 to 5/6, during subsequent reboot back?

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Wed Aug 07, 2019 11:58 pm

Haven't checked. Will go back to this again when I get the OLED display. :)
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Sun Aug 11, 2019 1:45 pm

shows the percentage of virtual memory, not RAM
At the same time, read the occupied RAM from the file /proc/meminfo
options

Code: Select all

MemTotal:       32755428 kB
MemFree:          280260 kB
membusy=(MemTotal-MemFree)/(MemTotal/100)= 99.14%
or
memfree=MemFree/(MemTotal/100)=0.86%
check calculation can be found in the utility nmon
.
ram.png
ram.png (4.53 KiB) Viewed 491 times

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Thu Aug 15, 2019 3:01 am

Got my new OLED display ... 128x64 single color. Couldn't stand the back-lit of the LED.
It works with ODROID H2, plugged pins on the same places as the 20x4 LED
(https://wiki.odroid.com/odroid-h2/appli ... c_20x4_lcd)

Despite the sellers pictures where all the images display the pins SOLDERED, this unit arrives unsoldered...
if you want the pins soldered you'd need to check around other sellers :
https://www.ebay.com/itm/382918678270
Not bad for ~4$ USD
I had to borrow a tool to solder myself, with the added danger to break the unit.

These are the drivers it made it work for me under Ubuntu:
https://luma-oled.readthedocs.io/en/latest/

And... I had to call the python scripts like this (without the added parameters in bold it doesn't work):
python3 examples/sys_info.py --i2c-port 2 --display sh1106

There's a ton of nice scripts for this OLED, all work well, images, videos, graphics, animations very very cool :
https://github.com/rm-hull/luma.examples

See proof of sys_info.py working:
Image-1.jpg
Image-1.jpg (125.24 KiB) Viewed 360 times
I might adapt my other script to work with this display, will post here.
These users thanked the author puremind for the post:
RomaT (Fri Aug 16, 2019 2:11 pm)
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

RomaT
Posts: 218
Joined: Thu Oct 23, 2014 4:48 pm
languages_spoken: Russian
ODROIDs: -H2 rev.B, -XU3, -XU4, -C1, -C2, -W, -VU, CloudShell
Location: Perm, Russia
Has thanked: 6 times
Been thanked: 34 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by RomaT » Thu Aug 15, 2019 9:06 am

It is very cool !
I would like to see examples adapted for H2.

User avatar
spitefulmonkey
Posts: 26
Joined: Sun Nov 10, 2013 11:55 am
languages_spoken: english
Has thanked: 0
Been thanked: 0
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by spitefulmonkey » Sat Aug 17, 2019 4:25 pm

I really like this. After I had originally read this post I hit up amazon and ordered a lil 1.3 oled to play with too. Seems to use the luma drivers as well. It arrives later today.....I love Amazon haha. Looking forward to playing with all this. I also receive my gpio expansion board today too!

I'll post back what I can come up with myself. I might add a couple buttons for screen navigation.

Mine was $8.99 due to amazon, but quick shipping. https://www.amazon.com/gp/product/B01MR ... UTF8&psc=1

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Sat Aug 17, 2019 5:14 pm

mmm. for that price you could've got a blue/yellow and cheaper ;)
https://www.amazon.com/UCTRONICS-SSD130 ... VJ83AZ8WH7

But well, they are so cheap that it doesn't matter. Let's see what scripts we can come up with!
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

puremind
Posts: 45
Joined: Wed Nov 21, 2018 2:27 am
languages_spoken: english
ODROIDs: Odroid H2 Rev B
Has thanked: 2 times
Been thanked: 8 times
Contact:

Re: i2c 20 pin alternative python display for 2 drives

Unread post by puremind » Sun Aug 18, 2019 12:19 am

Ok, here it goes my first python script for the OLED 128x64 display on the H2.

This uses luma drivers. To get them installed, follow their instructions:
https://luma-oled.readthedocs.io/en/latest/index.html

I've put my new updated script into the examples/h2_sys_info.py folder into the luma examples installation:
https://github.com/rm-hull/luma.example ... README.rst

Custom characters for drive up ("V"), sleep ("Z") or not present ("X") was a bit more complicated for a single saturday, so I'd leave where it is for now.

You've also have to put the monaco.ttf font I've used into the examples/fonts folder (or the script will not work)!
It's downloadable here:
https://github.com/mps/fonts/blob/master/MONACO.TTF
(remember to rename the file to lowercase monaco.ttf)

It's executed as follows (if you have a different display type rather than sh1106, you know what to do below):

root@h2:~/luma.examples# python3 examples/h2_sys_info.py --i2c-port 2 --display sh1106
Version: luma.oled 3.3.0 (luma.core 1.12.0)
Display: sh1106
Interface: i2c
Dimensions: 128 x 64
------------------------------------------------------------


The code of the new example script @ examples/h2_sys_info.py as follows
(it's a quick adaptation of my former script for the 20x4 LED display to OLED)

Code: Select all

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2014-18 Richard Hull and contributors
# See LICENSE.rst for details.
# PYTHON_ARGCOMPLETE_OK

"""
Display basic system information.

Needs psutil (+ dependencies) installed::

  $ sudo apt-get install python-dev
  $ sudo -H pip install psutil
"""


###Specify your devices here

interface_to_check="enp2s0"

#change here to sda sdb sdc etc
#disk1_to_check="sda"
#disk2_to_check="sdb"
disk1_to_check="nvme0n1p3"
#change here to partition full name for pct usage, check with "df -h"
path_disk1="/"

disk2_to_check="sda"
path_disk2="/mnt/data"


###NO NEED TO TOUCH BELOW











import os
import sys
import time
import stat
import psutil as ps
from datetime import datetime

if os.name != 'posix':
    sys.exit('{} platform not supported'.format(os.name))

from demo_opts import get_device
from luma.core.render import canvas
from PIL import ImageFont

try:
    import psutil
except ImportError:
    print("The psutil library was not found. Run 'sudo -H pip install psutil' to install it.")
    sys.exit()






#offsets 1 char to the right if the first disk starts with "nvme"
if disk1_to_check[0:4]!="nvme":
 nvmeoffset=0
else :
 nvmeoffset=1




#check if disk exists function
def disk_exists(path):
     try:
             return stat.S_ISBLK(os.stat(path).st_mode)
     except:
             return False


# network status get function
def get_bytes(t, iface=interface_to_check):
    with open('/sys/class/net/' + iface + '/statistics/' + t + '_bytes', 'r') as f:
        data = f.read();
        return int(data)



# TODO: custom font bitmaps for up/down arrows
# TODO: Load histogram



def stats(device):

  
  # get network speed for delay sec
  delay=0.96
  tx1 = get_bytes('tx')
  rx1 = get_bytes('rx')
  time.sleep(delay+0.04)

  if disk_exists("/dev/"+disk1_to_check)!=True:
    hddstr1="X";
  else :

   # read hdd state from hdparm
   hddstate1=str(os.popen('hdparm -C /dev/'+disk1_to_check+' | grep state | cut -f 2 -d :').readline())[2:][:-1]

   if hddstate1=="standby":
    hddstr1="Z";
   else :
    hddstr1="V";

   obj_Disk1 = ps.disk_usage(path_disk1)
 
  

   if disk_exists("/dev/"+disk2_to_check)!=True:
    hddstr2="X";
   else :

   # read hdd state from hdparm
    hddstate2=str(os.popen('hdparm -C /dev/'+disk2_to_check+' | grep state | cut -f 2 -d :').readline())[2:][:-1]

    if hddstate2=="standby":
     hddstr2="Z";
    else :
     hddstr2="V";

    obj_Disk2 = ps.disk_usage(path_disk2)
 


   tx2 = get_bytes('tx')
   rx2 = get_bytes('rx')
   tx_speed = (tx2 - tx1)/1048576.0/delay
   rx_speed = (rx2 - rx1)/1048576.0/delay

   cpupct=round(ps.cpu_percent())
   mempct=round(ps.virtual_memory()[2])
   
   dateString = datetime.now().strftime('%d-%b')
   timeString = datetime.now().strftime('%H:%M:%S')

   font3_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
                                'fonts', 'monaco.ttf'))
   font3 = ImageFont.truetype(font3_path, 10)


   with canvas(device) as draw:
        if hddstr1!="X":
         draw.text((0, 0), disk1_to_check[0:3+nvmeoffset]+" "+hddstr1+" "+str(round(obj_Disk1.percent))+"%", font=font3, fill="white")
        else:
         draw.text((0, 0), disk1_to_check[0:3+nvmeoffset]+" None", font=font3, fill="white")
        
        if hddstr2!="X":
         draw.text((66, 0), disk2_to_check[0:3]+" "+hddstr2+" "+str(round(obj_Disk2.percent))+"%", font=font3, fill="white")
        else:
         draw.text((66, 0), disk2_to_check[0:3]+" None", font=font3, fill="white")

        draw.text((0, 13), "CPU:"+str(cpupct).rjust(3)+"%", font=font3, fill="white")
		
        draw.line((50, 14, 126, 14), fill="white")
        draw.line((50, 15, 50, 22), fill="white")
        draw.line((50, 23, 126, 23), fill="white")
        draw.line((126, 15, 126, 22), fill="white")


        draw.text((0, 26), "MEM:"+str(mempct).rjust(3)+"%", font=font3, fill="white")
 
        draw.line((50, 27, 126, 27), fill="white")
        draw.line((50, 28, 50, 35), fill="white")
        draw.line((50, 36, 126, 36), fill="white")
        draw.line((126, 28, 126, 35), fill="white")

        for x in range(51,126):
         if (x<=(cpupct*(125-51)/100)+51):
          draw.line((x, 15, x, 22), fill="white")
         else:
          draw.line((x, 15, x, 22), fill="black")
         if (x<=(mempct*(125-51)/100)+51):
          draw.line((x, 28, x, 35), fill="white")
         else:
          draw.line((x, 28, x, 35), fill="black")

      
       
       
        draw.text(( 0, 39), "DN:"+str(round(rx_speed)).rjust(3)+"MB/s", font=font3, fill="white")
        draw.text((68, 39), "UP:"+str(round(tx_speed)).rjust(3)+"MB/s", font=font3, fill="white")

        draw.text(( 0, 52), dateString+" "+timeString, font=font3, fill="white")
        draw.text(( 98, 52), os.popen('sensors | grep "id 0:" | cut -d+ -f2 | cut -c1-2').read()[:-1].rjust(3)+"ºC", font=font3, fill="white")
  


def main():
    while True: 
        stats(device)
#        time.sleep(1)


if __name__ == "__main__":
    try:
        device = get_device()
        main()
    except KeyboardInterrupt:
        pass



Pictures:
IMG_4061.jpg
IMG_4061.jpg (112.39 KiB) Viewed 224 times
IMG_4062.jpg
IMG_4062.jpg (114.04 KiB) Viewed 224 times
These users thanked the author puremind for the post (total 3):
RomaT (Sun Aug 18, 2019 10:30 am) • odroid (Mon Aug 19, 2019 11:39 am) • domih (Thu Aug 22, 2019 10:45 am)
Odroid H2 Rev B, 16GB Ripjaws, MP510 Corsair 512GB Nvme

Post Reply

Return to “Hardware and peripherals”

Who is online

Users browsing this forum: No registered users and 1 guest