The Hoof & Paw
DocsCategoriesTagsView the current conditions from the WolfspyreLabs WeatherstationToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Part 7 - Extra Credit

  • In Part One, We talked about hardware
  • In Part Two, We Ubuntified your MicroSD card.
  • In Part Three, We got everything set up to my personal liking, but ymmv.
  • in Part Four, We built CoreDNS!
  • in Part Five, We deployed and configured CoreDNS
  • in Part Six, We added a few odds & ends to make the host more durable
Here in Part Seven We’re going to set up the eInk display →→→
🐺🔥⚗️

PaperTTY: An eInk console

PaperTTY1 is a nifty python tool to display a console TTY on an eInk display.
Here we’ll walk through setting it up to give us an easily viewable status indicator. I wanted some mechanism of easily assessing the health of the DNS hosts… I didn’t want to spend too much time on it, but I wanted it to be robust enough that it would ‘just work’ consistently.

I ended up trying a few different tools, but ended up landing on a combination of PaperTTY1 and DNSTop2.
There are definitely other ways to achieve this goal, and I will likely play with others later..
dnstop

…This works well enough for the moment.


Install Pre-Reqs for PaperTTY

Apt Packages

apt-get install libjpeg-dev libjpeg-turbo8-dev libjpeg8-dev libopenjp2-7 libtiff5 python3-pip \
python3-pip-whl python3-setuptools-whl python3-venv python3.10-venv python3-rgpio \
python3-rpi.gpio rpi.gpio-common rgpio-tools librgpio1

Poetry

Poetry3 is a packaging and dependency management toolchain for Python.

I do not advocate running random shit on the internet piped into an interpreter sight unseen.

I STRONGLY encourage you to look at the script (found here) before running the install one-liner below:

Install Poetry

curl -sSL https://install.python-poetry.org | python3 -
_B='/root/.local/share/pypoetry/venv/bin/poetry'
for TARGET in /root/bin/poetry /root/.local/bin/poetry ; do \
  if [[ -e ${TARGET} ]]; then \
    _L=`ls -la ${TARGET}|awk '{print $11}'`
    echo " TARGET: ${TARGET} exits. ${_L}";
  else 
    warn "TARGET: ${TARGET} does NOT exist. Linking";
    ln -s ${_B} ${TARGET}
    ls -la ${TARGET}
  fi
done

Quickly test and validate that Poetry is functional

root@coredns-03:~# poetry --version
Poetry (version 1.2.1)

PaperTTY Installation

Clone the Repo

root@coredns-03:~# git clone https://github.com/joukos/PaperTTY.git
Cloning into 'PaperTTY'...
remote: Enumerating objects: 801, done.
remote: Counting objects: 100% (29/29), done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 801 (delta 13), reused 20 (delta 8), pack-reused 772
Receiving objects: 100% (801/801), 7.00 MiB | 13.36 MiB/s, done.
Resolving deltas: 100% (497/497), done.
root@coredns-03:~#

Build PaperTTY

root@coredns-03:~# cd ~/PaperTTY && poetry install

Actually running the command should look something like this:

root@coredns-03:~# cd ~/PaperTTY && poetry install
Creating virtualenv papertty-uSU0pReX-py3.10 in /root/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies... (163.6s)

Writing lock file

Package operations: 1 install, 9 updates, 0 removals

  • Updating attrs (19.3.0 -> 22.1.0)
  • Updating idna (2.9 -> 3.4)
  • Updating setuptools (65.3.0 -> 50.3.2)
  • Updating six (1.15.0 -> 1.16.0)
  • Updating hyperlink (19.0.0 -> 21.0.0)
  • Updating incremental (17.5.0 -> 21.3.0)
  • Updating pyhamcrest (2.0.2 -> 2.0.3)
  • Updating zope-interface (5.1.0 -> 5.4.0)
  • Installing rpi-gpio (0.7.1)
  • Updating spidev (3.4 -> 3.5)

Installing the current project: papertty (0.1.8b)

Pretty neat and simple right?
Hopefully this got you a working setup, lets make sure real quick:

Quick test of PaperTTY

So… a quick test of PaperTTY will take you down one of two paths….

poetry run papertty --driver EPD2in13v2 scrub && echo "Evey loves steak" | poetry run papertty --driver EPD2in13v2 stdin

“Worky ish”

So… Cool! It works…
eveysteak

BUT… it’s not terribly useful just yet without a magnifying glass…

SO lets continue, and install fonts, shall we?

“W…T…F?”

If you’re… Unlucky..
eink-woes

Head over to the eInk Woes page to try and figure out what’s up.
Then come back here once papertty is working as expected.


Installing Font-related packages

PaperTTY includes the TomThumb4 Font. This is awesome, but pretty small. So, there’s a handful of fonts that can be installed and will work with PaperTTY…
In addition to the fonts available through apt, You can simply deploy TrueType fonts into /usr/share/fonts/truetype/ and reference them when launching PaperTTY.

Quick shout-out to OpenDyslexic5… Which is a neat font with increased readability. It wasn’t suitable to my needs here, but is useful in other contexts.

Install the fonts

apt-get install -y xfonts-utils xfonts-encodings x11-common \
ttf-mscorefonts-installer libfontenc1 fonts-liberation cabextract 

The process of installing these packages will require you to accept a user agreement ( remember, it IS microsoft we’re talking about here… )

Once you’ve installed the fonts; you can make things a hair more legible:

poetry run papertty --driver EPD2in13v2 scrub && echo "Evey loves steak" | \
poetry run papertty --driver EPD2in13v2 stdin --size=10 \
--font="/usr/share/fonts/truetype/msttcorefonts/Andale_Mono.ttf" 
eveysteak

Much more legible, IMHO…

To be fair, from a legibility perspective, both are kinda small; but this thing IS only
2" wide and 1" tall.

fonts11

PaperTTY Start script

The purpose of this script is to make the (re)configuration of the service independent from the invocation of PaperTTY.
(In other words, you won’t have to jiggle the systemd daemon-reload handle anytime you make changes to the PaperTTY start script.)

Install the following script someplace in your $PATH.
I personally chose /root/bin/start-PaperTTY.sh.
If you put this script in a different location, be sure to change your systemd init script for papertty accordingly.
/root/bin/start-PaperTTY.sh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/env bash
set -e
DATESTRING=$(date +%m%d%y)
_STARTTIME=$(/bin/date +%s)
runningTime () {
  _NOW=$(/bin/date +%s)
  echo -e "$(( ${_NOW}-${_STARTTIME}  ))"
}

convertsecs() {
 #take an int which is a number of seconds (IE:
 # now=$(date %s) ; sleep 30; newer_now=$(date %s); age=(${newer_now)-${now}) ; info "This took: $(convertsecs $age)"
 # ^^^^^---- untested example
 ((h=${1}/3600))
 ((m=(${1}%3600)/60))
 ((s=${1}%60))
 printf "%02d:%02d:%02d\n" ${h} ${m} ${s}
}

error () {
  MSG=$1
  _time=$(convertsecs $(runningTime))
  echo -e "\033[31m[${_time}] ERROR\033[0m: $MSG" >>/dev/stderr
}

warn () {
  MSG=$1
  _time=$(convertsecs $(runningTime))
  echo -e "\033[1;33m[${_time}] WARN\033[0m: $MSG" >>/dev/stderr
}

info () {
  MSG=$1
  _time=$(convertsecs $(runningTime))
  echo -e "\033[32m[${_time}] INFO\033[0m: $MSG"
}
VENVROOT='/root/.cache/pypoetry/virtualenvs'
PTTYROOT='/root/PaperTTY'
POETRYBIN='/root/bin/poetry'
if [[ `ls ${VENVROOT}|grep -c papertty` -ne 1 ]]; then
  error "I don't seem to be able to identify the papertty virtualenv. "
  sleep 1
  error "I'm looking for exactly one match on 'papertty' here: ${VENVROOT}:"
  error "Contents listed below....  `ls -lA ${VENVROOT}`"
  sleep 1
  error "I'm Not sure what to do. So... Executing maneuver FORCIBLY INSERT THUMBS UP ASS"
  sleep 3
  error "Ow. I'm In pain."
  false;
else
  _V=`ls ${VENVROOT}/papertty*/bin/activate`
  if [[ -e "${_V}" ]]; then
    info "WolfspyreLabs PaperTTY Wrapper -=/root/bin/StartPaperTTY=-"
    info "Virtualenv initializer: ${_V} "
    . ${_V}
    info "switching to PaperTTY Dir: ${PTTYROOT}"
    cd ${PTTYROOT}
    info "What a mess!!  Scrubbing the Eink Display"
    ${POETRYBIN} run papertty --driver EPD2in13v2 scrub
    info "Fuck...  what a day"
    info "Starting PaperTTY Terminal on /dev/vcsa3"
    info "  partial refresh disabled. Cursor disabled."
    info "  sleep 0.9. with an extra scrub first.... ..Because we care."
    info "     Thank you for choosing WolfspyreLabs "
    sleep 1
    info "Fuck your day.  --hugs n Kisses"
    ${POETRYBIN} run papertty --nopartial --driver EPD2in13v2 terminal --vcsa=/dev/vcsa3  --font="/usr/share/fonts/truetype/msttcorefonts/Andale_Mono.ttf" --size=9 --cols 51 --rows 13 --sleep 0.9 --scrub --cursor=none
  else
    error "${_V} is not real! BOO"
    false
  fi
fi

PaperTTY systemd service file

/lib/systemd/system/papertty.service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[Unit]
Description=PaperTTY
DefaultDependencies=no
After=getty.target

[Service]
Type=simple
KillSignal=SIGINT
TimeoutStopSec=8
Restart=on-failure

### Change the paths below to match yours
WorkingDirectory=/root/PaperTTY
ExecStart=/root/bin/StartPaperTTY
###

[Install]
WantedBy=sysinit.target

Install the systemd script

cat <<EOF>/lib/systemd/system/papertty.service
[Unit]
Description=PaperTTY
DefaultDependencies=no
After=getty.target

[Service]
Type=simple
KillSignal=SIGINT
TimeoutStopSec=8
Restart=on-failure

### Change the paths below to match yours
WorkingDirectory=/root/PaperTTY
ExecStart=/root/bin/StartPaperTTY
###

[Install]
WantedBy=sysinit.target
EOF

rc.local script

As pointed out on the PaperTTY Readme6 Occasionally agetty goes a little apeshit and needs to be kicked after papertty is started on a tty… I added this as a belt-and-suspenders prophylactic to keep the hosts stable.

it may not be necessary. YMMV.

‘/etc/rc.local`:

1
2
3
4
5
6
/#!/bin/bash
#/etc/rc.local

/etc/init.d/procps restart

exit 0

Last but not least… enable and start the PaperTTY Service

Its the simplest of things… …it’s so easily overlooked…

I may or may not have forgotten to do this and sat scratching my head for a bit wondering why stuff wasn’t working….

systemctl enable papertty.service
systemctl start papertty.service

And with that, You’ve done it!

  • PaperTTY is now running
  • It’s bound to the virtual terminal you specified in the PaperTTY Start script.
  • AND it aught be started next time you reboot too!

Strong work!

Now lets set up getty to run DNSTop on the virtual terminal PaperTTY is displaying.

🐺🔥⚗️

DNSTOP

So, there’s not really a whole lot to this, once the kinks are identified and worked out, it’s pretty clean, really.

Packages

1
apt-get install dnstop tio ttylog rungetty tio ttylog ttysnoop 

The two most important packages here are rungetty7 and dnstop2.

Rungetty

If you’ve not poked much into the way a console is managed in linux, this might be a little weird.

TLDR: getty ((or something similar)) listens on known locations for user activity and does the needful for that context.. Usually this involves starting a ’login’ process and letting things go from there.


Rungetty start script

We need to tell the system we want another virtual terminal instantiated.

and we want that terminal instantiated in a fashion which permits us to launch an arbitrary command instead of init / login

The simplest way of doing that is by creating a new singleton service which is responsible for making sure that a console manager process is started just for our chosen virtual terminal, and giving it unique startup parameters which tell it that we want it to run ((in our case)) ‘dnstop’ instead of login

Much like we did with papertty, the way to do this is by creating an init script / systemd startup file. Lets deploy one and continue on, shall we?

Rungetty init script

/lib/systemd/system/getty-tty3.service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
[Unit]
Description=Getty on vcsa3
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
After=rc-local.service

# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes

Conflicts=rescue.service
Before=rescue.service

# On systems without virtual consoles, don't start any getty. Note
# that serial gettys are covered by serial-getty@.service, not this
# unit.
ConditionPathExists=/dev/tty3

[Service]
ExecStart=/sbin/rungetty --autologin root tty3 -u root -g root -- /usr/bin/dnstop -l3 -4QRr5 eth0
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=tty3
TTYPath=/dev/tty3
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREM
ENT LC_IDENTIFICATION

[Install]
WantedBy=getty.target

Once you’ve put this in place, much like you did with the PaperTTY script, you’ll need to enable it, and start it.

Enable the rungetty process
systemctl enable getty-tty3.service
Start the rungetty process
systemctl start getty-tty3.service

This will start a dnstop process as root, listening for dns traffic on eth0 and emitting some metrics.

I’ll leave adjusting dnstop’s parameters as an exercise for the reader.

And that’s about it for dnstop.

WTFUtil