diff --git a/README.md b/README.md index 410b6de..9c4473c 100644 --- a/README.md +++ b/README.md @@ -201,3 +201,41 @@ Use (b) above to boot from the BOOT_TINY USB stick $ sh clone ``` You are asked to confirm or change the image name. + + +## Installation of a Fresh Raspberry Pi + +### install from cloned image (work currently from a Mac only) + +Put compute module into PoE adapter, connect microUSB 'POWER' to +a USB power supply, and connect micorUSB 'SLAVE' to your computer. + +Go to the rpi subdir and then start +``` +cd rpi +`./write_to emmc +``` + +You may test starting up the compute module in the PoE adapter, with a +LAN cable attached, or directly in the iono pi max. + +* on startup, the name will be boxaaaaaa with aaaaaa the last digits of the ethernet address +* a new file should be created in the cfg sub directory + + +### installing RPI tools from an fresh raspberry image (downloaded with Raspberry Pi Imager) + +use the script install-ionopimax.sh or install-ionopi.sh +(together with configsrtc.sh config-rtc-MCP79410.service) +to add the needed items for iono pi and the run time clock + +### create a clone image + +In the rpi subdirectory, execute +``` +./clone +``` +An dated image will be created within about 10 min. and copied to the given directory. + + + diff --git a/rpi/clone b/rpi/clone new file mode 100755 index 0000000..e3954e4 --- /dev/null +++ b/rpi/clone @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +import os +import time + +HOST = "l_samenv@samenv" +SUBDIR = "boxes/rpi_images" +DEFDEST = f"{HOST}:{SUBDIR}" +file = f"{time.strftime('%Y-%m-%d')}.lz4" + + +def prt(text): + print(time.strftime('%H:%M:%S'), text) + + +dest = input(f"destination directory for image [{DEFDEST}]: ") or DEFDEST + + +host, _, subdir = dest.partition(':') +if not subdir: + if '@' in host: + subdir = SUBDIR + else: + subdir, host = host, HOST + +if os.system(f'ssh {host} cd {subdir}'): + raise ValueError(f'{host}:{subdir} does not exist') +if not os.system(f"bash -c 'ssh {host} ls {subdir}/{file} &> /dev/null'"): + print(f'{host}:{subdir}/{file} already exists') + +file = input(f"destination file name [{file}]: ") or file + +prt('needs about 10 minutes') + +pid = os.getpid() +tmpfile = f"/media/rpi{pid}.img" +os.system(f"sudo bash image-backup -i {tmpfile}") +prt('pack and copy ... (3 minutes)') +os.system(f"bash -c 'dd if={tmpfile} bs=512k | lz4 | ssh {host} dd of={subdir}/{file} bs=512k'") +prt('cleanup ...') +os.system(f"sudo rm -f {tmpfile}") diff --git a/rpi/config-rtc-MCP79410.service b/rpi/config-rtc-MCP79410.service new file mode 100644 index 0000000..7924003 --- /dev/null +++ b/rpi/config-rtc-MCP79410.service @@ -0,0 +1,8 @@ +[Unit] +Description=RTC configuration for MCP79410 + +[Service] +ExecStart=/usr/local/bin/configrtc.sh + +[Install] +WantedBy=multi-user.target diff --git a/rpi/configrtc.sh b/rpi/configrtc.sh new file mode 100755 index 0000000..61be01f --- /dev/null +++ b/rpi/configrtc.sh @@ -0,0 +1,7 @@ +#!/bin/bash +modprobe i2c-dev +# Calibrate the clock (default: 0x47). See datasheet for MCP7940N +i2cset -y 1 0x6f 0x08 0x47 +modprobe i2c:mcp7941x +echo mcp7941x 0x6f > /sys/class/i2c-dev/i2c-1/device/new_device +hwclock -s diff --git a/rpi/image-backup b/rpi/image-backup new file mode 100644 index 0000000..9422a3b --- /dev/null +++ b/rpi/image-backup @@ -0,0 +1,521 @@ +#!/bin/bash + +trap '{ stty sane; echo ""; errexit "Aborted"; }' SIGINT SIGTERM + +MNTPATH="/tmp/img-backup-mnt" + +ONEMB=$((1024 * 1024)) + +errexit() +{ + echo "" + echo -e "$1" + echo "" + if [ "${MNTED}" = "TRUE" ]; then + umount "${BOOTMNT}/" &> /dev/null + umount "${MNTPATH}/" &> /dev/null + fi + rm -rf "${MNTPATH}/" &> /dev/null + rmloop + exit 1 +} + +mkloop() +{ + LOOP="$(losetup -f --show -P "${IMGFILE}")" + if [ $? -ne 0 ]; then + errexit "Unable to create loop device" + fi +} + +rmloop() +{ + if [ "${LOOP}" != "" ]; then + losetup -d "${LOOP}" + LOOP="" + fi +} + +mntimg() +{ + mkloop + if [ ! -d "${MNTPATH}/" ]; then + mkdir "${MNTPATH}/" + if [ $? -ne 0 ]; then + errexit "Unable to make ROOT partition mount point" + fi + fi + mount "${LOOP}p2" "${MNTPATH}/" + if [ $? -ne 0 ]; then + errexit "Unable to mount image ROOT partition" + fi + MNTED=TRUE + if [ ! -d "${BOOTMNT}/" ]; then + mkdir -p "${BOOTMNT}/" + if [ $? -ne 0 ]; then + errexit "Unable to make BOOT partition mount point" + fi + fi + mount "${LOOP}p1" "${BOOTMNT}/" + if [ $? -ne 0 ]; then + errexit "Unable to mount image BOOT partition" + fi +} + +umntimg() +{ + umount "${BOOTMNT}/" + if [ $? -ne 0 ]; then + errexit "Unable to unmount image BOOT partition" + fi + umount "${MNTPATH}/" + if [ $? -ne 0 ]; then + errexit "Unable to unmount image ROOT partition" + fi + MNTED=FALSE + rmloop + rm -r "${MNTPATH}/" +} + +fsckerr() +{ + errexit "Filesystem appears corrupted "$1" resize2fs" +} + +usage() +{ + errexit "Usage: $0 [options] [pathto/imagefile for incremental backup]\n\ +-h,--help This usage description\n\ +-i,--initial pathto/filename of image file [,inital size MB [,added space for incremental MB]]\n\ +-n,--noexpand Do not expand filesystem on first run after restore\n\ +-o,--options Additional rsync options (comma separated)\n\ +-u,--ubuntu Ubuntu (Deprecated)\n\ +-x,--extract Extract image from NOOBS (force BOOT partition to -01 / ROOT partition to -02)" +} + +backup() +{ + mntimg + sync + rsync -aDH --partial --numeric-ids --delete --force --exclude "${MNTPATH}" --exclude '/dev' --exclude '/lost+found' --exclude '/media' --exclude '/mnt' \ +--exclude '/proc' --exclude '/run' --exclude '/sys' --exclude '/tmp' --exclude '/var/swap' --exclude '/etc/udev/rules.d/70-persistent-net.rules' \ +--exclude '/var/lib/asterisk/astdb.sqlite3-journal' "${OPTIONS[@]}" / "${MNTPATH}/" + if [[ $? -ne 0 && $? -ne 24 ]]; then + errexit "Unable to create backup" + fi + mkdir -p "${MNTPATH}/dev/" "${MNTPATH}/lost+found/" "${MNTPATH}/media/" "${MNTPATH}/mnt/" "${MNTPATH}/proc/" "${MNTPATH}/run/" "${MNTPATH}/sys/" "${MNTPATH}/tmp/" + if [ $? -ne 0 ]; then + errexit "Unable to create image directories" + fi + chmod a+rwxt "${MNTPATH}/tmp/" + if [ "${EXTRACT}" = "TRUE" ]; then + sed -i "/^[[:space:]]*#/!s|^\(.*root=\)\S\+\(\s\+.*\)$|\1PARTUUID=${PTUUID}-02\2|" "${BOOTMNT}/cmdline.txt" + sed -i "/^[[:space:]]*#/!s|^\S\+\(\s\+/boot\S*\s\+vfat\s\+.*\)$|PARTUUID=${PTUUID}-01\1|" "${MNTPATH}/etc/fstab" + sed -i "/^[[:space:]]*#/!s|^\S\+\(\s\+/\s\+.*\)$|PARTUUID=${PTUUID}-02\1|" "${MNTPATH}/etc/fstab" + fi + if [[ "${OSID}" != "ubuntu" && "${NOEXPAND}" = "FALSE" && "${ROOT_TYPE}" != "f2fs" && $(grep -v '^[[:space:]]*#' "${MNTPATH}/etc/rc.local" | grep -c 'resize-root-fs') -eq 0 ]]; then + cat <<\EOF1 > "${MNTPATH}/etc/resize-root-fs" +#!/bin/bash + +ROOT_PART="$(mount | sed -n 's|^\(/dev/.*\) on / .*|\1|p')" +ROOT_DEV="$(sed 's/[0-9]\+$//' <<< "${ROOT_PART}")" +if [ "${ROOT_DEV}" = "/dev/mmcblk0p" ]; then + ROOT_DEV="${ROOT_DEV:0:(${#ROOT_DEV} - 1)}" +fi +START=$(sfdisk -d "${ROOT_DEV}" | sed -n "s|^${ROOT_PART}\s\+:.*start=\s*\([0-9]\+\).*$|\1|p") +PTTYPE="$(blkid "${ROOT_DEV}" | sed -n 's|^.*PTTYPE="\(\S\+\)".*|\1|p')" +if [ "${PTTYPE}" = "dos" ]; then + sfdisk --delete "${ROOT_DEV}" 2 > /dev/null + echo "${START},+" | sfdisk --no-reread --no-tell-kernel -N2 "${ROOT_DEV}" &> /dev/null +else + PARTUUID="$(blkid "${ROOT_PART}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" + sgdisk -d 2 "${ROOT_DEV}" > /dev/null + sgdisk -n 2:${START}:0 "${ROOT_DEV}" > /dev/null + sgdisk -u 2:${PARTUUID} "${ROOT_DEV}" > /dev/null +fi +cat <<\EOF2 > /etc/init.d/resize_root_fs +#!/bin/sh +### BEGIN INIT INFO +# Provides: resize_root_fs +# Required-Start: +# Required-Stop: +# Default-Start: 3 +# Default-Stop: +# Short-Description: Resize root filesystem +# Description: +### END INIT INFO + +case "$1" in + start) + resize2fs "$(mount | sed -n 's|^\(/dev/.*\) on / .*|\1|p')" + update-rc.d resize_root_fs remove + rm /etc/init.d/resize_root_fs + ;; + *) + exit 3 + ;; +esac +EOF2 +chmod +x /etc/init.d/resize_root_fs +update-rc.d resize_root_fs defaults +sed -i '/resize-root-fs/d' /etc/rc.local +rm /etc/resize-root-fs +shutdown --no-wall -r now +EOF1 + chmod +x "${MNTPATH}/etc/resize-root-fs" + sed -i 's|^exit 0$|/etc/resize-root-fs\nexit 0|' "${MNTPATH}/etc/rc.local" + fi + sync + umntimg + mkloop + fatlabel "${LOOP}p1" "$(fatlabel ${ROOT_DEV_P}1 | sed -n '$p')" &> /dev/null + e2label "${LOOP}p2" "$(e2label ${ROOT_DEV_P}2)" &> /dev/null + rmloop +} + +LOOP="" +MNTED=FALSE +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +if [ $(id -u) -ne 0 ]; then + errexit "$0 must be run as root user" +fi +PGMNAME="$(basename $0)" +for PID in $(pidof -x -o %PPID "${PGMNAME}"); do + if [ ${PID} -ne $$ ]; then + errexit "${PGMNAME} is already running" + fi +done +gdisk -l "${DEVICE}" &> /dev/null +if [ $? -eq 127 ]; then + echo "" + echo "gdisk not installed" + echo "" + echo -n "Ok to install gdisk (y/n)? " + while read -r -n 1 -s answer; do + if [[ "${answer}" = [yYnN] ]]; then + echo "${answer}" + if [[ "${answer}" = [yY] ]]; then + break + else + errexit "Aborted" + fi + fi + done + echo "" + echo "Installing gdisk" + echo "" + apt-get update + apt-get install gdisk +fi +rsync --version &> /dev/null +if [ $? -ne 0 ]; then + errexit "rsync not installed (run: apt-get install rsync)" +fi +OSID="$(sed -n 's|^ID=\(.*\)|\1|p' /etc/os-release)" +BOOTMNT="${MNTPATH}$(sed -n 's|^\S\+\s\+\(/boot\S*\)\s\+vfat\s\+.*$|\1|p' /etc/fstab)" +if [ "${BOOTMNT}" = "${MNTPATH}/boot/firmware" ]; then + BOOTSIZE=512 +else + BOOTSIZE=256 +fi +ROOT_PART="$(mount | sed -n 's|^\(/dev/.*\) on / .*|\1|p')" +ROOT_DEV_P="$(sed 's/[0-9]\+$//' <<< "${ROOT_PART}")" +ROOT_DEV="${ROOT_DEV_P}" +if [ "${ROOT_DEV}" = "/dev/mmcblk0p" ]; then + ROOT_DEV="${ROOT_DEV:0:(${#ROOT_DEV} - 1)}" +fi +ROOT_TYPE=$(blkid "${ROOT_PART}" | sed -n 's|^.*TYPE="\(\S\+\)".*|\1|p') +PTTYPE="$(blkid "${ROOT_DEV}" | sed -n 's|^.*PTTYPE="\(\S\+\)".*|\1|p')" +if [[ "${PTTYPE}" != "dos" && "${PTTYPE}" != "gpt" ]]; then + errexit "Unsupported partition table type: ${PTTYPE}" +fi +PARTUUID_B="$(blkid "${ROOT_DEV_P}1" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" +PARTUUID_R="$(blkid "${ROOT_PART}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" +PTUUID="$(blkid "${ROOT_DEV}" | sed -n 's|^.*PTUUID="\(\S\+\)".*|\1|p')" +DFKFREE=$(df | grep "% /$" | awk '{print $3}') +INISIZE=$(((${DFKFREE} + (${DFKFREE} / 5)) / 1024)) +IMGNAME="" +IMGSIZE="" +IMGINCR="" +IMGFILE="" +OPTIONS=() +EXTRACT=FALSE +NOEXPAND=FALSE +while [ $# -gt 0 ]; do + case "$1" in + + -h|--help) + usage + ;; + + -i|--initial) + OIFS=${IFS} + IFS=',' + INITIAL=($2) + IFS=${OIFS} + IMGNAME=${INITIAL[0]} + IMGSIZE=${INITIAL[1]} + IMGINCR=${INITIAL[2]} + shift 2 + ;; + + -n|--noexpand) + NOEXPAND=TRUE + shift + ;; + + -o|--options) + OIFS=${IFS} + IFS=',' + OPTIONS=($2) + IFS=${OIFS} + shift 2 + ;; + + -u|--ubuntu) + shift + ;; + + -x|--extract) + EXTRACT=TRUE + shift + ;; + + -*|--*) + usage + ;; + + *) + IMGFILE="$1" + shift + ;; + + esac +done +if [ "${IMGFILE}" = "" ]; then + if [ "${IMGNAME}" != "" ]; then + IMGFILE="${IMGNAME}" + if [[ ! "${IMGFILE}" =~ ^/mnt/.*$ && ! "${IMGFILE}" =~ ^/media/.*$ ]]; then + errexit "${IMGFILE} does not begin with /mnt/ or /media/" + fi + if [ -d "${IMGFILE}" ]; then + errexit "${IMGFILE} is a directory" + fi + if [ "${IMGSIZE}" = "" ]; then + IMGSIZE=${INISIZE} + fi + IRFSSIZE=${IMGSIZE} + if [ "${IMGINCR}" = "" ]; then + IMGINCR=0 + fi + ADDMB=${IMGINCR} + else + while : + do + echo "" + read -r -e -i "${IMGFILE}" -p "Image file to create? " IMGFILE + if [ "${IMGFILE}" = "" ]; then + continue + elif [[ ! "${IMGFILE}" =~ ^/mnt/.*$ && ! "${IMGFILE}" =~ ^/media/.*$ ]]; then + echo "" + echo "${IMGFILE} does not begin with /mnt/ or /media/" + continue + fi + if [ -d "${IMGFILE}" ]; then + echo "" + echo "${IMGFILE} is a directory" + elif [ -f "${IMGFILE}" ]; then + echo "" + echo -n "${IMGFILE} already exists, Ok to delete (y/n)? " + while read -r -n 1 -s answer; do + if [[ "${answer}" = [yYnN] ]]; then + echo "${answer}" + if [[ "${answer}" = [yY] ]]; then + break 2 + else + break 1 + fi + fi + done + else + break + fi + done + echo "" + read -r -e -p "Initial image file ROOT filesystem size (MB) [${INISIZE}]? " IRFSSIZE + if [ "${IRFSSIZE}" = "" ]; then + IRFSSIZE=${INISIZE} + fi + if [ "${ROOT_TYPE}" != "f2fs" ]; then + echo "" + read -r -e -p "Added space for incremental updates after shrinking (MB) [0]? " ADDMB + if [ "${ADDMB}" = "" ]; then + ADDMB=0 + fi + fi + echo "" + echo -n "Create ${IMGFILE} (y/n)? " + while read -r -n 1 -s answer; do + if [[ "${answer}" = [yYnN] ]]; then + echo "${answer}" + if [[ "${answer}" = [yY] ]]; then + break + else + errexit "Aborted" + fi + fi + done + fi + if [ -f "${IMGFILE}" ]; then + rm "${IMGFILE}" + if [ $? -ne 0 ]; then + errexit "Unable to delete existing image file" + fi + fi + truncate -s $(((${IRFSSIZE} + ${BOOTSIZE}) * ${ONEMB})) "${IMGFILE}" + if [ $? -ne 0 ]; then + errexit "Unable to create image file" + fi + if [ "${PTTYPE}" = "dos" ]; then + echo "label: dos" | sfdisk "${IMGFILE}" > /dev/null + sfdisk "${IMGFILE}" < /dev/null +,${BOOTSIZE}MiB,c +,+,83 +EOF + else + sgdisk -Z "${IMGFILE}" &> /dev/null + sgdisk -n 1:0:+${BOOTSIZE}M "${IMGFILE}" &> /dev/null + sgdisk -t 1:0700 "${IMGFILE}" > /dev/null + sgdisk -n 2:0:0 "${IMGFILE}" &> /dev/null + sgdisk -t 2:8300 "${IMGFILE}" > /dev/null + gdisk "${IMGFILE}" < /dev/null +r +h +1 +n +0c +n +n +w +y +EOF + fi + mkloop + mkfs.vfat -F 32 -n boot -s 4 "${LOOP}p1" &> /dev/null + if [ $? -ne 0 ]; then + errexit "Unable to create image BOOT filesystem" + fi + dosfsck "${LOOP}p1" > /dev/null + if [ $? -ne 0 ]; then + errexit "Image BOOT filesystem appears corrupted" + fi + if [ "${ROOT_TYPE}" = "f2fs" ]; then + mkfs.f2fs "${LOOP}p2" > /dev/null + else + mkfs.ext4 -b 4096 -L rootfs -q "${LOOP}p2" > /dev/null + fi + if [ $? -ne 0 ]; then + errexit "Unable to create image ROOT filesystem" + fi + rmloop + echo "" + echo "Starting full backup (for incremental backups, run: $0 ${IMGFILE})" + backup + if [ "${ROOT_TYPE}" != "f2fs" ]; then + echo "" + mkloop + e2fsck -f -n "${LOOP}p2" + if [ $? -ne 0 ]; then + fsckerr "before" + fi + echo "" + resize2fs -f -M "${LOOP}p2" + resize2fs -f -M "${LOOP}p2" + resize2fs -f -M "${LOOP}p2" + e2fsck -f -n "${LOOP}p2" + if [ $? -ne 0 ]; then + fsckerr "after" + fi + INFO="$(tune2fs -l "${LOOP}p2" 2>/dev/null)" + rmloop + NEWSIZE=$(sed -n 's|^Block count:\s*\(.*\)|\1|p' <<< "${INFO}") + BLKSIZE=$(sed -n 's|^Block size:\s*\(.*\)|\1|p' <<< "${INFO}") + IMGFILE_P="${IMGFILE}" + if [[ "${IMGFILE_P: -1}" =~ ^[[:digit:]]$ ]]; then + IMGFILE_P+='p' + fi + START=$(sfdisk -d ${IMGFILE} | sed -n "s|^${IMGFILE_P}2\s\+:.*start=\s*\([0-9]\+\).*$|\1|p") + NEWEND=$((${START} + (${NEWSIZE} * (${BLKSIZE} / 512)) + ((${ADDMB} * ${ONEMB}) / 512) - 1)) + if [ "${PTTYPE}" = "gpt" ]; then + ((NEWEND += 33)) + fi + truncate -s $(((${NEWEND} + 1) * 512)) "${IMGFILE}" + if [ "${PTTYPE}" = "dos" ]; then + sfdisk --delete "${IMGFILE}" 2 > /dev/null + echo "${START},+" | sfdisk -N2 "${IMGFILE}" &> /dev/null + fdisk "${IMGFILE}" < /dev/null +x +i +0x${PTUUID} +r +w +EOF + else + sgdisk -Z "${IMGFILE}" &> /dev/null + sgdisk -n 1:0:+${BOOTSIZE}M "${IMGFILE}" &> /dev/null + sgdisk -t 1:0700 "${IMGFILE}" > /dev/null + sgdisk -n 2:0:0 "${IMGFILE}" &> /dev/null + sgdisk -t 2:8300 "${IMGFILE}" > /dev/null + sgdisk -u 1:"${PARTUUID_B}" "${IMGFILE}" > /dev/null + sgdisk -u 2:"${PARTUUID_R}" "${IMGFILE}" > /dev/null + sgdisk -U "${PTUUID}" "${IMGFILE}" > /dev/null + gdisk "${IMGFILE}" < /dev/null +r +h +1 +n +0c +n +n +w +y +EOF + fi + if [ ${ADDMB} -ne 0 ]; then + echo "" + mkloop + e2fsck -f -n "${LOOP}p2" + if [ $? -ne 0 ]; then + fsckerr "before" + fi + echo "" + resize2fs -f "${LOOP}p2" + e2fsck -f -n "${LOOP}p2" + if [ $? -ne 0 ]; then + fsckerr "after" + fi + rmloop + fi + fi + mntimg + while read DIR + do + touch -r "${DIR}" "${MNTPATH}${DIR}" + done <<< "$(echo "$(rsync -aDH --dry-run --itemize-changes --partial --numeric-ids --delete --force --exclude "${MNTPATH}" --exclude '/dev' \ +--exclude '/lost+found' --exclude '/media' --exclude '/mnt' --exclude '/proc' --exclude '/run' --exclude '/sys' --exclude '/tmp' --exclude '/var/swap' \ +--exclude '/etc/udev/rules.d/70-persistent-net.rules' --exclude '/var/lib/asterisk/astdb.sqlite3-journal' "${OPTIONS[@]}" / "${MNTPATH}/")" | \ +sed -n "s|^\.d\.\.t\.\.\.\.\.\.\s\+\(.*$\)|/\1|p")" + umntimg + echo "" +else + if [[ ! "${IMGFILE}" =~ ^/mnt/.*$ && ! "${IMGFILE}" =~ ^/media/.*$ ]]; then + errexit "${IMGFILE} does not begin with /mnt/ or /media/" + fi + if [ -d "${IMGFILE}" ]; then + errexit "${IMGFILE} is a directory" + elif [ ! -f "${IMGFILE}" ]; then + errexit "${IMGFILE} not found" + fi + backup +fi +sync diff --git a/rpi/install-ionopi.sh b/rpi/install-ionopi.sh new file mode 100644 index 0000000..af6f591 --- /dev/null +++ b/rpi/install-ionopi.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# script for iono-pi when starting from fresh rpi image +sudo apt install -y git raspberrypi-kernel-headers +git clone --depth 1 https://github.com/sfera-labs/iono-pi-kernel-module +cd iono-pi-kernel-module +make +sudo make install +dtc -@ -Hepapr -I dts -O dtb -o ionopi.dtbo ionopi.dts +sudo cp ionopi.dtbo /boot/overlays/ + +sudo_append_to () { + sudo bash -c "grep -qxF '$2' $1 || sudo echo '$2' >> $1" +} +sudo_append_to /boot/config.txt dtoverlay=ionopi +sudo_append_to /boot/config.txt dtoverlay=i2c-rtc,mcp7941x +sudo groupadd ionopi || true +sudo cp 99-ionopi.rules /etc/udev/rules.d/ +sudo raspi-config nonint do_i2c 1 +sudo apt install -y i2c-tools +sudo apt autoremove -y --purge fake-hwclock + +# RTC clock +sudo cp ../config-rtc-MCP79410.service /etc/systemd/system/ +sudo cp ../configrtc.sh /usr/local/bin/ +sudo chmod 744 /usr/local/bin/configrtc.sh +sudo chown root:root /usr/local/bin/configrtc.sh +sudo systemctl enable config-rtc-MCP79410.service +sudo_append_to /etc/systemd/timesyncd.conf "NTP=pstime1.psi.ch pstime2.psi.ch pstime3.psi.ch" + +echo "do the following:" +echo "" +echo "> sudo reboot now" +echo "" +echo "and, after reboot set RTC time and check it is working" +echo "" +echo "> sudo hwclock -w" +echo "> timedatectl status" diff --git a/rpi/install-ionopimax.sh b/rpi/install-ionopimax.sh new file mode 100644 index 0000000..a3c5a32 --- /dev/null +++ b/rpi/install-ionopimax.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# script for iono-pi when starting from fresh rpi image +sudo apt install -y git raspberrypi-kernel-headers +git clone --depth 1 https://github.com/sfera-labs/iono-pi-max-kernel-module +cd iono-pi-max-kernel-module +make +sudo make install +dtc -@ -Hepapr -I dts -O dtb -o ionopimax.dtbo ionopimax.dts +sudo cp ionopimax.dtbo /boot/overlays/ + +sudo_append_to () { + sudo bash -c "grep -qxF '$2' $1 || sudo echo '$2' >> $1" +} +sudo_append_to /boot/config.txt dtoverlay=ionopimax +sudo_append_to /boot/config.txt dtoverlay=i2c-rtc,mcp7941x +sudo groupadd ionopimax || true +sudo cp 99-ionopimax.rules /etc/udev/rules.d/ +sudo raspi-config nonint do_i2c 1 +sudo apt install -y i2c-tools +sudo apt autoremove -y --purge fake-hwclock + +# RTC clock +sudo cp ../config-rtc-MCP79410.service /etc/systemd/system/ +sudo cp ../configrtc.sh /usr/local/bin/ +sudo chmod 744 /usr/local/bin/configrtc.sh +sudo chown root:root /usr/local/bin/configrtc.sh +sudo systemctl enable config-rtc-MCP79410.service +sudo_append_to /etc/systemd/timesyncd.conf "NTP=pstime1.psi.ch pstime2.psi.ch pstime3.psi.ch" + +echo "do the following:" +echo "" +echo "> sudo reboot now" +echo "" +echo "and, after reboot set RTC time and check it is working" +echo "" +echo "> sudo hwclock -w" +echo "> timedatectl status" diff --git a/rpi/write_to b/rpi/write_to new file mode 100755 index 0000000..16b9a6c --- /dev/null +++ b/rpi/write_to @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +import sys +import os +import time +import re +from select import select +from glob import glob +from subprocess import check_output, STDOUT, PIPE, Popen, TimeoutExpired + +IMAGES_HOST = 'l_samenv@samenv' +IMAGES_DIR = 'boxes/rpi_images' +IMAGES_PAT = '*.lz4' + +extdisks = {} + + +def prt(text): + print(time.strftime('%H:%M:%S'), text) + + +def cmd(command, verbose=False): + if verbose: + print(f'> {command}') + return check_output(command.split(), stderr=STDOUT).decode('latin-1') + + +def find_raspi(kind): + # collect info about disks + dlist = '\n' + cmd('/usr/sbin/diskutil list') + extdisks.clear() + proposed = [] + noname = re.compile(r' +0: +\*\d+\.?\d* GB +disk') + for dinfo in dlist.split('\n/dev/'): + if not dinfo: + continue + disk, info = dinfo.split(maxsplit=1) + if 'external' in info.split('\n')[0]: + extdisks[disk] = info + if kind == 'emmc': + if 'Windows_FAT_32 bootfs' in info and 'Linux' in info: + proposed.append(disk) + elif noname.search(info): + proposed.append(disk) + elif kind == 'sd': + if 'Apple' not in info: + proposed.append(disk) + return proposed + + +def rpiboot(): + if os.path.exists('/Volumes/bootfs'): + prt('unmount bootfs') + os.system('diskutil unmount bootfs') + lines = [] + with Popen(['rpiboot'], stdout=PIPE) as p: + tmo = 1 + t = 0 + while select([p.stdout], [], [], tmo)[0]: + rawline = p.stdout.readline() + if not rawline: + return tmo != 1 + line = rawline.decode('latin-1').strip() + if lines is None: + print(line) + elif 'Waiting' in line: + tmo = 2 + lines.append(line) + t = time.time() + elif tmo == 1: + lines.append(line) + else: + print('\n'.join(lines)) + print(line) + lines = None + else: + p.terminate() + return False + + +def list_external(): + print('--- external disks:') + for disk, info in extdisks.items(): + print(disk, info) + + +rescue = """ +Remark: not all cables might work. +Example on Markus Mac: +- does not work: black USB C to USB C cable +- works: white USB A to USB C cable plugged into docking station. + +Please unpower the box with compute module, +start this script again and put power immediately.""" + +def write_image(kind): + if kind == 'eemc': + print("\nmake sure microUSB 'SLAVE' is connected to this computer") + print("and microUSB 'POWER' is connected to a USB power supply\n") + prt('try to find CM (compute module on PoE device) ...') + else: + pass + proposed = find_raspi(kind) + if not proposed and kind == 'emmc': + # we may install rpiboot here if not available + # git clone --depth=1 https://github.com/raspberrypi/usbboot + # cd usbboot + # make + # ./rpiboot + + print('not found - put rpi in bootloader mode') + if not rpiboot() and not rpiboot(): + print(rescue) + return + for _ in range(10): + proposed = find_raspi(kind) + if proposed: + break + time.sleep(0.1) + else: + list_external() + print('can not find CM') + return + list_external() + if len(proposed) > 1: + if kind == 'emmc': + print('several potential devices for CM:', proposed) + return + dev = input('select device to write to {" ".join(proposed)}: ') + else: + dev = proposed[0] + images = check_output(['sudo', '-u', 'zolliker', 'ssh', IMAGES_HOST, f'cd {IMAGES_DIR} ; ls {IMAGES_PAT}']).decode('latin-1').split('\n') + for file in sorted(images): + print(file) + print('') + image = input(f'select image to write to {dev} [{file}]: ') or file + prt(f'unmount {dev} ...') + print('you may be prompted for your account password for sudo') + os.system(f'sudo diskutil unmountDisk /dev/{dev}') + print('') + prt('get, uncompress and write... (takes about 15 min)') + os.system(f'ssh {IMAGES_HOST} "dd if={IMAGES_DIR}/{image} bs=512k" | lz4 -d | sudo dd of=/dev/{dev} bs=512k') + prt('unmount bootfs') + for _ in range(3): + if os.path.exists('/Volumes/bootfs'): + prt('unmount bootfs') + os.system('diskutil unmount bootfs') + break + time.sleep(1.0) + prt('done') + os.system(f'diskutil unmountDisk /dev/{dev}') + +if len(sys.argv) == 2: + write_image(sys.argv[1]) +else: + print(""" +Usage: + +> write_to emmc # write to compute module using PoE board" +> write_to sd # write to sd card + +""")