I roast coffee at home regularly. I haven’t updated my roasting log, I need a more lazy man approach to converting it all nicely. But I still do roast, and I use the neatest, dumb script ever..which I know works on OpenBSD and Linux. And it’s public domain

#!/usr/bin/env sh

COFFEEDIR="$HOME/Documents/coffeeroasts"

echo "Coffee country of origin?"; read origin
mkdir -p "$COFFEEDIR/$origin"

filecount=$(ls "$COFFEEDIR/$origin" | wc -l | xargs)

start_timer()
{
	terms="xterm konsole gnome-terminal xfce4-terminal mate-terminal rxvt urxvt xterm-256color"
	if [ $(command -v xterm) ]; then
		xterm -e sh ./timer.sh &
	elif [ $(command -v gnome-terminal) ]; then
		gnome-terminal -- sh ./timer.sh &
	fi
}

seconds_to_ts()
{
	TIME=$1
	if [ $(uname | grep -c "BSD") -eq 1 ]; then
		date -u -r $TIME +%T
	elif [ $(uname) = "Linux" ]; then
		date -d @${TIME} +%T
	fi
}

configure_roast()
{
	echo "What is the coffee's name?"; read coffeename
	dirname=$(echo $coffeename | sed 's/ /_/g')
	roastdir="$COFFEEDIR/$origin/$dirname"
	mkdir -p "$roastdir"
}

write_details()
{
	START="$1"
	DETAILS="$2"
	FILE="$3"

	#timefromstart=$(($(date +%s) - $START))
	timefromstart=$(expr $(date +%s) - $START)
	#printtime=$(date -u -r $timefromstart +%T)
	printtime=$(seconds_to_ts $timefromstart)
	echo "$printtime $DETAILS"
	echo "$printtime $DETAILS" >> "$FILE"
}


calculate_phases()
{
	START="$1"
	ROASTFILE="$2"

	drop=$(grep "TAG:DROP" "$ROASTFILE" | awk '{print $3}')
	#roasttotal=$((($drop - $START)))
	roasttotal=$(expr $drop - $START)

	drystart=$(grep "TAG:DRY" "$ROASTFILE" | awk '{print $3}')
	#drytime=$((($drystart - $START)))
	drytime=$(expr $drystart - $START)
	drypercent=$(echo $(echo "scale=2; $drytime/$roasttotal" | bc -l)*100 | bc)

	onecrackstart=$(grep "TAG:1CSTART" "$ROASTFILE" | awk '{print $3}')
	#browningtotal=$((($onecrackstart - $drystart)))
	browningtotal=$(expr $onecrackstart - $drystart)
	browningpercent=$(echo $(echo "scale=2; $browningtotal/$roasttotal" | bc -l)*100 | bc)

	#developtotal=$((($drop - $onecrackstart)))
	developtotal=$(expr $drop - $onecrackstart)
	developpercent=$(echo $(echo "scale=2; $developtotal/$roasttotal" | bc -l)*100 | bc)

	echo "Roast complete at $(date +%T) in $(seconds_to_ts $roasttotal) minutes"
	echo "Roast complete at $(date +%T) in $(seconds_to_ts $roasttotal) minutes" >> "$ROASTFILE"
	echo "Dry time: $(seconds_to_ts $drytime)\t\t\tDry time percent: $drypercent"
	echo "Dry time: $(seconds_to_ts $drytime)\t\t\tDry time percent: $drypercent" >> "$ROASTFILE"
	echo "Caramelization time: $(seconds_to_ts $browningtotal)\t\tCaramelization percent: $browningpercent"
	echo "Caramelization time: $(seconds_to_ts $browningtotal)\t\tCaramelization percent: $browningpercent" >> "$ROASTFILE"
	echo "Develop time: $(seconds_to_ts $developtotal)\t\t\tDevelop percent: $developpercent"
	echo "Develop time: $(seconds_to_ts $developtotal)\t\t\tDevelop percent: $developpercent" >> "$ROASTFILE"

}

if [ $filecount -eq 0 ]; then
	configure_roast
else
	ls "$COFFEEDIR/$origin" | nl
	echo "Are you roasting any of these? Pick the number or any other key for no"; read pickroast
	dirname=$(ls "$COFFEEDIR/$origin" | nl | grep " $pickroast" | awk '{print $2}')
	if [ -z "$dirname" ]; then
		configure_roast
	else
		roastdir="$COFFEEDIR/$origin/$dirname"
	fi
fi



start=$(date +%F)
roastfile="$roastdir/$start.txt"
touch "$roastfile"

echo "Batch size?" ; read batchsize
echo "Batch weight $batchsize" >> "$roastfile"

echo "Press any key to start the roast timer"; read x
startepoch=$(date +%s)
#xtimer &
start_timer
timer_pid=$!

echo "Enter 1 to note end of drying phase"
echo "Enter 2 to note start of 1 crack"
echo "Enter 3 to note end of roast"
echo "Enter q to exit the program entirely"

while read detail; do
	case "$detail" in
		q)
			break
			exit
			;;
		1)
			write_details $startepoch "TAG:DRY $(date +%s)" "$roastfile"
			continue
			;;
		2)
			write_details $startepoch "TAG:1CSTART $(date +%s)" "$roastfile"
			continue
			;;
		3)
			write_details $startepoch "TAG:DROP $(date +%s)" "$roastfile"
			break
			;;
		*)
			write_details $startepoch "$detail" "$roastfile"
			continue
			;;
	esac
done

calculate_phases $startepoch "$roastfile"
#pkill xtimer
if [ -z $timer_pid ]; then
	pkill timer.sh
else
	kill $timer_pid
fi

echo "Final roast size?" ; read finalbatchsize

echo "Drop weight: $finalbatchsize" >> "$roastfile"

startbatchnum=$(echo $batchsize | sed 's/g//g')
endbatchnum=$(echo $finalbatchsize | sed 's/g//g')
percentloss=$(echo "100*(1-($endbatchnum/$startbatchnum))" | bc -l)
printf "Weight percent loss: %.2f\n" $percentloss >> "$roastfile"

grep "TEMP" "$roastfile" | awk '{print $1" " $3}' | sed 's/C//g' > temp.dat
cat graphit.gp | gnuplot
graphfile="$roastfile-graph.png"
rm temp.dat
mv temps.png "$graphfile"

It’s pretty dumb, but works great. I roast with a BocaBoca250 so I have no way to input data in except to watch the thermometer and do my best. This script is nice because you can select the coffee origin, the name of the coffee you are roasting, batch size, then input information you want such as roaster settings, smells, etc. The part that matters is temperature entry. For example, TEMP 100, TEMP 180, etc. Then numbers 1,2,3 for different phases

  1. Dry phase finish

  2. First crack start

  3. Roast done

Looking at the contents here, you can see how I entered the values. I didn’t enter the times though. That is handle automatically as you enter temperatures and such.

When done, it calculates the time and percentages in dry phase, caramelization phase, and development phase, and lets you enter the final weight which will get you the % of moisture loss while roasting. Lastly, with gnuplot installed you will get a nice graph too like in the above post.

The last 2 scripts needed are timer.sh

#!/usr/bin/env sh

seconds_to_ts()
{
        TIME=$1
        if [ $(uname | grep -c "BSD") -eq 1 ]; then
                date -u -r $TIME +%H:%M:%S
        elif [ $(uname) = "Linux" ]; then
                date -d @${TIME} +%H:%M:%S
        fi
}

start=$(date +%s)
while true; do
	time="$(($(date +%s) - $start))"
	printf '%s\r' "$(seconds_to_ts $time)"
done

and graphit.gp

#!/usr/bin/env gnuplot

set terminal png size 800,600
set output "temps.png"

set xdata time
set timefmt "%H:%M:%S"

set title "Roasting Temperature Profile"
set xlabel "Minutes/seconds"
set ylabel "Temperature (C)"

set key below
set grid
plot "temp.dat" using 1:2 title "Time" with lines

Very basic and dumb but I’ve had fun using it the last year, and it has helped me make consistent roasts.

#100DaysToOffload #Post3