Skip to content

Instantly share code, notes, and snippets.

@fastcat
Last active February 27, 2024 05:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fastcat/5708253 to your computer and use it in GitHub Desktop.
Save fastcat/5708253 to your computer and use it in GitHub Desktop.
Shell script to make it easy to control an Adafruit USB+Serial backpack (http://www.adafruit.com/products/784) from other shell scripts
#!/bin/bash
dev=/dev/serial/by-id/usb-239a_Adafruit_Industries-if00
# prefix byte for all commands
pfx='\xfe'
# named commands, associative array, start as just the hex chars
declare -A cmd=(
[on]=42\\xff # macro for below
[on2]=42 # +1 # arg ignored
[off]=46
[brightness]=99 # +1
# [savebrightness]=98 # +1 # same as brightness
[contrast]=50 # +1
# [savecontrast]=91 # +1 # same as contrast
[autoscroll]=51
[noautoscroll]=52
[clear]=58
# splash=40+32
[movecursor]=47 # +2
[gohome]=48
[cursorback]=4c
[cursorforward]=4d
[underlineon]=4a
[underlineoff]=4b
[blockon]=53
[blockoff]=54
[color]=d0 # +3
# color macros
# led primaries
[white]=d0\\xff\\xff\\xff
[red]=d0\\xff\\x00\\x00
[green]=d0\\x00\\xff\\x00
[blue]=d0\\x00\\x00\\xff
# cmy(k) "primaries", roughly color balanced due to led brightness differences
[yellow]=d0\\xff\\x80\\x00
[cyan]=d0\\x00\\xc0\\xff
[magenta]=d0\\xff\\x00\\x80
# other saturated simple combos
# full saturation red+green is just a different shade of yellow, slightly greenish
[teal]=d0\\x00\\xff\\xff
[purple]=d0\\xff\\x00\\xff
# hack
[black]=d0\\x00\\x00\\x00
# size=d1+2
[customchar]=4e
# savecustom=c1+1+1+8
# loadcustom=c0+1
# gpiooff=56+1
# gpioon=57+1
# gpiostart=c3+1
# autolinewrapon=43 # not supp because "not useful"
# autolinewrapoff=44 # not supp because "not useful"
)
declare -A cmdargs=(
[on2]=1
[brightness]=1
[contrast]=1
[movecursor]=2
[color]=3
[customchar]=9 # 1 spot + 8 data bytes (bitmask for each row)
)
for c in "${!cmd[@]}" ; do
# all these commands are prefixed
cmd["$c"]="${pfx}\\x${cmd[$c]}"
done
# non-prefixed commands
cmd[nl]='\n'
# "fonts" for slots 1-8
# processed as decimal format from https://www.quinapalus.com/hd44780udg.html
declare -a font_hfill=(
"16,16,16,16,16,16,16,16"
"24,24,24,24,24,24,24,24"
"28,28,28,28,28,28,28,28"
"30,30,30,30,30,30,30,30"
"31,31,31,31,31,31,31,31"
)
declare -a font_vfill=(
"00,00,00,00,00,00,00,31"
"00,00,00,00,00,00,31,31"
"00,00,00,00,00,31,31,31"
"00,00,00,00,31,31,31,31"
"00,00,00,31,31,31,31,31"
"00,00,31,31,31,31,31,31"
"00,31,31,31,31,31,31,31"
"31,31,31,31,31,31,31,31"
)
declare -a font_loadbars=(
# 5 entries
"${font_hfill[@]}"
# 3 "multiplier" blocks
"31,31,31,31,31,17,17,31"
"31,31,31,17,17,17,17,31"
"31,17,17,17,17,17,17,31"
)
printfx1() {
value="$1"
if [ $value -eq 0 ]; then
value=1
fi
printf %02x $value
}
# printf format string...
out=
# ... and printf arguments
declare -a outargs=()
okempty=no
#set -x
while [ $# -gt 0 ]; do
i="$1"
shift
c="${i#-}"
if [ "$c" = "$i" ]; then
out+="%s"
#TODO: support incoming hex
outargs+=("$i")
elif [ "${cmd["$c"]+xxx}" ]; then
out+="${cmd["$c"]}"
if [ "${cmdargs["$c"]+xxx}" ]; then
if [ $# -lt ${cmdargs["$c"]} ]; then
echo "Not enough args to '$c'" 1>&2
exit 1
fi
for ((a=0;a<${cmdargs["$c"]};++a)); do
out+="\\x$(printfx1 "$1")"
shift
done
fi
elif [ "$c" = "msg" ]; then
out+="%s"
#TODO: support incoming hex
outargs+=("$1")
shift
elif [ "$c" = "hex" ]; then
out+="\\x$1"
shift
elif [ "$c" = "font" ]; then
declare -n fvar="font_$1"
shift
for ((j=0;j<${#fvar[@]};++j)); do
# font entries are 0-7, but bash can't hold 0 in a string
out+="${cmd[customchar]}\\x$j"
for r in ${fvar[$j]//,/ } ; do
out+="\\x$(printfx1 $r)"
done
done
elif [ "$c" = "loadbar" ]; then
n="$1"
shift
declare -a lavgargs=()
lavg=$(cut -d. -f1 < /proc/loadavg)
lavgt=$(cat /proc/loadavg | cut -d. -f2 | cut -c 1)
loffset=0
nn="$n"
while [ $lavg -gt $n -a $nn -ge 5 ]; do
loffset=$((loffset + 5))
lavg=$((lavg - 5))
# 5, 10, 15 each are one char
n=$((nn - (loffset + 14)/15))
done
while [ $loffset -gt 0 ]; do
if [ $loffset -ge 15 ]; then
out+='\x7'
loffset=$((loffset - 15))
elif [ $loffset -eq 10 ]; then
out+='\x6'
loffset=$((loffset - 10))
elif [ $loffset -eq 5 ]; then
out+='\x5'
loffset=$((loffset - 5))
else
echo "WTF?!" 1>&2
exit 1
fi
done
for ((j=0;j<$n;++j)); do
if [ $lavg -lt $j ]; then
out+=' '
elif [ $lavg -gt $j ]; then
out+='\x4'
elif [ $lavgt -ge 8 ]; then
out+='\x3'
elif [ $lavgt -ge 6 ]; then
out+='\x2'
elif [ $lavgt -ge 4 ]; then
out+='\x1'
elif [ $lavgt -ge 2 ]; then
out+='\x0'
else
out+=' '
fi
done
elif [ "$c" = "help" ]; then
echo "Usage: $(basename "$0") [-cmd|message]..."
echo "Commands:"
for k in "${!cmd[@]}" ; do
a=${cmdargs["$k"]}
if [ -z "$a" ]; then
echo " $k"
else
echo " $k ($a args)"
fi
done
echo " -msg <message>"
echo " -help"
echo " -testdev"
echo " -hex <hexchar>"
echo " -font <name>"
exit 0
elif [ "$c" = "testdev" ]; then
okempty=yes
if [ ! -e "$dev" ]; then
exit 1
fi
elif [ "$c" = "powermode" ]; then
okempty=yes
if [ ! -e "$dev" ]; then
exit 1
fi
devshortname=$(basename $(realpath "$dev"))
sysbase=/sys/class/tty/$devshortname/device/
powermodechanged=no
for sysp in $(find $sysbase -type f -path '**/power/control') ; do
[ "$(<$sysp)" != "on" ] || continue
echo "set power $sysp on"
echo on | sudo sponge $sysp
powermodechanged=yes
done
# if it was in suspend mode, it generally needs a reset to work again
if [ "$powermodechanged" = "yes" ]; then
sudo usbreset 239a:0001
for sysp in $(find $sysbase -type f -path '**/power/control') ; do
[ "$(<$sysp)" != "on" ] || continue
echo "set power $sysp on"
echo on | sudo sponge $sysp
powermodechanged=yes
done
fi
else
echo "Unrecognized argument '$i'" 1>&2
exit 1
fi
done
#set +x
if [ -z "$out" ]; then
if [ "$okempty" != "yes" ]; then
echo "Gimme some args" 1>&2
exit 1
else
exit 0
fi
fi
#printf "$out" "${outargs[@]}" | hd
printf "$out" "${outargs[@]}" > $dev
[Unit]
Description=LCD Clock
[Service]
Type=simple
Environment="PATH=/usr/lib/ccache:%h/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games"
ExecStart=%h/bin/lcdclock.sh
Restart=on-failure
[Install]
WantedBy=default.target
#!/bin/bash
if [ ! -t 0 -a -z "$DIDSETSID" -a -z "$INVOCATION_ID" ]; then
export DIDSETSID=1
exec setsid nohup "$0" "$@" </dev/null &>/dev/null
exit 1
fi
#set -x
lcd="$(dirname "$(realpath "$0")")"/lcd.sh
# don't hold open dirs or such
cd /
# line just wraps via careful counting
format="+%b %d %H:%M:%S"
export lockfile=/run/user/$UID/lcdclock.lock
ltouch=
cleanup() {
"$lcd" -clear -white -brightness 255 -off
if [ -n "$ltouch" ]; then
kill $ltouch
fi
}
if ! lockfile-create --use-pid --retry 0 --lock-name $lockfile &>/dev/null ; then
[ ! -t 0 ] || echo "Cannot get lock"
exit 1
fi
lfpid=$BASHPID
trap cleanup EXIT
lockfile-touch --lock-name $lockfile &
ltouch=$!
sleep=
#shorthost="${HOSTNAME:-$(hostname -s)}"
#shorthost="$(printf "%-7s" "${shorthost:0:7}")"
for ((i=1;;++i)); do
[ "$sleep" ] && sleep $sleep
# don't stomp on other lcd usage
if false ; then
sleep=10
i=0
continue
fi
sleep=0.25
if [ $i -eq 1 -o $i -gt 300 ]; then
[ $i -gt 1 ] && i=0
# reset lcd after stolen or every 5 minutes in case it's messed up
# if this fails, retry lcd cleanup on next round
"$lcd" -testdev -clear -on -white -brightness 3 -powermode -font loadbars || {
i=0
continue
}
fi
lcd.sh -testdev -gohome -msg "$(date "$format")" -loadbar 16 || i=0
if [ $((i % 4)) -eq 0 ] && ! lockfile-check --use-pid --lock-name $lockfile || [ "$(<$lockfile)" != "$lfpid" ] ; then
[ ! -t 0 ] || echo "Another instance stole our lock!"
exit 1
fi
done
#!/bin/bash
bmax=3
gmod=60
ymod=30
rmod=15
gmax=$gmod
ymax=$((gmod + ymod))
zero=$(date +%s)
lnow=-1
lnows=-1
lcolor=null
fmod=0
while true ; do
now=$(($(date +%s) - zero))
if [ $now -lt $gmax ]; then
color=green
mod=$gmod
elif [ $now -lt $ymax ]; then
color=yellow
mod=$ymod
else
color=red
mod=$rmod
fi
nows=$((now % mod))
bmod=$((fmod % 2))
if [ $nows -eq 0 -a $lnows -ne $nows ]; then
[ $lcolor = null ] || echo
echo -n "$color "
lcolor=$color
fi
if [ $nows -lt $bmax ]; then
if [ $bmod -eq 0 ] ; then
lcd.sh -$color -brightness 255
else
lcd.sh -white -brightness 3
fi
elif [ $nows -eq $bmax ]; then
lcd.sh -white -brightness 3
fi
if [ $lnow -ne $now ]; then
p=.
else
p=
fi
if read -t 0.3 -p "$p" ; then
zero=$(date +%s)
fmod=0
else
let fmod+=1
fi
lnow=$now
lnows=$nows
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment