#!/usr/bin/bash # am2 # Copyright 2019, 2020, 2021, 2022 Sébastien Millet # Can perform the following: # 1. Compile the code # 2. Upload to Arduino # 3. Read (continually) what is arriving from the USB port the Arduino is # connected to # Versions history (as of 1.3) # 1.3 Output from Arduino is recorded in files named with numbers instead of # date-time string. # 1.4 Adds -t (--testplan) option, to set TESTPLAN macro # 1.5 -t (or --testplan) now comes with a value, so as to manage multiple test # plans. # 1.6 Updated to work fine with Arch arduino package instead of the manually # installed (from tar.gz source) package used so far. # 1.7 Renames archlinux-arduino back to arduino, and created corresponding # symlink (was cleaner to do s). # 2.0 Replaces arduino-builder with arduino-cli # 2.1 Output warning if ARDUINO_USER_LIBS environment variable is not set set -euo pipefail VERSION=2.1 PORT= BOARD= SPEED= FQBN= BUILDDIR= RECORDDIR=out READSPEED= RECORDFILE= UPLOAD="no" REMOVE="no" VERBOSE="no" CATUSB="no" STTY="no" RECORDUSB="no" COMPILE="yes" TESTPLAN= NOCOLOR= DISPLAYSEP=no function finish { if [ "${DISPLAYSEP}" == "yes" ]; then echo "-----END ARDUINO OUTPUT-----" | tee -a "${RECORDFILE}" fi } trap finish EXIT function usage { echo "Usage:" echo " am [OPTIONS...] FILE" echo "Compile FILE using arduino-builder." echo "Example: am sketch.ino" echo "" echo "ENVIRONMENT VARIABLES" echo " If ARDUINO_USER_LIBS is defined and non empty, then arduino-builder" echo " is called with the supplementary option -libraries followed by" echo " ARDUINO_USER_LIBS' value." echo "" echo "OPTIONS" echo " -h --help Display this help screen" echo " -V --version Output version information and quit" echo " -v --verbose Be more talkative" echo " -u --upload Upload compiled code into Arduino" echo " -R --remove Remove /tmp/arduino-core-cache and ./buid" echo " -b --board Board, either 'uno' or 'nano'" echo " -p --port Port, for ex. '/dev/ttyUSB0'" echo " -s --speed Upload speed, for ex. 115200" echo " Normally, speed is infered from device type:" echo " 115200 for Uno, 57600 for Nano" echo " -B --fqbn Board Fully Qualified Name, like 'arduino:avr:uno'" echo " -d --builddir Build directory" echo " -c --catusb Display (continually) what Arduino writes on USB" echo " --stty Tune stty properly for later communication (implied" echo " by --catusb)" echo " -r --recordusb Write USB (continually) to a file (implies -c)" echo " --recordfile Output file if -r option is set" echo " -n --nocompile Don't compile code" echo " --readspeed Read speed of USB. If not specified, this script" echo " will try to infere it from source file. If it" echo " fails, it'll fallback to 9600." echo " This option is useful only if USB is read" echo " (-c or --stty option set)" echo " -t --testplan Set TESTPLAN macro value" echo " (as if #define TESTPLAN VALUE)" echo " --no-color Pass on the --no-color option to arduino-cli" exit 1 } function version { echo "am version ${VERSION}" exit } OPTS=$(getopt -o hVvuRb:p:s:B:d:crnt: --long help,version,verbose,upload,remove,board:,port:,speed:,fqbn:,builddir:,catusb,stty,no-color,recordusb,nocompile,readspeed:,recordfile:,testplan: -n 'am' -- "$@") eval set -- "$OPTS" while true; do case "$1" in -h | --help ) usage; shift ;; -V | --version ) version; shift ;; -v | --verbose ) VERBOSE="yes"; shift ;; -u | --upload ) UPLOAD="yes"; shift ;; -R | --remove ) REMOVE="yes"; shift ;; -b | --board ) BOARD="$2"; shift 2 ;; -p | --port ) PORT="$2"; shift 2 ;; -s | --speed ) SPEED="$2"; shift 2 ;; -B | --fqbn ) FQBN="$2"; shift 2 ;; -d | --builddir ) BUILDDIR="$2"; shift 2 ;; -c | --catusb ) CATUSB="yes"; shift ;; -r | --recordusb ) RECORDUSB="yes"; CATUSB="yes"; shift ;; -n | --nocompile ) COMPILE="no"; shift ;; --readspeed ) READSPEED="$2"; shift 2 ;; --recordfile ) RECORDFILE="$2"; shift 2 ;; --stty ) STTY="yes"; shift ;; --no-color ) NOCOLOR="--no-color"; shift ;; -t | --testplan ) TESTPLAN="$2"; shift 2 ;; -- ) shift; break ;; * ) break ;; esac done FILE=${1:-} TRAILINGOPTS=${2:-} if [ -n "${TRAILINGOPTS}" ]; then echo "Error: trailing options" exit 1; fi if [ -z "${FILE}" ]; then echo "Error: no input file" exit 1; fi set +e if [ -n "${BOARD}" ]; then if [ "${BOARD}" != "uno" ] && [ "${BOARD}" != "nano" ]; then echo "Error: board '${BOARD}' unknown" exit 1 fi fi ARDUINODIR=/usr/share/arduino COUNTUNO=$(compgen -G '/dev/ttyACM*' | wc -l) COUNTNANO=$(compgen -G '/dev/ttyUSB*' | wc -l) if [ -z "${BOARD}" ]; then if [ "${COUNTUNO}" -ge 1 ] && [ "${COUNTNANO}" -ge 1 ]; then echo "Error: cannot guess board, found ${COUNTUNO} uno(s), ${COUNTNANO} nano(s)" exit 10 fi if [ "${COUNTUNO}" -ge 1 ]; then BOARD=uno elif [ "${COUNTNANO}" -ge 1 ]; then BOARD=nano fi if [ -z "${BOARD}" ]; then echo "Error: cannot guess board, none found"; exit 10 fi fi if [ "${UPLOAD}" == "yes" ] || [ "${CATUSB}" == "yes" ]; then if [ -z "${PORT}" ]; then if [ "${BOARD}" == "uno" ]; then COUNT=${COUNTUNO} PORT=$(compgen -G '/dev/ttyACM*') elif [ "${BOARD}" == "nano" ]; then COUNT=${COUNTNANO} PORT=$(compgen -G '/dev/ttyUSB*') else echo "FATAL #001, CHECK THIS CODE" exit 99 fi if [ "${COUNT}" -ge 2 ]; then echo "Error: cannot guess port, more than 1 board '${BOARD}' found" exit 10 fi if [ -z "${PORT}" ]; then echo "Error: cannot guess port, none found" exit 10 fi fi if [ -z "${SPEED}" ]; then if [ "${BOARD}" == "uno" ]; then SPEED=115200 elif [ "${BOARD}" == "nano" ]; then SPEED=57600 else echo "FATAL #002, CHECK THIS CODE" exit 99 fi fi if [ ! -e "${PORT}" ]; then echo "Error: port not found" exit 10 fi fi if [ -z "${FQBN}" ]; then if [ "${BOARD}" == "uno" ]; then FQBN="arduino:avr:uno" elif [ "${BOARD}" == "nano" ]; then FQBN="arduino:avr:nano:cpu=atmega328old" else echo "FATAL #003, CHECK THIS CODE" exit 99 fi fi if [ -z "${BUILDDIR}" ]; then if [[ "${FILE}" == */* ]]; then BUILDDIR=${FILE%/*} BUILDDIR="${BUILDDIR%/}/build" else BUILDDIR=build fi fi if [ "${RECORDUSB}" == "yes" ]; then if [ -z "${RECORDFILE}" ]; then V=${FILE##*/} V=${V%.*} V=${V:-out} PREV= for i in {15..00}; do F="${RECORDDIR}/${V}-$i.txt" if [ -e "${F}" ] && [ -n "${PREV}" ]; then mv "${F}" "${PREV}" fi PREV="${F}" done RECORDFILE="${F}" mkdir -p "${RECORDDIR}" fi else RECORDFILE="/dev/null" fi if [ "${VERBOSE}" == "yes" ]; then echo "-- Settings" echo "Arduino dir: ${ARDUINODIR}" echo "Board: ${BOARD}" echo "Port: ${PORT}" echo "Speed: ${SPEED}" echo "Fqbn: ${FQBN}" echo "Upload: ${UPLOAD}" echo "Remove: ${REMOVE}" echo "Catusb: ${CATUSB}" echo "Recordusb: ${RECORDUSB}" echo "Record file: ${RECORDFILE}" echo "Verbose: ${VERBOSE}" echo "File: ${FILE}" echo "Build dir: ${BUILDDIR}" fi set -e if [ "${REMOVE}" == "yes" ]; then rm -rf /tmp/arduino-core-cache fi if [ "${COMPILE}" == "yes" ]; then echo "-- Compile" mkdir -p "${BUILDDIR}" OPT_LIB= TMP_ULIB=${ARDUINO_USER_LIBS:-} if [ -n "${TMP_ULIB}" ]; then OPT_LIB="--libraries ""${TMP_ULIB}""" else echo echo "***********************************************************" echo "* WARNING *" echo "* The environment variable ARDUINO_USER_LIBS is not set *" echo "***********************************************************" echo fi TESTPLAN_OPT="" if [ -n "${TESTPLAN}" ]; then TESTPLAN_OPT="--build-property build.extra_flags=-DRF433ANY_TESTPLAN=${TESTPLAN}" fi # shellcheck disable=SC2086 # (We don't want to quote OPT_LIB as it can contain multiple options.) arduino-cli compile -b "${FQBN}" ${NOCOLOR} --build-path "${BUILDDIR}" ${OPT_LIB} ${TESTPLAN_OPT} "${FILE}" fi FILEBASENAME=${FILE##*/} if [ "${UPLOAD}" == "yes" ]; then echo "-- Upload" arduino-cli upload -v -b "${FQBN}" --input-dir "${BUILDDIR}" -p "${PORT}" fi if [ "${CATUSB}" == "yes" ] || [ "${STTY}" == "yes" ]; then if [ -z "${READSPEED}" ]; then TFILE=$(mktemp) gcc -fpreprocessed -dD -x c++ -E "${FILE}" > "${TFILE}" for sp in 9600 19200 28800 38400 57600 115200; do if grep ${sp} "${TFILE}" > /dev/null; then READSPEED=${sp} fi done READSPEED=${READSPEED:-9600} rm "${TFILE}" fi stty -F "${PORT}" -hupcl -echo "${READSPEED}" echo "-- usb setup with speed ${READSPEED}" fi if [ "${CATUSB}" == "yes" ]; then echo "-- Read usb (Ctrl-C to quit)" DISPLAYSEP=yes { echo "speed=${READSPEED}" echo "fqbn=${FQBN}" echo "port=${PORT}" echo "file=${FILE}" echo "filedate=$(date +"%Y-%m-%dT%H:%M:%SZ" -d @$(stat -c '%Y' "${FILE}"))" echo "date=$(date +'%Y-%m-%dT%H:%M:%SZ')" echo "" echo "-----BEGIN ARDUINO OUTPUT-----" } | tee "${RECORDFILE}" tee -a "${RECORDFILE}" < "${PORT}" fi