The Move to RPi5 – Upgrading the RPi4

Elsewhere I’ve written about moving from Raspberry Pi 4 and legacy 32-bit Bullseye (Pi OS) to Raspberry Pi 5 and 64-bit Bookworm (Pi OS). So I thought, having backed up my RPi4 and 5, I’d take the RPi4 and do a from-scratch installation of Bullseye on the latter.

Armed with my PC and RPi4 (Ethernet connection as usual for me), Balena Etcher on the PC and a handy SD (I typically use 32GB SDs), I selected the LITE version of Pi OS Bookworm 64-bit and flashed that onto the SD. I was asked by Balena Etcher before flashing started if I’d like to customise the install – so I called it RPi4 and put in user pi with my usual password. That’s about it.

On removing the flashed image from the PC, I put it into the RPi4 and applied power. Advanced IP Scanner (PC) told me the address of the board on my network, so using Mobaxterm on the PC, I selected a Mobatek SSH session for device 192.168.1.232 (that’s the address that dhcp gave it), remembering to select in Mobaxterm terminal setup, the 256 colour terminal option for later.

In the SSH session as user pi, I enabled root access – and while still user pi, I fixed the IP address (IPV4 configuration) in the RPi4 as I did here on RPi5. – NOT using the mouse – remember that. i.e.

# enter a password for root user
sudo passwd root
# change these 2 lines in /etc/ssh/sshd_config to allow root login via ssh
sudo nano /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
sudo nmtui

After setting the fixed IP address I wanted (192.168.1.19) and default gateway and DNS (my router at 192.168.1.1) and finally disabling IPV6, I hit OK and rebooted the RPi4 which no longer appeared at the original address, so I created a new Mobaxterm session at 192.168.1.19

I stuck with user pi at this point – as that’s what I’ve always used on the RPi4 and previous (RPi3, RPi2). I found a new script on a blog Ultimate Python Script – and selected Python 3.8 specifically as last time I installed Amazon AWS Polly (days ago) it seemed to want Python 3.8

wget -qO - https://raw.githubusercontent.com/tvdsluijs/sh-python-installer/main/python.sh | sudo bash -s 3.8.0

But no – it said “you are trying to install an older version than your current version – exiting this script” – ok, I’ve documented it now, so, in for a penny…

Next, for the RPi4, a variation of my RPi5 blog entry , remembering I’m still pi

## jq will be needed for Antonio's Docker stuff, mc is for the mcedit editor
sudo apt install jq
sudo apt install mc

No interaction above but plenty of waiting.

As before, I went into mc – options – appearance and selected julia256 theme – just because I hate the default theme – I don’t find it easy to read. I’ve explained elsewhere that I find mcedit to be way easier to use than nano which comes with the pi – and I’m starting to use mc for copying files, folders etc. It’s almost like being civilised. See this Midnight Commander reference.

As I’m using Bookworm I went for the revised rpi-clone (not BillW’s original github respository which is no longer supported)- for me an utterly essential tool which I’ve discussed in the RPi5 entry.

curl https://raw.githubusercontent.com/geerlingguy/rpi-clone/master/install | sudo bash

Seconds later – done. rpi-clone available but then I was going to have to remember the commands – so immediately I went to install my usual aliases in /etc/bash.bashrc – this time using sudo mcedit – so:

sudo mcedit /etc/bash.bashrc
##once in the editor I add this lot after commenting out the original PS1 section in the file
##
##
# set a fancy prompt
# original commented out along with the conditional code
# PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
PS1="\[\033[38;5;39m\]\u\[$(tput sgr0)\]\[\033[38;5;15m\]@\[$(tput sgr0)\]\[\033[38;5;222m\]\h\[$(tput sgr0)\]\[\033[38;5;15m\]:\[$(tput sgr0)\]\[\033[38;5;83m\]\W\[$(tput sgr0)\]\[\033[38;5;15m\]:\[$(tput sgr0)\]\[\033[38;5;69m\]\A\[$(tput sgr0)\]\[\033[38;5;15m\][\[$(tput sgr0)\]\[\033[38;5;174m\]\$?\[$(tput sgr0)\]\[\033[38;5;15m\]]> \[$(tput sgr0)\]"

BLACK='\033[0;30m'
RED='\033[0;31m'
GREEN='\033[0;32m'
BROWN='\033[0;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
LIGHTGRAY='\033[0;37m'
DARKGRAY='\033[1;30m'
LIGHTRED='\033[1;31m'
LIGHTGREEN='\033[1;32m'
YELLOW='\033[1;33m'
LIGHTBLUE='\033[1;34m'
LIGHTPURPLE='\033[1;35m'
LIGHTCYAN='\033[1;36m'
WHITE='\033[1;37m'
NC='\033[0m'

alias space='df -h|grep -v udev|grep -v tmpfs|grep -v run'
alias stop='sudo shutdown now'
alias boot='sudo reboot'
alias partitions='cat /proc/partitions'
alias parts='sudo fdisk -l /dev/mmc* /dev/sd*'
alias cloned="sudo printf 'Last cloned on ' && sudo tune2fs -l /dev/sda2|grep -i write|grep -iv life|cut -d: -f 2-|xargs"
#alias cls='python /home/pi/cls.py'

# rpi-clone aliases to make life easy
#optional hostnames in clone functions below
# clone assumes running the RPi on SD and clones to an already used SSD
clone () { 
printf "${LIGHTBLUE}Creating a quick clone on SDA${NC}\n"
touch /home/pi/clone-date
bashCmd=(sudo rpi-clone -U sda)
if [ -n "$1" ];  then
bashCmd+=(-s "$1")
fi
"${bashCmd[@]}"
}

# Same as clone but assumes a brand new clean (or used for other purposes) SSD  - it means clean clone
cclone () { 
printf "${LIGHTRED}Creating a full clone on SDA${NC}\n"
touch /home/pi/clone-date
bashCmd=(sudo rpi-clone -f -U sda)
if [ -n "$1" ];  then
bashCmd+=(-s "$1")
fi
"${bashCmd[@]}"
}

# Same as clone but assuming a ssecond SSD in SDB - I used to have an adaptor for 2 USB-mounted SSDs on the RPi
cloneb () { 
printf "${LIGHTBLUE}Creating a quick clone on SDB${NC}\n"
touch /home/pi/clone-date
bashCmd=(sudo rpi-clone -U sdb)
if [ -n "$1" ];  then
bashCmd+=(-s "$1")
fi
"${bashCmd[@]}"
}

# clonem is used assuming you are running on SSD and will create a clone on SD
clonem () { 
printf "${LIGHTBLUE}Creating a quick clone on MMCBLK0${NC}\n"
touch /home/pi/clone-date
bashCmd=(sudo rpi-clone -U mmcblk0)
if [ -n "$1" ];  then
bashCmd+=(-s "$1")
fi
"${bashCmd[@]}"
}

ccloneb () { 
printf "${LIGHTRED}Creating a full clone on SDB${NC}\n"
touch /home/pi/clone-date
bashCmd=(sudo rpi-clone -f -U sdb)
if [ -n "$1" ];  then
bashCmd+=(-s "$1")
fi
"${bashCmd[@]}"
}

cclonem () { 
printf "${LIGHTRED}Creating a full clone on MMCBLK0${NC}\n"
touch /home/pi/clone-date
bashCmd=(sudo rpi-clone -f -U mmcblk0)
if [ -n "$1" ];  then
bashCmd+=(-s "$1")
fi
"${bashCmd[@]}"
}

cclonec () { 
printf "${LIGHTRED}Creating a full clone on SDC${NC}\n"
touch /home/pi/clone-date
bashCmd=(sudo rpi-clone -f -U sdc)
if [ -n "$1" ];  then
bashCmd+=(-s "$1")
fi
"${bashCmd[@]}"
}

clonec () { 
printf "${LIGHTBLUE}Creating a quick clone on SDC${NC}\n"
touch /home/pi/clone-date
bashCmd=(sudo rpi-clone -U sdc)
if [ -n "$1" ];  then
bashCmd+=(-s "$1")
fi
"${bashCmd[@]}"
}

update () {
printf "${LIGHTGREEN}Getting upgrades...${NC}"
sudo apt update
sudo apt upgrade
}

created () {
printf "${LIGHTGREEN}This setup was created at ${YELLOW}"
bashCmd=(date -r /home/pi/clone-date +"%H:%M on %-d/%m/%Y")
"${bashCmd[@]}"
printf "${NC}"
}

After pasting that into the end of the existing /etc/bash.bashrc file, I hit F2 to save – then F10 to exit the editor – it doesn’t get any easier. I’m sure someone will tell me the command to reset bash.bashrc without a reboot but I find it as easy to use sudo reboot (after this it’ll just be boot thanks to my aliases).

Time for a update and upgrade – bang goes another 141MB of storage.

sudo apt update && sudo apt upgrade

And now I made a clone to the used SSD I just happen to have plugged into one of the RPi4 USB3 connectors. Always great being able to know that if the next step messes up,, I can get back to this place in no time at all (there’s a reason I’m installing in the order you see here – editor – rpi-clone, latest updates all in place before trying anything major). If I’d only had a brand new SSD handy I’d have gone for the much slower of my alias commands for rpi-clone – cclone for clean clone. Instead, I used clone

clone

As the SSD had none of this installation – it was previously used for my full RPI backup using the previous operating system – hence….. total time for the clone above… around 6 minutes. The next clone will take much less than that – and in both cases – no interaction – a great time for a bathroom break or coffee.

Now I was ready to install my cls file (cls.py but the .py suffix is not needed here) in /usr/local/bin folder – I also need to give all users full permissions for that file including execute permission…

Here’s the content of the file /usr/local/bin/cls which I put into that /usr/local/bin folder with mcedit… and then give everyone full permissions… to the file…

#!/usr/bin/python3
import time
import os
import psutil
import platform
import socket
from datetime import datetime

import subprocess
 
byteunits = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')
def filesizeformat(value):
    exponent = int(log(value, 1024))
    return "%.1f %s" % (float(value) / pow(1024, exponent), byteunits[exponent])
 
def bytes2human(n):
    """
    >>> bytes2human(10000)
    '9K'
    >>> bytes2human(100001221)
    '95M'
    """
    symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
    prefix = {}
    for i, s in enumerate(symbols):
        prefix[s] = 1 << (i + 1) * 10
    for s in reversed(symbols):
        if n >= prefix[s]:
            value = int(float(n) / prefix[s])
            return '%s%s' % (value, s)
    return "%sB" % n
  
def cpu_usage():
    # load average, uptime
    av1, av2, av3 = os.getloadavg()
    return "%.1f %.1f %.1f" \
        % (av1, av2, av3)
 
def cpu_temperature():
    tempC = ((int(open('/sys/class/thermal/thermal_zone0/temp').read()) / 1000))
    return "%sc" \
        % (str(round(tempC,1)))
 
  
def disk_usage(dir):
    usage = psutil.disk_usage(dir)
    return " %s/%s" \
        % (bytes2human(usage.total-usage.used), bytes2human(usage.total))
  
def network(iface):
    stat = psutil.net_io_counters(pernic=True)[iface]
    return "%s: Tx%s, Rx%s" % \
           (iface, bytes2human(stat.bytes_sent), bytes2human(stat.bytes_recv))
  
    
bold = '\033[1m'
normal = '\033[0m'
red='\033[91m'
green='\033[92m'
blue='\033[94m'
default = '\033[39m'
magenta = '\033[38;5;200m'
lime = '\033[38;5;156m'
cyan = '\033[38;5;39m'
yellow = '\033[38;5;229m'

uptime = datetime.now() - datetime.fromtimestamp(psutil.boot_time())

os.system('cls' if os.name == 'nt' else 'clear')
host_name = socket.gethostname()
#host_ip = socket.gethostbyname(host_name+".local")  

def get_ip_address():
 ip_address = '';
 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 s.connect(("8.8.8.8",80))
 ip_address = s.getsockname()[0]
 s.close()
 return ip_address
 
host_ip = get_ip_address()

def ram_usage():
  mem = psutil.virtual_memory()
  s=str(round((mem.available/1000000000.0),2)) + "G/" + str(round((mem.total/1000000000),2)) + "G"
  return s
  
sb=subprocess.Popen(['vcgencmd', 'get_throttled'],stdout=subprocess.PIPE)
cmd_out=sb.communicate()
power_value=cmd_out[0].decode().split("=")[1]

print (blue + "____________________________________________________________________________\n" + normal)
print (" Platform: " + cyan + "%s %s" % (platform.system(),platform.release()) + normal + "\tStart: " + yellow + str(datetime.now().strftime('%a %b %d at %H:%M:%S')) + normal)
print (" IP Address: " + red + host_ip + normal + "\t\tUptime: " + green + "%s" % str(uptime).split('.')[0]  + normal)
print (" CPU Temperature: " + red + cpu_temperature() + normal + "\t\t\tHostname: " + red + host_name + normal)
print (" Memory Free: " + magenta + ram_usage() + normal + "\t\tDisk Free: " + lime + disk_usage('/') + normal)
bum=psutil.cpu_freq(0)
print (" Current CPU speed: " + red + "%d" % int(bum.current) + "Mhz" + normal + "\t\tmin: " + red + "%d" % int(bum.min) + "Mhz" + normal + " max: " + red + "%d" % int(bum.max) + "Mhz" + normal)
print (" Power Status: " + power_value)
print (blue + "____________________________________________________________________________\n" + normal)

Now I had a working basic RPi4 setup with Bookworm Pi OS, rpi-clone, the editor and my cls command. Next – start work on Docker and hence all my usual stuff like Node-Red – just like my RPI5, using Antonio’s instructions on Github. As yet, on the RPi5 we have my full home control setup but I can’t progress audio (aws Polly) until my audio dongle arrives – we DO have AWS working entirely in a container on the RPi5 but as yet not neen able to rest MPG123 until I have the dongle – so I’m backtracking to the RPi4 here to narrow down any issues – Bookworm or change of board…

To clarify: mpg123 needed installing – as root user I just used apt install mpg123 – meanwhile amixer as used to control volume – is already installed by default in Bookworm just as before – no changes – it’s a weird setup…

At the command line:

amixer cset numid=1 -- 80%
amixer cset numid=1 -- 90%
amixer cset numid=1 -- 100%

In the above, I get VERY quiet at 80%, listenable at 90%, maximum at 100%

It seems that from a value range of +400 to -10239, 100% produces 400, 90% produces -664, 80% produces -1728 etc. Weird. Then I decided to re-think my volume control in which I input from 1 to 100…. below 75 was at this point silent on my speaker.

See the updated Polly article for that – I improved the audio level control and made the whole lot work with Node-Red installed in a Docker Container – all explained between the updated Polly blog entry and the RPi5 – Docker blog entry – any missing details – check Antonio Fragola’s DockerIOT Git repository.

Enjoy.


Leave a Reply

Your email address will not be published. Required fields are marked *

Leave the field below empty!


The maximum upload file size: 512 MB. You can upload: image, audio, video, document, spreadsheet, interactive, text, archive, code, other. Links to YouTube, Facebook, Twitter and other services inserted in the comment text will be automatically embedded. Drop file here