diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index bd900e9..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "src/hbprotocol"] - path = src/hbprotocol - url = https://github.com/bipropellant/hbprotocol.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 03ebf7b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: c -sudo: false -cache: - directories: - - ~/arduino_ide - - ~/.arduino15/packages/ -git: - depth: false - quiet: true -env: - global: - - ARDUINO_IDE_VERSION="1.8.7" - - PRETTYNAME="Bipropellant Hoverboard API" - -before_install: - - source <(cat scripts/installArduino.sh) -# - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh) - -#install: -# - arduino --install-library "Adafruit ILI9341","Adafruit GFX Library" - -script: - - build_io_platforms - - -deploy: - provider: script - script: bash scripts/update_arduino_branch.sh - on: - branch: master - repo: bipropellant/bipropellant-hoverboard-api diff --git a/scripts/installArduino.sh b/scripts/installArduino.sh deleted file mode 100644 index 592cd96..0000000 --- a/scripts/installArduino.sh +++ /dev/null @@ -1,852 +0,0 @@ -#!/usr/bin/env bash - -# we need bash 4 for associative arrays -if [ "${BASH_VERSION%%[^0-9]*}" -lt "4" ]; then - echo "BASH VERSION < 4: ${BASH_VERSION}" >&2 - exit 1 -fi - -# associative array for the platforms that will be verified in build_main_platforms() -# this will be eval'd in the functions below because arrays can't be exported -# Uno is ATmega328, Zero is SAMD21G18, ESP8266, Leonardo is ATmega32u4, M4 is SAMD51, Mega is ATmega2560, ESP32 -export MAIN_PLATFORMS='declare -A main_platforms=( [uno]="arduino:avr:uno" [due]="arduino:sam:arduino_due_x" [zero]="arduino:samd:arduino_zero_native" [esp8266]="esp8266:esp8266:huzzah:eesz=4M3M,xtal=80" [leonardo]="arduino:avr:leonardo" [m4]="adafruit:samd:adafruit_metro_m4:speed=120" [mega2560]="arduino:avr:mega:cpu=atmega2560" [esp32]="esp32:esp32:featheresp32:FlashFreq=80" )' - -# associative array for other platforms that can be called explicitly in .travis.yml configs -# this will be eval'd in the functions below because arrays can't be exported -export AUX_PLATFORMS='declare -A aux_platforms=( [trinket]="adafruit:avr:trinket5" [gemma]="arduino:avr:gemma" )' - -export CPLAY_PLATFORMS='declare -A cplay_platforms=( [cplayClassic]="arduino:avr:circuitplay32u4cat" [cplayExpress]="arduino:samd:adafruit_circuitplayground_m0" ) ' - -export SAMD_PLATFORMS='declare -A samd_platforms=( [zero]="arduino:samd:arduino_zero_native", [cplayExpress]="arduino:samd:adafruit_circuitplayground_m0", [m4]="adafruit:samd:adafruit_metro_m4:speed=120" )' - -export M4_PLATFORMS='declare -A m4_platforms=( [m4]="adafruit:samd:adafruit_metro_m4:speed=120", [trellis_m4]="adafruit:samd:adafruit_trellis_m4:speed=120" )' - -export ARCADA_PLATFORMS='declare -A arcada_platforms=( [pybadge]="adafruit:samd:adafruit_pybadge_m4:speed=120", [pygamer]="adafruit:samd:adafruit_pygamer_m4:speed=120" )' - -export IO_PLATFORMS='declare -A io_platforms=( [zero]="arduino:samd:arduino_zero_native", [m4wifi]="adafruit:samd:adafruit_metro_m4_airliftlite:speed=120", [esp8266]="esp8266:esp8266:huzzah:eesz=4M3M,xtal=80" [esp32]="esp32:esp32:featheresp32:FlashFreq=80" )' - -export NRF5X_PLATFORMS='declare -A nrf5x_platforms=( [nrf52840]="adafruit:nrf52:feather52840:softdevice=s140v6,debug=l0")' - -# make display available for arduino CLI -/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 -sleep 3 -export DISPLAY=:1.0 - -# define colors -GRAY='\033[1;30m'; RED='\033[0;31m'; LRED='\033[1;31m'; GREEN='\033[0;32m'; LGREEN='\033[1;32m'; ORANGE='\033[0;33m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; LBLUE='\033[1;34m'; PURPLE='\033[0;35m'; LPURPLE='\033[1;35m'; CYAN='\033[0;36m'; LCYAN='\033[1;36m'; LGRAY='\033[0;37m'; WHITE='\033[1;37m'; - -echo -e "\n########################################################################"; -echo -e "${YELLOW}INSTALLING ARDUINO IDE" -echo "########################################################################"; - -# if .travis.yml does not set version -if [ -z $ARDUINO_IDE_VERSION ]; then -export ARDUINO_IDE_VERSION="1.8.9" -echo "NOTE: YOUR .TRAVIS.YML DOES NOT SPECIFY ARDUINO IDE VERSION, USING $ARDUINO_IDE_VERSION" -fi - -# if newer version is requested -if [ ! -f $HOME/arduino_ide/$ARDUINO_IDE_VERSION ] && [ -f $HOME/arduino_ide/arduino ]; then -echo -n "DIFFERENT VERSION OF ARDUINO IDE REQUESTED: " -shopt -s extglob -cd $HOME/arduino_ide/ -rm -rf * -if [ $? -ne 0 ]; then echo -e """$RED""\xe2\x9c\x96"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -cd $OLDPWD -fi - -# if not already cached, download and install arduino IDE -echo -n "ARDUINO IDE STATUS: " -if [ ! -f $HOME/arduino_ide/arduino ]; then -echo -n "DOWNLOADING: " -wget --quiet https://downloads.arduino.cc/arduino-$ARDUINO_IDE_VERSION-linux64.tar.xz -if [ $? -ne 0 ]; then echo -e """$RED""\xe2\x9c\x96"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -echo -n "UNPACKING ARDUINO IDE: " -[ ! -d $HOME/arduino_ide/ ] && mkdir $HOME/arduino_ide -tar xf arduino-$ARDUINO_IDE_VERSION-linux64.tar.xz -C $HOME/arduino_ide/ --strip-components=1 -if [ $? -ne 0 ]; then echo -e """$RED""\xe2\x9c\x96"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -touch $HOME/arduino_ide/$ARDUINO_IDE_VERSION -else -echo -n "CACHED: " -echo -e """$GREEN""\xe2\x9c\x93" -fi - -# link test library folder to the arduino libraries folder -ln -s $TRAVIS_BUILD_DIR $HOME/arduino_ide/libraries/Adafruit_Test_Library - -# add the arduino CLI to our PATH -export PATH="$HOME/arduino_ide:$PATH" - -echo -e "\n########################################################################"; -echo -e "${YELLOW}INSTALLING DEPENDENCIES" -echo "########################################################################"; - - -# install the due, esp8266, and adafruit board packages -echo -n "ADD PACKAGE INDEX: " -DEPENDENCY_OUTPUT=$(arduino --pref "boardsmanager.additional.urls=https://adafruit.github.io/arduino-board-index/package_adafruit_index.json,http://arduino.esp8266.com/stable/package_esp8266com_index.json,https://dl.espressif.com/dl/package_esp32_index.json" --save-prefs 2>&1) -if [ $? -ne 0 ]; then echo -e """$RED""\xe2\x9c\x96"; else echo -e """$GREEN""\xe2\x9c\x93"; fi - -# This is a hack, we have to install by hand so lets delete it -echo "Removing ESP32 cache" -rm -rf ~/.arduino15/packages/esp32 -echo -n "Current packages list:" -ls ~/.arduino15/packages/ - -INSTALL_ESP32=$([[ $INSTALL_PLATFORMS == *"esp32"* || -z "$INSTALL_PLATFORMS" ]] && echo 1 || echo 0) -INSTALL_DUE=$([[ $INSTALL_PLATFORMS == *"due"* || -z "$INSTALL_PLATFORMS" ]] && echo 1 || echo 0) -INSTALL_ZERO=$([[ $INSTALL_PLATFORMS == *"zero"* || -z "$INSTALL_PLATFORMS" ]] && echo 1 || echo 0) -INSTALL_ESP8266=$([[ $INSTALL_PLATFORMS == *"esp8266"* || -z "$INSTALL_PLATFORMS" ]] && echo 1 || echo 0) -INSTALL_AVR=$([[ $INSTALL_PLATFORMS == *"avr"* || -z "$INSTALL_PLATFORMS" ]] && echo 1 || echo 0) -INSTALL_SAMD=$([[ $INSTALL_PLATFORMS == *"samd"* || -z "$INSTALL_PLATFORMS" ]] && echo 1 || echo 0) -INSTALL_NRF52=$([[ $INSTALL_PLATFORMS == *"nrf52"* || -z "$INSTALL_PLATFORMS" ]] && echo 1 || echo 0) - -if [[ $INSTALL_ESP32 == 1 ]]; then - echo -n "ESP32: " - DEPENDENCY_OUTPUT=$(arduino --install-boards esp32:esp32 2>&1) - if [ $? -ne 0 ]; then echo -e "\xe2\x9c\x96 OR CACHED"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -fi - -if [[ $INSTALL_DUE == 1 ]]; then - echo -n "DUE: " - DEPENDENCY_OUTPUT=$(arduino --install-boards arduino:sam 2>&1) - if [ $? -ne 0 ]; then echo -e "\xe2\x9c\x96 OR CACHED"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -fi - -if [[ $INSTALL_ZERO == 1 ]]; then - echo -n "ZERO: " - DEPENDENCY_OUTPUT=$(arduino --install-boards arduino:samd 2>&1) - if [ $? -ne 0 ]; then echo -e "\xe2\x9c\x96 OR CACHED"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -fi - -if [[ $INSTALL_ESP8266 == 1 ]]; then - echo -n "ESP8266: " - DEPENDENCY_OUTPUT=$(arduino --install-boards esp8266:esp8266 2>&1) - if [ $? -ne 0 ]; then echo -e "\xe2\x9c\x96 OR CACHED"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -fi - -if [[ $INSTALL_AVR == 1 ]]; then - echo -n "ADAFRUIT AVR: " - DEPENDENCY_OUTPUT=$(arduino --install-boards adafruit:avr 2>&1) - if [ $? -ne 0 ]; then echo -e "\xe2\x9c\x96 OR CACHED"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -fi - -if [[ $INSTALL_SAMD == 1 ]]; then - echo -n "ADAFRUIT SAMD: " - DEPENDENCY_OUTPUT=$(arduino --install-boards adafruit:samd 2>&1) - if [ $? -ne 0 ]; then echo -e "\xe2\x9c\x96 OR CACHED"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -fi - -if [[ $INSTALL_NRF52 == 1 ]]; then - echo -n "ADAFRUIT NRF5X: " - DEPENDENCY_OUTPUT=$(arduino --install-boards adafruit:nrf52 2>&1) - if [ $? -ne 0 ]; then echo -e "\xe2\x9c\x96 OR CACHED"; else echo -e """$GREEN""\xe2\x9c\x93"; fi -fi - -# install random lib so the arduino IDE grabs a new library index -# see: https://github.com/arduino/Arduino/issues/3535 -echo -n "UPDATE LIBRARY INDEX: " -DEPENDENCY_OUTPUT=$(arduino --install-library USBHost > /dev/null 2>&1) -if [ $? -ne 0 ]; then echo -e """$RED""\xe2\x9c\x96"; else echo -e """$GREEN""\xe2\x9c\x93"; fi - -# set the maximal compiler warning level -echo -n "SET BUILD PREFERENCES: " -DEPENDENCY_OUTPUT=$(arduino --pref "compiler.warning_level=all" --save-prefs 2>&1) -if [ $? -ne 0 ]; then echo -e """$RED""\xe2\x9c\x96"; else echo -e """$GREEN""\xe2\x9c\x93"; fi - -# init the json temp var for the current platform -export PLATFORM_JSON="" - -# init test stats counters -export PASS_COUNT=0 -export SKIP_COUNT=0 -export FAIL_COUNT=0 -export PDE_COUNT=0 - -# build all of the examples for the passed platform -function build_platform() -{ - - # arrays can't be exported, so we have to eval - eval $MAIN_PLATFORMS - eval $AUX_PLATFORMS - eval $CPLAY_PLATFORMS - eval $M4_PLATFORMS - eval $ARCADA_PLATFORMS - eval $IO_PLATFORMS - eval $NRF5X_PLATFORMS - - # reset platform json var - PLATFORM_JSON="" - - # expects argument 1 to be the platform key - local platform_key=$1 - - # placeholder for platform - local platform="" - - # track the exit code for this platform - local exit_code=0 - - # grab all pde and ino example sketches - declare -a examples - - if [ "$PLATFORM_CHECK_ONLY_ON_FILE" = true ]; then - # loop through results and add them to the array - examples=($( - for f in $(find . -type f -iname '*.ino' -o -iname '*.pde'); do - # TODO: distinguish platforms - if [ -e "$(dirname $f)/.$platform_key.test" ]; then - echo "$f" - fi - done - )) - else - # loop through results and add them to the array - examples=($(find $PWD -name "*.pde" -o -name "*.ino")) - fi - - # get the last example in the array - local last="${examples[@]:(-1)}" - - # grab the platform info from array or bail if invalid - if [[ ${main_platforms[$platform_key]} ]]; then - platform=${main_platforms[$platform_key]} - elif [[ ${aux_platforms[$platform_key]} ]]; then - platform=${aux_platforms[$platform_key]} - elif [[ ${cplay_platforms[$platform_key]} ]]; then - platform=${cplay_platforms[$platform_key]} - elif [[ ${m4_platforms[$platform_key]} ]]; then - platform=${m4_platforms[$platform_key]} - elif [[ ${arcada_platforms[$platform_key]} ]]; then - platform=${arcada_platforms[$platform_key]} - elif [[ ${io_platforms[$platform_key]} ]]; then - platform=${io_platforms[$platform_key]} - elif [[ ${nrf5x_platforms[$platform_key]} ]]; then - platform=${nrf5x_platforms[$platform_key]} - else - echo "NON-STANDARD PLATFORM KEY: $platform_key" - platform=$platform_key - fi - - echo -e "\n########################################################################"; - - echo -e -n "${YELLOW}SWITCHING TO ${platform_key}: " - - # switch to the requested board. - # we have to avoid reading the exit code of local: - # "when declaring a local variable in a function, the local acts as a command in its own right" - local platform_stdout - platform_stdout=$(arduino --board $platform --save-prefs 2>&1) - - # grab the exit status of the arduino board change - local platform_switch=$? - - # notify if the platform switch failed - if [ $platform_switch -ne 0 ]; then - # heavy X - echo -e """$RED""\xe2\x9c\x96" - echo -e "arduino --board ${platform} --save-prefs 2>&1" - echo $platform_stdout - exit_code=1 - else - # heavy checkmark - echo -e """$GREEN""\xe2\x9c\x93" - fi - - echo "########################################################################"; - - # loop through example sketches - for example in "${examples[@]}"; do - - # store the full path to the example's sketch directory - local example_dir=$(dirname $example) - - # store the filename for the example without the path - local example_file=$(basename $example) - - # is this the last example in the loop - local last_example=0 - if [ "$last" == "$example" ]; then - last_example=1 - fi - - echo -n "$example_file: " - - # continue to next example if platform switch failed - if [ $platform_switch -ne 0 ]; then - # heavy X - echo -e """$RED""\xe2\x9c\x96" - - # add json - PLATFORM_JSON="${PLATFORM_JSON}$(json_sketch 0 $example_file $last_example)" - - # increment fails - FAIL_COUNT=$((FAIL_COUNT + 1)) - - # mark fail - exit_code=1 - - continue - - fi - - # ignore this example if there is an all platform skip - if [[ -f "${example_dir}/.test.skip" ]]; then - - # right arrow - echo -e "\xe2\x9e\x9e" - - # add json - PLATFORM_JSON="${PLATFORM_JSON}$(json_sketch -1 $example_file $last_example)" - - # increment skips - SKIP_COUNT=$((SKIP_COUNT + 1)) - - continue - - fi - - # ignore this example if there is a skip file preset for this specific platform - if [[ -f "${example_dir}/.${platform_key}.test.skip" ]]; then - - # right arrow - echo -e "\xe2\x9e\x9e" - - # add json - PLATFORM_JSON="${PLATFORM_JSON}$(json_sketch -1 $example_file $last_example)" - - # increment skips - SKIP_COUNT=$((SKIP_COUNT + 1)) - - continue - fi - - # make sure that all examples are .ino files - if [[ $example =~ \.pde$ ]]; then - - # heavy X - echo -e """$RED""\xe2\x9c\x96" - - echo -e "-------------------------- DEBUG OUTPUT --------------------------\n" - echo "${LRED}PDE EXTENSION. PLEASE UPDATE TO INO" - echo -e "\n------------------------------------------------------------------\n" - - # add json - PLATFORM_JSON="${PLATFORM_JSON}$(json_sketch 0 $example_file $last_example)" - - # increment fails - FAIL_COUNT=$((FAIL_COUNT + 1)) - - # mark as fail - exit_code=1 - - continue - - fi - - # verify the example, and save stdout & stderr to a variable - # we have to avoid reading the exit code of local: - # "when declaring a local variable in a function, the local acts as a command in its own right" - local build_stdout - build_stdout=$(arduino --verify $example 2>&1) - - # echo output if the build failed - if [ $? -ne 0 ]; then - - # heavy X - echo -e """$RED""\xe2\x9c\x96" - - echo -e "----------------------------- DEBUG OUTPUT -----------------------------\n" - echo "$build_stdout" - echo -e "\n------------------------------------------------------------------------\n" - - # add json - PLATFORM_JSON="${PLATFORM_JSON}$(json_sketch 0 $example_file $last_example)" - - # increment fails - FAIL_COUNT=$((FAIL_COUNT + 1)) - - # mark as fail - exit_code=1 - - else - - # heavy checkmark - echo -e """$GREEN""\xe2\x9c\x93" - - # add json - PLATFORM_JSON="${PLATFORM_JSON}$(json_sketch 1 "$example_file" $last_example)" - - # increment passes - PASS_COUNT=$((PASS_COUNT + 1)) - - fi - - done - - return $exit_code - -} - -# build all examples for every platform in $MAIN_PLATFORMS -function build_main_platforms() -{ - - # arrays can't be exported, so we have to eval - eval $MAIN_PLATFORMS - - # track the build status all platforms - local exit_code=0 - - # var to hold platforms - local platforms_json="" - - # get the last element in the array - local last="${main_platforms[@]:(-1)}" - - # loop through platforms in main platforms assoc array - for p_key in "${!main_platforms[@]}"; do - - # is this the last platform in the loop - local last_platform=0 - if [ "$last" == "${main_platforms[$p_key]}" ]; then - last_platform=1 - fi - - # build all examples for this platform - build_platform $p_key - - # check if build failed - if [ $? -ne 0 ]; then - platforms_json="${platforms_json}$(json_platform $p_key 0 "$PLATFORM_JSON" $last_platform)" - exit_code=1 - else - platforms_json="${platforms_json}$(json_platform $p_key 1 "$PLATFORM_JSON" $last_platform)" - fi - - done - - # exit code is opposite of json build status - if [ $exit_code -eq 0 ]; then - json_main_platforms 1 "$platforms_json" - else - json_main_platforms 0 "$platforms_json" - fi - - return $exit_code - -} - -# build all examples for every platform in $AUX_PLATFORMS -function build_aux_platforms() -{ - - # arrays can't be exported, so we have to eval - eval $AUX_PLATFORMS - - # track the build status all platforms - local exit_code=0 - - # var to hold platforms - local platforms_json="" - - # get the last element in the array - local last="${aux_platforms[@]:(-1)}" - - # loop through platforms in main platforms assoc array - for p_key in "${!aux_platforms[@]}"; do - - # is this the last platform in the loop - local last_platform=0 - if [ "$last" == "${aux_platforms[$p_key]}" ]; then - last_platform=1 - fi - - # build all examples for this platform - build_platform $p_key - - # check if build failed - if [ $? -ne 0 ]; then - platforms_json="${platforms_json}$(json_platform $p_key 0 "$PLATFORM_JSON" $last_platform)" - exit_code=1 - else - platforms_json="${platforms_json}$(json_platform $p_key 1 "$PLATFORM_JSON" $last_platform)" - fi - - done - - # exit code is opposite of json build status - if [ $exit_code -eq 0 ]; then - json_main_platforms 1 "$platforms_json" - else - json_main_platforms 0 "$platforms_json" - fi - - return $exit_code - -} -function build_cplay_platforms() -{ - - # arrays can't be exported, so we have to eval - eval $CPLAY_PLATFORMS - - # track the build status all platforms - local exit_code=0 - - # var to hold platforms - local platforms_json="" - - # get the last element in the array - local last="${cplay_platforms[@]:(-1)}" - - # loop through platforms in main platforms assoc array - for p_key in "${!cplay_platforms[@]}"; do - - # is this the last platform in the loop - local last_platform=0 - if [ "$last" == "${cplay_platforms[$p_key]}" ]; then - last_platform=1 - fi - - # build all examples for this platform - build_platform $p_key - - # check if build failed - if [ $? -ne 0 ]; then - platforms_json="${platforms_json}$(json_platform $p_key 0 "$PLATFORM_JSON" $last_platform)" - exit_code=1 - else - platforms_json="${platforms_json}$(json_platform $p_key 1 "$PLATFORM_JSON" $last_platform)" - fi - - done - - # exit code is opposite of json build status - if [ $exit_code -eq 0 ]; then - json_main_platforms 1 "$platforms_json" - else - json_main_platforms 0 "$platforms_json" - fi - - return $exit_code - -} - -function build_samd_platforms() -{ - - # arrays can't be exported, so we have to eval - eval $SAMD_PLATFORMS - - # track the build status all platforms - local exit_code=0 - - # var to hold platforms - local platforms_json="" - - # get the last element in the array - local last="${samd_platforms[@]:(-1)}" - - # loop through platforms in main platforms assoc array - for p_key in "${!samd_platforms[@]}"; do - - # is this the last platform in the loop - local last_platform=0 - if [ "$last" == "${samd_platforms[$p_key]}" ]; then - last_platform=1 - fi - - # build all examples for this platform - build_platform $p_key - - # check if build failed - if [ $? -ne 0 ]; then - platforms_json="${platforms_json}$(json_platform $p_key 0 "$PLATFORM_JSON" $last_platform)" - exit_code=1 - else - platforms_json="${platforms_json}$(json_platform $p_key 1 "$PLATFORM_JSON" $last_platform)" - fi - - done - - # exit code is opposite of json build status - if [ $exit_code -eq 0 ]; then - json_main_platforms 1 "$platforms_json" - else - json_main_platforms 0 "$platforms_json" - fi - - return $exit_code - -} - -function build_m4_platforms() -{ - - # arrays can't be exported, so we have to eval - eval $M4_PLATFORMS - - # track the build status all platforms - local exit_code=0 - - # var to hold platforms - local platforms_json="" - - # get the last element in the array - local last="${m4_platforms[@]:(-1)}" - - # loop through platforms in main platforms assoc array - for p_key in "${!m4_platforms[@]}"; do - - # is this the last platform in the loop - local last_platform=0 - if [ "$last" == "${m4_platforms[$p_key]}" ]; then - last_platform=1 - fi - - # build all examples for this platform - build_platform $p_key - - # check if build failed - if [ $? -ne 0 ]; then - platforms_json="${platforms_json}$(json_platform $p_key 0 "$PLATFORM_JSON" $last_platform)" - exit_code=1 - else - platforms_json="${platforms_json}$(json_platform $p_key 1 "$PLATFORM_JSON" $last_platform)" - fi - - done - - # exit code is opposite of json build status - if [ $exit_code -eq 0 ]; then - json_main_platforms 1 "$platforms_json" - else - json_main_platforms 0 "$platforms_json" - fi - - return $exit_code - -} - -function build_io_platforms() -{ - - # arrays can't be exported, so we have to eval - eval $IO_PLATFORMS - - # track the build status all platforms - local exit_code=0 - - # var to hold platforms - local platforms_json="" - - # get the last element in the array - local last="${io_platforms[@]:(-1)}" - - # loop through platforms in main platforms assoc array - for p_key in "${!io_platforms[@]}"; do - - # is this the last platform in the loop - local last_platform=0 - if [ "$last" == "${io_platforms[$p_key]}" ]; then - last_platform=1 - fi - - # build all examples for this platform - build_platform $p_key - - # check if build failed - if [ $? -ne 0 ]; then - platforms_json="${platforms_json}$(json_platform $p_key 0 "$PLATFORM_JSON" $last_platform)" - exit_code=1 - else - platforms_json="${platforms_json}$(json_platform $p_key 1 "$PLATFORM_JSON" $last_platform)" - fi - - done - - # exit code is opposite of json build status - if [ $exit_code -eq 0 ]; then - json_main_platforms 1 "$platforms_json" - else - json_main_platforms 0 "$platforms_json" - fi - - return $exit_code - -} - - - -function build_arcada_platforms() -{ - - # arrays can't be exported, so we have to eval - eval $ARCADA_PLATFORMS - - # track the build status all platforms - local exit_code=0 - - # var to hold platforms - local platforms_json="" - - # get the last element in the array - local last="${arcada_platforms[@]:(-1)}" - - # loop through platforms in main platforms assoc array - for p_key in "${!arcada_platforms[@]}"; do - - # is this the last platform in the loop - local last_platform=0 - if [ "$last" == "${arcada_platforms[$p_key]}" ]; then - last_platform=1 - fi - - # build all examples for this platform - build_platform $p_key - - # check if build failed - if [ $? -ne 0 ]; then - platforms_json="${platforms_json}$(json_platform $p_key 0 "$PLATFORM_JSON" $last_platform)" - exit_code=1 - else - platforms_json="${platforms_json}$(json_platform $p_key 1 "$PLATFORM_JSON" $last_platform)" - fi - - done - - # exit code is opposite of json build status - if [ $exit_code -eq 0 ]; then - json_main_platforms 1 "$platforms_json" - else - json_main_platforms 0 "$platforms_json" - fi - - return $exit_code - -} - - -function build_nrf5x_platforms() -{ - - # arrays can't be exported, so we have to eval - eval $NRF5X_PLATFORMS - - # track the build status all platforms - local exit_code=0 - - # var to hold platforms - local platforms_json="" - - # get the last element in the array - local last="${nrf5x_platforms[@]:(-1)}" - - # loop through platforms in main platforms assoc array - for p_key in "${!nrf5x_platforms[@]}"; do - - # is this the last platform in the loop - local last_platform=0 - if [ "$last" == "${nrf5x_platforms[$p_key]}" ]; then - last_platform=1 - fi - - # build all examples for this platform - build_platform $p_key - - # check if build failed - if [ $? -ne 0 ]; then - platforms_json="${platforms_json}$(json_platform $p_key 0 "$PLATFORM_JSON" $last_platform)" - exit_code=1 - else - platforms_json="${platforms_json}$(json_platform $p_key 1 "$PLATFORM_JSON" $last_platform)" - fi - - done - - # exit code is opposite of json build status - if [ $exit_code -eq 0 ]; then - json_main_platforms 1 "$platforms_json" - else - json_main_platforms 0 "$platforms_json" - fi - - return $exit_code - -} - - -# generate json string for a sketch -function json_sketch() -{ - - # -1: skipped, 0: failed, 1: passed - local status_number=$1 - - # the filename of the sketch - local sketch=$2 - - # is this the last sketch for this platform? 0: no, 1: yes - local last_sketch=$3 - - # echo out the json - echo -n "\"$sketch\": $status_number" - - # echo a comma unless this is the last sketch for the platform - if [ $last_sketch -ne 1 ]; then - echo -n ", " - fi - -} - -# generate json string for a platform -function json_platform() -{ - - # the platform key from main platforms or aux platforms - local platform_key=$1 - - # 0: failed, 1: passed - local status_number=$2 - - # the json string for the verified sketches - local sketch_json=$3 - - # is this the last platform we are building? 0: no, 1: yes - local last_platform=$4 - - echo -n "\"$platform_key\": { \"status\": $status_number, \"builds\": { $sketch_json } }" - - # echo a comma unless this is the last sketch for the platform - if [ $last_platform -ne 1 ]; then - echo -n ", " - fi - -} - -# generate final json string -function json_main_platforms() -{ - - # 0: failed, 1: passed - local status_number=$1 - - # the json string for the main platforms - local platforms_json=$2 - - local repo=$(git config --get remote.origin.url) - - echo -e "\n||||||||||||||||||||||||||||| JSON STATUS ||||||||||||||||||||||||||||||" - - echo -n "{ \"repo\": \"$repo\", " - echo -n "\"status\": $status_number, " - echo -n "\"passed\": $PASS_COUNT, " - echo -n "\"skipped\": $SKIP_COUNT, " - echo -n "\"failed\": $FAIL_COUNT, " - echo "\"platforms\": { $platforms_json } }" - - echo -e "||||||||||||||||||||||||||||| JSON STATUS ||||||||||||||||||||||||||||||\n" - -} diff --git a/scripts/update_arduino_branch.sh b/scripts/update_arduino_branch.sh deleted file mode 100644 index e0eca01..0000000 --- a/scripts/update_arduino_branch.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/sh - -setup_git() { - git config --global user.email "travis@travis-ci.org" - git config --global user.name "Travis CI" -} - -flatten_submodule() { - git checkout -b master_arduino - - git rm --cached src/hbprotocol # delete reference to submodule HEAD (no trailing slash) - git rm .gitmodules # if you have more than one submodules, - # you need to edit this file instead of deleting! - rm -rf src/hbprotocol/.git # make sure you have backup!! - rm -rf src/hbprotocol/examples - rm -rf src/hbprotocol/README.md - rm -rf scripts - rm -rf .travis.yml -# mv src/hbprotocol/* src/ -# rm -rf src/hbprotocol - git add . -# git mv src/* ./ - - git commit --message "Auto convert to Arduino Library ($TRAVIS_BUILD_NUMBER)" -} - -upload_files() { - git remote add upload https://${GITHUB_TOKEN}@github.com/bipropellant/bipropellant-hoverboard-api.git > /dev/null 2>&1 - git push --quiet --set-upstream upload master_arduino -f -} - -setup_git -flatten_submodule -upload_files diff --git a/src/hbprotocol b/src/hbprotocol deleted file mode 160000 index 9383e49..0000000 --- a/src/hbprotocol +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9383e495c65978f02e11a51c41db39d4ee1d91d2 diff --git a/src/hbprotocol/LICENSE b/src/hbprotocol/LICENSE new file mode 100644 index 0000000..988b4f1 --- /dev/null +++ b/src/hbprotocol/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Bipropellant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/hbprotocol/ascii_protocol.c b/src/hbprotocol/ascii_protocol.c new file mode 100644 index 0000000..2dba765 --- /dev/null +++ b/src/hbprotocol/ascii_protocol.c @@ -0,0 +1,213 @@ +/* +* This file is part of the hoverboard-firmware-hack project. +* +* Copyright (C) 2018 Simon Hailes +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +#include "protocol.h" + +#include +#include +#include + + +/////////////////////////////////////////////////// +// used in machine_protocol.c +void ascii_byte(PROTOCOL_STAT *s, unsigned char byte ); + +// used in main_ascii_init, external to this file` +int enable_immediate = 0; + +static int initialised = 0; + + +/////////////////////////////////////////////////// +// local variables for handling the 'human' protocol, +// not really for external usage +// +static char ascii_cmd[20]; +static char ascii_out[512]; +static int ascii_posn = 0; +static int8_t asciiProtocolUnlocked = 0; + +typedef struct ASCII_FUNCTION_tag { + int (*fn)(PROTOCOL_STAT *s, char *line, char *ascii_out); + char *description; +} ASCII_FUNCTION; + +typedef struct ASCII_IMMEDIATE_FUNCTION_tag { + int (*fn)(PROTOCOL_STAT *s, char byte, char *ascii_out); + char *description; +} ASCII_IMMEDIATE_FUNCTION; + +static ASCII_IMMEDIATE_FUNCTION immediate_functions[256]; +static ASCII_FUNCTION line_functions[256]; + + +static char password[] = "unlockASCII"; // unlock password, has to start with an 'u' +static int line_unlock_ascii(PROTOCOL_STAT *s, char *cmd, char *ascii_out) { +//case 'u': + if (strlen(cmd) < strlen(password)){ + sprintf(ascii_out, "Wrong Password - Enter unlockASCII to enable ASCII input mode.\r\n"); + } else { + for (int i = 0; i < strlen(password); i++){ + if(cmd[i] != password[i]) { + sprintf(ascii_out, "Wrong Password - Enter unlockASCII to enable ASCII input mode.\r\n"); + break; + } + if(i == 10) { + asciiProtocolUnlocked = 1; + sprintf(ascii_out, "ASCII input active. Type ? for help\r\n"); + } + } + } + return 1; +} + +static int line_lock_ascii(PROTOCOL_STAT *s, char *cmd, char *ascii_out) { +//case 'L': + asciiProtocolUnlocked = 0; + sprintf(ascii_out, "ASCII protocol locked.\r\n"); + return 1; +} + + +void ascii_add_immediate( unsigned char letter, int (*fn)(PROTOCOL_STAT *s, char byte, char *ascii_out), char* description ) { + immediate_functions[letter].fn = fn; + immediate_functions[letter].description = description; + if ((letter >= 'A') && (letter <= 'Z')) { + letter |= 0x20; + immediate_functions[letter].fn = fn; + immediate_functions[letter].description = NULL; + } +} + +void ascii_add_line_fn( unsigned char letter, int (*fn)(PROTOCOL_STAT *s, char *line, char *ascii_out), char *description ) { + line_functions[letter].fn = fn; + line_functions[letter].description = description; + if ((letter >= 'A') && (letter <= 'Z')) { + letter |= 0x20; + line_functions[letter].fn = fn; + line_functions[letter].description = NULL; + } +} + +void ascii_byte(PROTOCOL_STAT *s, unsigned char byte ){ + int skipchar = 0; + // only if no characters buffered, process single keystorkes + if (enable_immediate && (ascii_posn == 0)){ + // returns 1 if char should not be kept in command buffer + if (immediate_functions[byte].fn){ + skipchar = immediate_functions[byte].fn(s, byte, ascii_out); + } else { + skipchar = 0; + } + } + + if (!skipchar){ + // on CR or LF, process gathered messages + if ((byte == '\r') || (byte == '\n')){ + s->send_serial_data((unsigned char *) &byte, 1); + + + if (ascii_posn > 0) { + ascii_cmd[ascii_posn] = 0; + sprintf(ascii_out, "- %s\r\n", ascii_cmd); + s->send_serial_data((unsigned char *) ascii_out, strlen(ascii_out)); + ascii_out[0] = 0; + + if (!asciiProtocolUnlocked && (ascii_cmd[0] != 'u')) { + sprintf(ascii_out, "Locked. Enter unlockASCII to enable ASCII input mode.\r\n>"); + s->send_serial_data((unsigned char *) ascii_out, strlen(ascii_out)); + ascii_cmd[0] = 0; + ascii_posn = 0; + return; + } + + if (line_functions[(unsigned char)ascii_cmd[0]].fn){ + line_functions[(unsigned char)ascii_cmd[0]].fn(s, ascii_cmd, ascii_out); + } else { + sprintf(ascii_out, "No cmd %c - use ? for help\r\n", ascii_cmd[0]); + } + ascii_cmd[0] = 0; + ascii_posn = 0; + // send prompt + byte = '>'; + } else { + byte = '>'; + } + } else { + if (ascii_posn < 20){ + ascii_cmd[ascii_posn++] = byte; + } else { + //byte = '#'; + } + } + } else { + // no echo for immediate. + // send prompt after immediate + byte = '>'; + } + if (ascii_out[0]){ + s->send_serial_data((unsigned char *) ascii_out, strlen(ascii_out)); + ascii_out[0] = 0; + } + + // echo or prompt after processing + if (byte) { + s->send_serial_data((unsigned char *) &byte, 1); + } +} + + +static int line_help(PROTOCOL_STAT *s, char *cmd, char *ascii_out) { + char out[512]; + out[0] = 0; + for (int i = 0; i < 256; i++) { + if (line_functions[i].description) { + int count = strlen(line_functions[i].description); + char *p = line_functions[i].description; + sprintf(out, "%c - ", (char)i); + s->send_serial_data_wait((unsigned char *) out, strlen(out)); + while(count > 0) { + int len = count; + if (len > 128){ + len = 128; + } + s->send_serial_data_wait((unsigned char *) p, len); + p += len; + count -= len; + } + strncpy(out, "\r\n", sizeof(out)-1); + s->send_serial_data_wait((unsigned char *) out, strlen(out)); + } + } + ascii_out[0] = 0; + return 1; +} + + + +int ascii_init() { + if (!initialised) { + ascii_add_line_fn( 'L', line_lock_ascii, "Lock ascii protocol"); + ascii_add_line_fn( 'u', line_unlock_ascii, "unlockASCII - unlock ASCII protocol"); + + ascii_add_line_fn( '?', line_help, "display help"); + initialised = 1; + return 1; + } + return 0; +} diff --git a/src/hbprotocol/ascii_protocol.h b/src/hbprotocol/ascii_protocol.h new file mode 100644 index 0000000..90ebbaf --- /dev/null +++ b/src/hbprotocol/ascii_protocol.h @@ -0,0 +1,30 @@ +/* +* This file is part of the hoverboard-firmware-hack project. +* +* Copyright (C) 2018 Simon Hailes +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +#pragma once + +#include "protocol_private.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/hbprotocol/machine_protocol.c b/src/hbprotocol/machine_protocol.c new file mode 100644 index 0000000..a4ae64d --- /dev/null +++ b/src/hbprotocol/machine_protocol.c @@ -0,0 +1,518 @@ +/* +* This file is part of the hoverboard-firmware-hack project. +* +* Copyright (C) 2018 Simon Hailes +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +/* +* usage: +* call void protocol_byte( unsigned char byte ); with incoming bytes from main.call +* will call protocol_process_message when message received (protocol.c) +* call protocol_post to send a message +*/ + +#include "protocol_private.h" + +#include +#include + + + + + +static unsigned char mpGetTxByte(MACHINE_PROTOCOL_TX_BUFFER *buf); +static char mpGetTxMsg(MACHINE_PROTOCOL_TX_BUFFER *buf, unsigned char *dest); +static void mpPutTx(MACHINE_PROTOCOL_TX_BUFFER *buf, unsigned char value); + +////////////////////////////////////////////////////////////////// + + + + +#define PROTOCOL_STATE_IDLE 0 +#define PROTOCOL_STATE_WAIT_LEN 1 +#define PROTOCOL_STATE_WAIT_CI 2 +#define PROTOCOL_STATE_WAIT_END 3 +#define PROTOCOL_STATE_BADCHAR 4 + +#define PROTOCOL_ACK_TX_IDLE 0 +#define PROTOCOL_ACK_TX_WAITING 1 + + + +// private to us +static void protocol_send_nack(int (*send_serial_data)( unsigned char *data, int len ), unsigned char CI, unsigned char som); +static void protocol_send_ack(int (*send_serial_data)( unsigned char *data, int len ), unsigned char CI); +static int protocol_send(PROTOCOL_STAT *s, PROTOCOL_MSG2 *msg); +static void protocol_send_raw(int (*send_serial_data)( unsigned char *data, int len ), PROTOCOL_MSG2 *msg); + + +// called from main.c +// externed in protocol.h +void protocol_byte(PROTOCOL_STAT *s, unsigned char byte ){ + + switch(s->state){ + case PROTOCOL_STATE_BADCHAR: + case PROTOCOL_STATE_IDLE: + if ((byte == PROTOCOL_SOM_ACK) || (byte == PROTOCOL_SOM_NOACK)){ + s->curr_msg.SOM = byte; + s->last_char_time = protocol_GetTick(); + s->state = PROTOCOL_STATE_WAIT_CI; + s->CS = 0; + } else { + if (s->allow_ascii){ + ////////////////////////////////////////////////////// + // if the byte was NOT SOM (02), then treat it as an + // ascii protocol byte. BOTH protocol can co-exist + ascii_byte(s, byte ); + ////////////////////////////////////////////////////// + } else { + s->last_char_time = protocol_GetTick(); + s->state = PROTOCOL_STATE_BADCHAR; + } + } + break; + + case PROTOCOL_STATE_WAIT_CI: + s->last_char_time = protocol_GetTick(); + s->curr_msg.CI = byte; + s->CS += byte; + s->state = PROTOCOL_STATE_WAIT_LEN; + break; + + case PROTOCOL_STATE_WAIT_LEN: + s->last_char_time = protocol_GetTick(); + s->curr_msg.len = byte; + s->count = 0; + s->CS += byte; + s->state = PROTOCOL_STATE_WAIT_END; + break; + + case PROTOCOL_STATE_WAIT_END: + s->last_char_time = protocol_GetTick(); + s->curr_msg.bytes[s->count++] = byte; + s->CS += byte; + + if (s->count == s->curr_msg.len+1){ + s->last_char_time = 0; + + switch(s->curr_msg.SOM) { + case PROTOCOL_SOM_ACK: + switch(s->curr_msg.bytes[0]) { + case PROTOCOL_CMD_ACK: + if (s->send_state == PROTOCOL_ACK_TX_WAITING){ + if (s->curr_msg.CI == s->ack.curr_send_msg.CI){ + s->ack.last_send_time = 0; + s->send_state = PROTOCOL_ACK_TX_IDLE; + // if we got ack, then try to send a next message + int txcount = mpTxQueued(&s->ack.TxBuffer); + if (txcount){ + // send from tx queue + protocol_send(s, NULL); + } + } else { + // ignore + s->ack.counters.unwantedacks++; + // 'if an ACK is received which contains a CI different to the last sent message, the ACK will be ignored. (?? implications??)' + } + } else { + // ignore, sort of: + // 'if an ACK is received which contains the same CI as the last ACK, the ACK will be ignored.' + s->ack.counters.unwantedacks++; + } + break; + + case PROTOCOL_CMD_NACK: + // 'If an end receives a NACK, it should resend the last message with the same CI, up to 2 retries' + if (s->send_state == PROTOCOL_ACK_TX_WAITING){ + // ignore CI + if (s->ack.retries > 0){ + s->ack.counters.txRetries++; + protocol_send_raw(s->send_serial_data, &s->ack.curr_send_msg); + s->ack.last_send_time = protocol_GetTick(); + s->ack.retries--; + } else { + s->send_state = PROTOCOL_ACK_TX_IDLE; + s->ack.counters.txFailed++; + // if we run out of retries, then try to send a next message + int txcount = mpTxQueued(&s->ack.TxBuffer); + if (txcount){ + // send from tx queue + protocol_send(s, NULL); + } + } + } else { + // unsolicited NACK received? + s->ack.counters.unwantednacks++; + // ignore + } + break; + default: + if (s->CS != 0){ + // Checksum invalid. Complain and Discard. + protocol_send_nack(s->send_serial_data, s->curr_msg.CI, s->curr_msg.SOM); + break; + } + + if (s->ack.lastRXCI == s->curr_msg.CI) { + // 'if a message is received with the same CI as the last received message, ACK will be sent, but the message discarded.' + protocol_send_ack(s->send_serial_data, s->curr_msg.CI); + break; + } + + if( s->curr_msg.CI < s->ack.lastRXCI) { + // CI is smaller than a received message. Probably Overflow of CI. (Other case would resending of previous lost message. Shouldn't happen.) + s->ack.lastRXCI = s->ack.lastRXCI - 256; // Max value of char is 255 + } + + // Add to rxMissing Counter, when CIs where skipped + s->ack.counters.rxMissing += s->curr_msg.CI - (s->ack.lastRXCI + 1); + + // process message + protocol_send_ack(s->send_serial_data, s->curr_msg.CI); + s->ack.lastRXCI = s->curr_msg.CI; + s->ack.counters.rx++; + protocol_process_message(s, &(s->curr_msg)); + break; + } + break; + + case PROTOCOL_SOM_NOACK: + switch(s->curr_msg.bytes[0]) { + case PROTOCOL_CMD_ACK: + // We shouldn't get ACKs in the NoACK protocol.. + s->noack.counters.unwantedacks++; + break; + + case PROTOCOL_CMD_NACK: + // 'If an end receives a NACK, it should resend the last message with the same CI, up to 2 retries' + if (s->noack.retries > 0){ + s->noack.counters.txRetries++; + protocol_send_raw(s->send_serial_data, &s->noack.curr_send_msg); + s->noack.retries--; + } else { + s->noack.counters.txFailed++; + // if we run out of retries, then try to send a next message + int txcount = mpTxQueued(&s->noack.TxBuffer); + if (txcount){ + // send from tx queue + protocol_send(s, NULL); + } + } + break; + default: + if (s->CS != 0){ + // Checksum invalid. Complain and Discard. + protocol_send_nack(s->send_serial_data, s->curr_msg.CI, s->curr_msg.SOM); + break; + } + + if (s->noack.lastRXCI == s->curr_msg.CI) { + // if a message is received with the same CI as the last received message, the message is discarded. + break; + } + + if( s->curr_msg.CI < s->noack.lastRXCI) { + // CI is smaller than a received message. Probably Overflow of CI. (Other case would resending of previous lost message. Shouldn't happen.) + s->noack.lastRXCI = s->noack.lastRXCI - 256; // Max value of char is 255 + } + + // Add to rxMissing Counter, when CIs where skipped + s->noack.counters.rxMissing += s->curr_msg.CI - (s->noack.lastRXCI + 1); + + // process message + s->noack.lastRXCI = s->curr_msg.CI; + s->noack.counters.rx++; + protocol_process_message(s, &(s->curr_msg)); + break; + } + break; + } + s->state = PROTOCOL_STATE_IDLE; + } + break; + } +} + + +// private +void protocol_send_nack(int (*send_serial_data)( unsigned char *data, int len ), unsigned char CI, unsigned char som){ + + // Enforce valid SOM, otherwise Message is discarded by protocol_send_raw. + // PROTOCOL_SOM_ACK is chosen to ensure backwards compatibilty. + // If ANY SOM could be send, it would be identified as a badchar, which would trigger a NACK with the same SOM. + // That's an infinite loop of NACKs with invalid SOM. + if(som != PROTOCOL_SOM_ACK && som != PROTOCOL_SOM_NOACK) { + som = PROTOCOL_SOM_ACK; + } + + char tmp[] = { som, CI, 1, PROTOCOL_CMD_NACK, 0 }; + protocol_send_raw(send_serial_data, (PROTOCOL_MSG2 *)tmp); +} + +// private +void protocol_send_ack(int (*send_serial_data)( unsigned char *data, int len ), unsigned char CI){ + char tmp[] = { PROTOCOL_SOM_ACK, CI, 1, PROTOCOL_CMD_ACK, 0 }; + protocol_send_raw(send_serial_data, (PROTOCOL_MSG2 *)tmp); +} + + +// called to send a message. +// just supply len|bytes - no SOM, CI, or CS +// returns: +// -1 - cannot even queue +// 0 sent immediately +// 1 queued for later TX +int protocol_post(PROTOCOL_STAT *s, PROTOCOL_MSG2 *msg){ + + + if(msg->SOM == PROTOCOL_SOM_ACK) { + int txcount = mpTxQueued(&s->ack.TxBuffer); + if ((s->send_state != PROTOCOL_ACK_TX_WAITING) && !txcount){ + + return protocol_send(s, msg); + } + + // add to tx queue + int total = msg->len + 1; // +1 len + + if (txcount + total >= MACHINE_PROTOCOL_TX_BUFFER_SIZE-2) { + s->ack.TxBuffer.overflow++; + return -1; + } + + char *src = (char *) &(msg->len); + for (int i = 0; i < total; i++) { + mpPutTx(&s->ack.TxBuffer, *(src++)); + } + + return 1; // added to queue + + } else if(msg->SOM == PROTOCOL_SOM_NOACK) { + return protocol_send(s, msg); + } + return -1; +} + + +// private +// note: if NULL in, send one message from queue +int protocol_send(PROTOCOL_STAT *s, PROTOCOL_MSG2 *msg){ + if(msg) { + if(msg->SOM == PROTOCOL_SOM_ACK && s->send_state == PROTOCOL_ACK_TX_WAITING) { + // Tried to Send Message which requires ACK, but a message is still pending. + return -1; + + } else if(msg->SOM == PROTOCOL_SOM_ACK && s->send_state == PROTOCOL_ACK_TX_IDLE) { + // Idling (not waiting for ACK), send the Message directly + memcpy(&s->ack.curr_send_msg, msg, 1 + 1 + 1 + msg->len); // SOM + CI + Len + Payload + s->ack.curr_send_msg.CI = ++(s->ack.lastTXCI); + s->ack.counters.tx++; + protocol_send_raw(s->send_serial_data, &s->ack.curr_send_msg); + s->send_state = PROTOCOL_ACK_TX_WAITING; + s->ack.last_send_time = protocol_GetTick(); + s->ack.retries = 2; + return 0; + + } else if (msg->SOM == PROTOCOL_SOM_NOACK) { + // Send Message without ACK immediately + memcpy(&s->noack.curr_send_msg, msg, 1 + 1 + 1 + msg->len); // SOM + CI + Len + Payload + s->noack.curr_send_msg.CI = ++(s->noack.lastTXCI); + s->noack.counters.tx++; + protocol_send_raw(s->send_serial_data, &s->noack.curr_send_msg); + return 0; + + } else { + // Shouldn't happen, unknowm SOM + return -1; + } + } else { + // No Message was given, work on Buffers then.. + + if(s->send_state == PROTOCOL_STATE_IDLE && mpTxQueued(&s->ack.TxBuffer)) { + // Make sure we are not waiting for another ACK. Check if There is something in the buffer. + mpGetTxMsg(&s->ack.TxBuffer, &s->ack.curr_send_msg.len); + s->ack.curr_send_msg.SOM = PROTOCOL_SOM_ACK; + s->ack.curr_send_msg.CI = ++(s->ack.lastTXCI); + s->ack.counters.tx++; + protocol_send_raw(s->send_serial_data, &s->ack.curr_send_msg); + s->send_state = PROTOCOL_ACK_TX_WAITING; + s->ack.last_send_time = protocol_GetTick(); + s->ack.retries = 2; + return 0; + + } else { + // Do the other queue + int ismsg = mpGetTxMsg(&s->noack.TxBuffer, &s->noack.curr_send_msg.len); + if (ismsg){ + // Queue has message waiting + s->noack.curr_send_msg.SOM = PROTOCOL_SOM_NOACK; + s->noack.curr_send_msg.CI = ++(s->noack.lastTXCI); + s->noack.counters.tx++; + protocol_send_raw(s->send_serial_data, &s->noack.curr_send_msg); + return 0; + } + } + } + return -1; // nothing to send +} + + +// private +void protocol_send_raw(int (*send_serial_data)( unsigned char *data, int len ), PROTOCOL_MSG2 *msg){ + + // Enforce validity of SOM. + if(msg->SOM == PROTOCOL_SOM_ACK || msg->SOM == PROTOCOL_SOM_NOACK) { + unsigned char CS = 0; + int i; + CS -= msg->CI; + CS -= msg->len; + + for (i = 0; i < msg->len; i++){ + CS -= msg->bytes[i]; + } + msg->bytes[i] = CS; + send_serial_data((unsigned char *) msg, msg->len+4); +} +} + + +// called regularly from main.c +// externed from protocol.h +void protocol_tick(PROTOCOL_STAT *s){ + s->last_tick_time = protocol_GetTick(); + + + if(s->send_state == PROTOCOL_ACK_TX_WAITING) { + if ((s->last_tick_time - s->ack.last_send_time) > s->timeout1){ + // 'If an end does not receive an ACK response within (TIMEOUT1), it should resend the last message with the same CI, up to 2 retries' + if (s->ack.retries > 0){ + s->ack.counters.txRetries++; + protocol_send_raw(s->send_serial_data, &s->ack.curr_send_msg); + s->ack.last_send_time = protocol_GetTick(); + s->ack.retries--; + } else { + // if we run out of retries, then try to send a next message + s->send_state = PROTOCOL_ACK_TX_IDLE; + } + } + } + + // send from tx queue + protocol_send(s, NULL); + + switch(s->state){ + case PROTOCOL_STATE_IDLE: + break; + case PROTOCOL_STATE_BADCHAR: + // 'normally, characters received BETWEEN messages which are not SOM should be treated as ASCII commands.' + // 'implement a mode where non SOM characters between messages cause (TIMEOUT2) to be started, + // resulting in a _NACK with CI of last received message + 1_.' + if ((s->last_tick_time - s->last_char_time) > s->timeout2){ + protocol_send_nack(s->send_serial_data, s->curr_msg.CI+1, s->curr_msg.SOM); + s->last_char_time = 0; + s->state = PROTOCOL_STATE_IDLE; + } + break; + default: + // in a message + // 'In receive, if in a message (SOM has been received) and the time since the last character + // exceeds (TIMEOUT2), the incomming message should be discarded, + // and a NACK should be sent with the CI of the message in progress or zero if no CI received yet' + if ((s->last_tick_time - s->last_char_time) > s->timeout2){ + protocol_send_nack(s->send_serial_data, s->curr_msg.CI, s->curr_msg.SOM); + s->last_char_time = 0; + s->state = PROTOCOL_STATE_IDLE; + } + break; + } + + + // process subscriptions + int len = sizeof(s->subscriptions)/sizeof(s->subscriptions[0]); + int index = 0; + + // Check if subscription exists. + for (index = 0; index < len; index++) { + if( s->subscriptions[index].code != 0 && s->subscriptions[index].count != 0 ) { + + // Check if message is due + if( s->subscriptions[index].next_send_time <= s->last_tick_time) { + + //If so, pretend that a Read request has arrived. + + PROTOCOL_MSG2 newMsg; + memset((void*)&newMsg,0x00,sizeof(PROTOCOL_MSG2)); + PROTOCOL_BYTES_READVALS *readvals = (PROTOCOL_BYTES_READVALS *) &(newMsg.bytes); + + readvals->cmd = PROTOCOL_CMD_READVAL; + readvals->code = s->subscriptions[index].code; + newMsg.SOM = s->subscriptions[index].som; + newMsg.len = sizeof(readvals->cmd) + sizeof(readvals->code) + 1; // 1 for Checksum + + protocol_process_message(s, &newMsg); + + + //reschedule job + s->subscriptions[index].next_send_time = s->last_tick_time + s->subscriptions[index].period; + if(s->subscriptions[index].count > 0) { + s->subscriptions[index].count = s->subscriptions[index].count - 1; + } + + } + + + } + } + +} + + +int mpTxQueued(MACHINE_PROTOCOL_TX_BUFFER *buf){ + if (buf->head != buf->tail){ + int count = buf->head - buf->tail; + if (count < 0){ + count += MACHINE_PROTOCOL_TX_BUFFER_SIZE; + } + return count; + } + return 0; +} + +unsigned char mpGetTxByte(MACHINE_PROTOCOL_TX_BUFFER *buf){ + short t = -1; + if (buf->head != buf->tail){ + t = buf->buff[buf->tail]; + buf->tail = ((buf->tail + 1 ) % MACHINE_PROTOCOL_TX_BUFFER_SIZE); + } + return t; +} + +char mpGetTxMsg(MACHINE_PROTOCOL_TX_BUFFER *buf, unsigned char *dest){ + if (mpTxQueued(buf)) { + unsigned char len = *(dest++) = mpGetTxByte(buf); // len of bytes to follow + for (int i = 0; i < len; i++) { + *(dest++) = mpGetTxByte(buf); // data + } + return 1; // we got a message + } + return 0; +} + +void mpPutTx(MACHINE_PROTOCOL_TX_BUFFER *buf, unsigned char value){ + buf->buff[buf->head] = value; + buf->head = ((buf->head + 1 ) % MACHINE_PROTOCOL_TX_BUFFER_SIZE); +} \ No newline at end of file diff --git a/src/hbprotocol/protocol.c b/src/hbprotocol/protocol.c new file mode 100644 index 0000000..6d12f93 --- /dev/null +++ b/src/hbprotocol/protocol.c @@ -0,0 +1,558 @@ +/* +* This file is part of the hoverboard-firmware-hack project. +* +* Copyright (C) 2018 Simon Hailes +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#include "protocol_private.h" +#include +#include + + +/////////////////////////////////////////////////////// +// Function Pointers to system functions +////////////////////////////////////////////////////////// + +// Need to be assigned to functions "real" system fucntions +uint32_t noTick(void) { return 0; }; +uint32_t (*protocol_GetTick)() = noTick; + +void noDelay(uint32_t Delay) {}; +void (*protocol_Delay)(uint32_t Delay) = noDelay; + +void noReset(void) {}; +void (*protocol_SystemReset)() = noReset; + + + +static int initialised_functions = 0; + +// forward declaration +extern PARAMSTAT *params[]; +////////////////////////////////////////////// +// variables you want to read/write here. Only common used ones, specific ones below. + +// Default temporary storage for received values +unsigned char contentbuf[sizeof( ((PROTOCOL_BYTES_WRITEVALS *)0)->content )]; + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +// Default function, wipes receive memory before writing (and readresponse is just a differenct type of writing) + + +void fn_preWriteClear ( PROTOCOL_STAT *s, PARAMSTAT *param, uint8_t fn_type, unsigned char *content, int len ) { + switch (fn_type) { + case FN_TYPE_PRE_WRITE: + case FN_TYPE_PRE_READRESPONSE: + // ensure clear in case of short write + memset(param->ptr, 0, param->len); + break; + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////// +// Variable & Functions for 0x00 version + +static int version = 1; + + +//////////////////////////////////////////////////////////////////////////////////////////// +// Variable & Functions for 0x22 SubscribeData + +void fn_SubscribeData ( PROTOCOL_STAT *s, PARAMSTAT *param, uint8_t fn_type, unsigned char *content, int len ) { + + fn_preWriteClear(s, param, fn_type, content, len); // Wipes memory before write (and readresponse is just a differenct type of writing) + + switch (fn_type) { + + case FN_TYPE_POST_WRITE: + case FN_TYPE_POST_READRESPONSE: + + // Check if length of received data is plausible. + if(len != sizeof(PROTOCOL_SUBSCRIBEDATA)) { + break; + } + + int subscriptions_len = sizeof(s->subscriptions)/sizeof(s->subscriptions[0]); + int index = 0; + + // Check if subscription already exists for this code + for (index = 0; index < subscriptions_len; index++) { + if(s->subscriptions[index].code == ((PROTOCOL_SUBSCRIBEDATA*) content)->code) { + break; + } + } + + // If code was not found, look for vacant subscription slot + if(index == subscriptions_len) { + for (index = 0; index < subscriptions_len; index++) { + // NOTE: if you set a count of 0, or the count runs out, then + // the subscription will be overwritten later - + // i.e. you effectively delete it.... + if( s->subscriptions[index].code == 0 || s->subscriptions[index].count == 0 ) { + break; + } + } + } + + // Fill in new subscription when possible; Plausibility check for period + if(index < subscriptions_len && ((PROTOCOL_SUBSCRIBEDATA*) content)->period >= 10) { + s->subscriptions[index] = *((PROTOCOL_SUBSCRIBEDATA*) content); + //char tmp[100]; + //sprintf(tmp, "subscription added at %d for 0x%x, period %d, count %d, som %d\n", index, ((SUBSCRIBEDATA*) (param->ptr))->code, ((SUBSCRIBEDATA*) (param->ptr))->period, ((SUBSCRIBEDATA*) (param->ptr))->count, ((SUBSCRIBEDATA*) (param->ptr))->som); + //consoleLog(tmp); + } else { + // TODO. Inform sender?? + // consoleLog("no subscriptions available\n"); + } + break; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Variable & Functions for 0x22 and 0x23 ProtocolcountData + +PROTOCOLCOUNT ProtocolcountData = { .rx = 0 }; + +void fn_ProtocolcountDataSum ( PROTOCOL_STAT *s, PARAMSTAT *param, uint8_t fn_type, unsigned char *content, int len ) { + + fn_preWriteClear(s, param, fn_type, content, len); // Wipes memory before write (and readresponse is just a differenct type of writing) + + switch (fn_type) { + + case FN_TYPE_PRE_READ: + ProtocolcountData.rx = s->ack.counters.rx + s->noack.counters.rx; + ProtocolcountData.rxMissing = s->ack.counters.rxMissing + s->noack.counters.rxMissing; + ProtocolcountData.tx = s->ack.counters.tx + s->noack.counters.tx; + ProtocolcountData.txFailed = s->ack.counters.txFailed + s->noack.counters.txFailed; + ProtocolcountData.txRetries = s->ack.counters.txRetries + s->noack.counters.txRetries; + ProtocolcountData.unwantedacks = s->ack.counters.unwantedacks + s->noack.counters.unwantedacks; + ProtocolcountData.unknowncommands = s->ack.counters.unknowncommands + s->noack.counters.unknowncommands; + ProtocolcountData.unplausibleresponse = s->ack.counters.unplausibleresponse + s->noack.counters.unplausibleresponse; + ProtocolcountData.unwantednacks = s->ack.counters.unwantednacks + s->noack.counters.unwantednacks; + break; + + case FN_TYPE_POST_WRITE: + s->ack.counters = ProtocolcountData; + s->noack.counters = ProtocolcountData; + break; + } +} + +void fn_ProtocolcountDataAck ( PROTOCOL_STAT *s, PARAMSTAT *param, uint8_t fn_type, unsigned char *content, int len ) { + + fn_preWriteClear(s, param, fn_type, content, len); // Wipes memory before write (and readresponse is just a differenct type of writing) + + switch (fn_type) { + case FN_TYPE_PRE_READ: + ProtocolcountData = s->ack.counters; + break; + + case FN_TYPE_POST_WRITE: + s->ack.counters = ProtocolcountData; + break; + } +} + +void fn_ProtocolcountDataNoack ( PROTOCOL_STAT *s, PARAMSTAT *param, uint8_t fn_type, unsigned char *content, int len ) { + + fn_preWriteClear(s, param, fn_type, content, len); // Wipes memory before write (and readresponse is just a differenct type of writing) + + switch (fn_type) { + case FN_TYPE_PRE_READ: + ProtocolcountData = s->noack.counters; + break; + + case FN_TYPE_POST_WRITE: + s->noack.counters = ProtocolcountData; + break; + } +} + + +//////////////////////////////////////////////////////////// +// allows read of parameter descritpions and variable length +// accepts (unsigned char first, unsigned char count) in read message! +// data returned +typedef struct tag_descriptions { + unsigned char first; + unsigned char count_read; + char descriptions[251]; +} DESCRIPTIONS; +DESCRIPTIONS paramstat_descriptions; +typedef struct tag_description { + unsigned char len; // overall length of THIS + unsigned char code; // code from params + unsigned char var_len;// length of variable referenced + unsigned char var_type;// UI_NONE or UI_SHORT for the moment + char description[249];// nul term description + // could be expanded here, as len at the top. + // but 'description' above will be shorter than 249, so strucutre variabls can;t be used? +} DESCRIPTION; + +void fn_paramstat_descriptions ( PROTOCOL_STAT *s, PARAMSTAT *param, uint8_t fn_type, unsigned char *content, int len ){ + switch (fn_type) { + case FN_TYPE_PRE_READ: { + // content[0] is the first entry to read. + // content[1] is max count of entries to read + // we prepare a buffer here, an MODIFY the paramstat length to tell it how much to send! (evil!?). + int first = 0; + int count = 100; + if (len > 0) { + first = content[0]; + } + if (len > 1) { + count = content[1]; + } + + if (first >= (sizeof(params)/sizeof(params[0]))) { + params[0]->len = 0; + return; + } + if (first + count >= (sizeof(params)/sizeof(params[0]))){ + count = (sizeof(params)/sizeof(params[0])) - first; + } + + // now loop over requested entries until no more will fit in buffer, + // informing on start and count done. + int actual_count = 0; + int len_out = 0; + char *p = paramstat_descriptions.descriptions; + for (int i = first; i < first+count; i++){ + if(params[i] != NULL) { + int desc_len = 0; + if (params[i]->description) { + desc_len = strlen(params[i]->description); + } + DESCRIPTION *d = (DESCRIPTION *)p; + if (len_out+sizeof(*d)-sizeof(d->description)+desc_len+1 > sizeof(paramstat_descriptions.descriptions)){ + break; + } + d->len = sizeof(*d)-sizeof(d->description)+desc_len+1; + d->code = params[i]->code; + d->var_len = params[i]->len; + d->var_type = params[i]->ui_type; + if (desc_len) { + strcpy(d->description, params[i]->description); + } else { + d->description[0] = 0; + } + p += d->len; + len_out = p - paramstat_descriptions.descriptions; + actual_count++; + } + } + paramstat_descriptions.first = first; + paramstat_descriptions.count_read = actual_count; + param->len = sizeof(DESCRIPTIONS) - sizeof(paramstat_descriptions.descriptions) + len_out; + break; + } + + case FN_TYPE_POST_READ: + param->len = 0; // reset to zero + break; + } +} + + + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// + +// NOTE: Don't start uistr with 'a' +PARAMSTAT *params[256]; + + +PARAMSTAT initialparams[] = { + // Protocol Relevant Parameters + { 0xFF, "descriptions", NULL, UI_NONE, ¶mstat_descriptions, 0, PARAM_R, fn_paramstat_descriptions }, + { 0x00, "version", NULL, UI_LONG, &version, sizeof(int), PARAM_R, NULL }, + { 0x22, "subscribe data", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_SUBSCRIBEDATA), PARAM_RW, fn_SubscribeData }, + { 0x23, "protocol stats ack+noack",NULL, UI_NONE, &ProtocolcountData, sizeof(PROTOCOLCOUNT), PARAM_RW, fn_ProtocolcountDataSum }, + { 0x24, "protocol stats ack", NULL, UI_NONE, &ProtocolcountData, sizeof(PROTOCOLCOUNT), PARAM_RW, fn_ProtocolcountDataAck }, + { 0x25, "protocol stats noack", NULL, UI_NONE, &ProtocolcountData, sizeof(PROTOCOLCOUNT), PARAM_RW, fn_ProtocolcountDataNoack }, + + // Sensor (Hoverboard mode) + { 0x01, "sensor data", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_SENSOR_FRAME), PARAM_R, NULL }, + + // Control and Measurements + { 0x02, "hall data", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_HALL_DATA_STRUCT), PARAM_R, NULL }, + { 0x03, "speed control mm/s", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_SPEED_DATA), PARAM_RW, fn_preWriteClear }, + { 0x04, "hall position mm", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_POSN), PARAM_RW, fn_preWriteClear }, + { 0x05, "position control increment mm",NULL,UI_NONE,&contentbuf,sizeof(PROTOCOL_POSN_INCR), PARAM_RW, fn_preWriteClear }, + { 0x06, "position control mm", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_POSN_DATA), PARAM_RW, fn_preWriteClear }, + { 0x07, "hall position steps", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_POSN), PARAM_RW, fn_preWriteClear }, + { 0x08, "electrical measurements", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_ELECTRICAL_PARAMS), PARAM_R, NULL }, + { 0x09, "enable motors", NULL, UI_CHAR, &contentbuf, sizeof(uint8_t), PARAM_RW, fn_preWriteClear }, + { 0x0A, "disable poweroff timer", NULL, UI_CHAR, &contentbuf, sizeof(uint8_t), PARAM_RW, fn_preWriteClear }, + { 0x0B, "enable console logs", NULL, UI_CHAR, &contentbuf, sizeof(uint8_t ), PARAM_RW, fn_preWriteClear }, + { 0x0C, "read/clear xyt position", NULL, UI_3LONG, &contentbuf, sizeof(PROTOCOL_INTEGER_XYT_POSN), PARAM_RW, fn_preWriteClear }, + { 0x0D, "PWM control", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_PWM_DATA), PARAM_RW, fn_preWriteClear }, + { 0x0E, "simpler PWM", NULL, UI_2LONG, &contentbuf, sizeof( ((PROTOCOL_PWM_DATA *)0)->pwm), PARAM_RW, fn_preWriteClear }, + { 0x21, "buzzer", NULL, UI_NONE, &contentbuf, sizeof(PROTOCOL_BUZZER_DATA), PARAM_RW, fn_preWriteClear }, + + // Flash Storage + { 0x80, "flash magic", "m", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, // write this with CURRENT_MAGIC to commit to flash + + { 0x81, "posn kp x 100", "pkp", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, + { 0x82, "posn ki x 100", "pki", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, // pid params for Position + { 0x83, "posn kd x 100", "pkd", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, + { 0x84, "posn pwm lim", "pl", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, // e.g. 200 + + { 0x85, "speed kp x 100", "skp", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, + { 0x86, "speed ki x 100", "ski", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, // pid params for Speed + { 0x87, "speed kd x 100", "skd", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, + { 0x88, "speed pwm incr lim", "sl", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, // e.g. 20 + { 0x89, "max current limit x 100", "cl", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear }, // by default 1500 (=15 amps), limited by DC_CUR_LIMIT + { 0xA0, "hoverboard enable", "he", UI_SHORT, &contentbuf, sizeof(short), PARAM_RW, fn_preWriteClear } // e.g. 20 +}; + + +///////////////////////////////////////////// +// Set entry in params +int setParam(PARAMSTAT *param) { + + if(param == NULL) return 1; // Failure, got NULL pointer + + // Check if len can actually be received + if( param->len > sizeof( ((PROTOCOL_BYTES_WRITEVALS *)0)->content ) ) { + return 1; // Too long, Failure + } + + if( param->code < (sizeof(params)/sizeof(params[0])) ) { + params[param->code] = param; + return 0; // Successfully assigned + } + + return 1; // Failure, index too big. +} + +int setParams( PARAMSTAT params[], int len) { + int error = 0; + for (int i = 0; i < len; i++) { + error += setParam(¶ms[i]); + } + return error; +} + +///////////////////////////////////////////// +// Change variable at runtime +int setParamVariable(unsigned char code, char ui_type, void *ptr, int len, char rw) { + + // Check if len can actually be received + if( len > sizeof( ((PROTOCOL_BYTES_WRITEVALS *)0)->content ) ) { + return 1; // Too long, Failure + } + + if( code < (sizeof(params)/sizeof(params[0])) ) { + if(params[code] != NULL) { + params[code]->ui_type = ui_type; + params[code]->ptr = ptr; + params[code]->len = len; + params[code]->rw = rw; + return 0; // Success + } + } + return 1; // Not found, Failure +} + +///////////////////////////////////////////// +// Register new function handler at runtime +int setParamHandler(unsigned char code, PARAMSTAT_FN callback) { + + if( code < (sizeof(params)/sizeof(params[0])) ) { + if(params[code] == NULL) return 1; + params[code]->fn = callback; + return 0; // Successfully assigned + } + + return 1; // Failure, index too big. +} + +///////////////////////////////////////////// +// get param function handler +PARAMSTAT_FN getParamHandler(unsigned char code) { + + if( code < (sizeof(params)/sizeof(params[0])) ) { + if(params[code] != NULL) { + return params[code]->fn; + } + } + + return NULL; +} + +///////////////////////////////////////////// +// initialize protocol +// called from main.c +int nosend( unsigned char *data, int len ){ return 0; }; +int protocol_init(PROTOCOL_STAT *s) { + memset(s, 0, sizeof(*s)); + s->timeout1 = 500; + s->timeout2 = 100; + s->allow_ascii = 1; + s->send_serial_data = nosend; + s->send_serial_data_wait = nosend; + + int error = 0; + if (!initialised_functions) { + error += setParams(initialparams, sizeof(initialparams)/sizeof(initialparams[0])); + initialised_functions = 1; + // yes, may be called multiple times, but checks internally. + ascii_init(); + } + + return error; +} + +///////////////////////////////////////////// +// a complete machineprotocol message has been +// received without error +void protocol_process_message(PROTOCOL_STAT *s, PROTOCOL_MSG2 *msg) { + PROTOCOL_BYTES_WRITEVALS *writevals = (PROTOCOL_BYTES_WRITEVALS *) msg->bytes; + + switch (writevals->cmd){ + case PROTOCOL_CMD_READVAL: { + if( writevals->code < (sizeof(params)/sizeof(params[0])) ) { + if(params[writevals->code] != NULL) { + if (params[writevals->code]->fn) params[writevals->code]->fn( s, params[writevals->code], FN_TYPE_PRE_READ, writevals->content, msg->len-2 ); // NOTE: re-uses the msg object (part of stats) + unsigned char *src = params[writevals->code]->ptr; + for (int j = 0; j < params[writevals->code]->len; j++){ + writevals->content[j] = *(src++); + } + msg->len = 1+1+params[writevals->code]->len; // command + code + data len only + writevals->cmd = PROTOCOL_CMD_READVALRESPONSE; // mark as response + // send back with 'read' command plus data like write. + protocol_post(s, msg); + if (params[writevals->code]->fn) params[writevals->code]->fn( s, params[writevals->code], FN_TYPE_POST_READ, writevals->content, msg->len-2 ); + break; + } + } + // parameter code not found + msg->len = 1+1; // cmd + code only + writevals->cmd = PROTOCOL_CMD_READVALRESPONSE; // mark as response + // send back with 'read' command plus data like write. + protocol_post(s, msg); + break; + } + + case PROTOCOL_CMD_READVALRESPONSE: { + if( writevals->code < (sizeof(params)/sizeof(params[0])) ) { + if(params[writevals->code] != NULL) { + if (params[writevals->code]->fn) params[writevals->code]->fn( s, params[writevals->code], FN_TYPE_PRE_READRESPONSE, writevals->content, msg->len-2 ); + + unsigned char *dest = params[writevals->code]->ptr; + // ONLY copy what we have, else we're stuffing random data in. + // e.g. is setting posn, structure is 8 x 4 bytes, + // but we often only want to set the first 8 + for (int j = 0; ((j < params[writevals->code]->len) && (j < (msg->len-2))); j++){ + *(dest++) = writevals->content[j]; + } + if (params[writevals->code]->fn) params[writevals->code]->fn( s, params[writevals->code], FN_TYPE_POST_READRESPONSE, writevals->content, msg->len-2 ); + break; + } + } + // parameter code not found + if(msg->SOM == PROTOCOL_SOM_ACK) { + s->ack.counters.unplausibleresponse++; + } else { + s->noack.counters.unplausibleresponse++; + } + break; + } + + case PROTOCOL_CMD_WRITEVALRESPONSE:{ + if( writevals->code < (sizeof(params)/sizeof(params[0])) ) { + if(params[writevals->code] != NULL) { + break; + } + } + // parameter code not found + if(msg->SOM == PROTOCOL_SOM_ACK) { + s->ack.counters.unplausibleresponse++; + } else { + s->noack.counters.unplausibleresponse++; + } + break; + } + + case PROTOCOL_CMD_WRITEVAL:{ + if( writevals->code < (sizeof(params)/sizeof(params[0])) ) { + if(params[writevals->code] != NULL) { + if (params[writevals->code]->fn) params[writevals->code]->fn( s, params[writevals->code], FN_TYPE_PRE_WRITE, writevals->content, msg->len-2 ); + // NOTE: re-uses the msg object (part of stats) + unsigned char *dest = params[writevals->code]->ptr; + + // ONLY copy what we have, else we're stuffing random data in. + // e.g. is setting posn, structure is 8 x 4 bytes, + // but we often only want to set the first 8 + for (int j = 0; ((j < params[writevals->code]->len) && (j < (msg->len-2))); j++){ + *(dest++) = writevals->content[j]; + } + msg->len = 1+1+1; // cmd+code+'1' only + writevals->cmd = PROTOCOL_CMD_WRITEVALRESPONSE; // mark as response + writevals->content[0] = 1; // say we wrote it + // send back with 'write' command with no data. + protocol_post(s, msg); + if (params[writevals->code]->fn) params[writevals->code]->fn( s, params[writevals->code], FN_TYPE_POST_WRITE, writevals->content, msg->len-2 ); + break; + } + } + // parameter code not found + msg->len = 1+1+1; // cmd +code +'0' only + writevals->cmd = PROTOCOL_CMD_WRITEVALRESPONSE; // mark as response + writevals->content[0] = 0; // say we did not write it + // send back with 'write' command plus data like write. + protocol_post(s, msg); + break; + } + + case PROTOCOL_CMD_REBOOT: + //protocol_send_ack(); // we no longer ack from here + protocol_Delay(500); + protocol_SystemReset(); + break; + + case PROTOCOL_CMD_TEST: + // just send it back! + writevals->cmd = PROTOCOL_CMD_TESTRESPONSE; + // note: original 'bytes' sent back, so leave len as is + protocol_post(s, msg); + // post second immediately to test buffering + // protocol_post(s, msg); + break; + + case PROTOCOL_CMD_UNKNOWN: + // Do nothing, otherwise endless loop is entered. + if(msg->SOM == PROTOCOL_SOM_ACK) { + s->ack.counters.unknowncommands++; + } else { + s->noack.counters.unknowncommands++; + } + break; + + default: + if(msg->SOM == PROTOCOL_SOM_ACK) { + s->ack.counters.unknowncommands++; + } else { + s->noack.counters.unknowncommands++; + } writevals->cmd = PROTOCOL_CMD_UNKNOWN; + msg->len = 1; + protocol_post(s, msg); + break; + } +} diff --git a/src/hbprotocol/protocol.h b/src/hbprotocol/protocol.h new file mode 100644 index 0000000..36b54c9 --- /dev/null +++ b/src/hbprotocol/protocol.h @@ -0,0 +1,417 @@ +/* +* This file is part of the hoverboard-firmware-hack project. +* +* Copyright (C) 2018 Simon Hailes +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//// control structures used in firmware +#pragma pack(push, 4) // all used data types are 4 byte +typedef struct tag_PROTOCOL_POSN_DATA { + // these get set + long wanted_posn_mm[2]; + + // configurations/constants + int posn_max_speed; // max speed in this mode + int posn_min_speed; // minimum speed (to get wheels moving) + + // just so it can be read back + long posn_diff_mm[2]; + long posn_speed_demand[2]; +} PROTOCOL_POSN_DATA; +#pragma pack(pop) + + +#pragma pack(push, 4) // all used data types are 4 byte +typedef struct tag_PROTOCOL_SPEED_DATA { + // these get set + long wanted_speed_mm_per_sec[2]; + + // configurations/constants + int speed_max_power; // max speed in this mode + int speed_min_power; // minimum speed (to get wheels moving) + int speed_minimum_speed; // below this, we don't ask it to do anything + + // just so it can be read back + long speed_diff_mm_per_sec[2]; + long speed_power_demand[2]; +} PROTOCOL_SPEED_DATA; +#pragma pack(pop) + + +#pragma pack(push, 4) // all used data types are 4 byte +typedef struct tag_PROTOCOL_PWM_DATA { + // these get set + long pwm[2]; + + // configurations/constants + int speed_max_power; // max speed in this mode + int speed_min_power; // minimum speed (to get wheels moving) + int speed_minimum_pwm; // below this, we don't ask it to do anything +} PROTOCOL_PWM_DATA; +#pragma pack(pop) + + + +#pragma pack(push, 4) // long and float are 4 byte each +typedef struct tag_PROTOCOL_HALL_DATA_STRUCT{ + long HallPosn; // 90 per revolution + long HallSpeed; // speed part calibrated to speed demand value + + float HallPosnMultiplier; // m per hall segment + + long HallPosn_lastread; // posn offset set via protocol in raw value + long HallPosn_mm; // posn in mm + long HallPosn_mm_lastread; // posn offset set via protocol in mm + long HallSpeed_mm_per_s; // speed in m/s + + unsigned long HallTimeDiff; + unsigned long HallSkipped; +} PROTOCOL_HALL_DATA_STRUCT; +#pragma pack(pop) + +#pragma pack(push, 4) // all used types (float and int) are 4 bytes + +typedef struct tag_PROTOCOL_MOTOR_ELECTRICAL{ + float dcAmps; + float dcAmpsAvgAcc; + float dcAmpsAvg; + int r1; + int r2; + int q; + + int dcAmpsx100; + + int pwm_limiter; + int pwm_requested; + int pwm_actual; + + unsigned int limiter_count; +} PROTOCOL_MOTOR_ELECTRICAL; +#pragma pack(pop) + +#pragma pack(push, 4) // all used types (float and int) are 4 bytes +typedef struct tag_PROTOCOL_ELECTRICAL_PARAMS{ + int bat_raw; + float batteryVoltage; + + int board_temp_raw; + float board_temp_filtered; + float board_temp_deg_c; + + int charging; + + int dcCurLim; // amps*100 + int dc_adc_limit; // limit expressed in terms of ADC units. + + PROTOCOL_MOTOR_ELECTRICAL motors[2]; + +} PROTOCOL_ELECTRICAL_PARAMS; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct tag_PROTOCOL_sensor_frame{ + unsigned char header_00; // this byte gets 0x100 (9 bit serial) + short Angle; + short Angle_duplicate; + unsigned char AA_55; + unsigned char Accelleration; + unsigned char Accelleration_duplicate; + short Roll; +} PROTOCOL_SENSOR_FRAME; +#pragma pack(pop) + +#pragma pack(push, 4) // since on 'long' are used, alignment can be optimized for 4 bytes +typedef struct PROTOCOL_INTEGER_XYT_POSN_tag { + long x; + long y; + long degrees; +} PROTOCOL_INTEGER_XYT_POSN; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint8_t buzzerFreq; + uint8_t buzzerPattern; + uint16_t buzzerLen; +} PROTOCOL_BUZZER_DATA; +#pragma pack(pop) + + +#pragma pack(push, 4) // all used data types are 4 byte +typedef struct tag_PROTOCOL_POSN { + long LeftAbsolute; + long RightAbsolute; + long LeftOffset; + long RightOffset; +} PROTOCOL_POSN; +#pragma pack(pop) + +#pragma pack(push, 4) // all used data types are 4 byte +typedef struct tag_PROTOCOL_POSN_INCR { + long Left; + long Right; +} PROTOCOL_POSN_INCR; +#pragma pack(pop) + + +#define CONTROL_TYPE_NONE 0 +#define CONTROL_TYPE_POSITION 1 +#define CONTROL_TYPE_SPEED 2 +#define CONTROL_TYPE_PWM 3 +#define CONTROL_TYPE_MAX 4 + + + + +#pragma pack(push, 1) +typedef struct tag_PROTOCOL_SUBSCRIBEDATA { + char code; // code in protocol to refer to this + unsigned int period; // how often should the information be sent? + int count; // how many messages shall be sent? -1 for infinity + char som; // which SOM shall be used? with or without ACK + unsigned long next_send_time; // last time a message requiring an ACK was sent + +} PROTOCOL_SUBSCRIBEDATA; +#pragma pack(pop) + + +#pragma pack(push, 1) +typedef struct tag_PROTOCOL_MSG2 { + unsigned char SOM; // 0x02 + unsigned char CI; // continuity counter + unsigned char len; // len is len of bytes to follow, NOT including CS + unsigned char bytes[255]; // variable number of data bytes, with a checksum on the end, cmd is first + // checksum such that sum of bytes CI to CS is zero +} PROTOCOL_MSG2; +#pragma pack(pop) + + +////////////////////////////////////////////////////////////////// +// protocol_post uses this structure to store outgoing messages +// until they can be sent. +// messages are stored only as len|data +// SOM, CI, and CS are not included. +#define MACHINE_PROTOCOL_TX_BUFFER_SIZE 1024 +typedef struct tag_MACHINE_PROTOCOL_TX_BUFFER { + volatile unsigned char buff[MACHINE_PROTOCOL_TX_BUFFER_SIZE]; + volatile int head; + volatile int tail; + + // count of buffer overflows + volatile unsigned int overflow; + +} MACHINE_PROTOCOL_TX_BUFFER; + +////////////////////////////////////////////////////////// + + +#pragma pack(push, 4) // all used data types are 4 byte +typedef struct tag_PROTOCOLCOUNT { + unsigned long rx; // Count of received messages (valid CS) + unsigned long rxMissing; // If message IDs went missing.. + unsigned long tx; // Count of sent messages (ACK, NACK and retries do not count) + unsigned int txRetries; // how often were messages resend? + unsigned int txFailed; // TX Messages which couldn't be deliveredr. No retries left. + + unsigned int unwantedacks; // count of unwated ACK messages + unsigned int unwantednacks; // count of unwanted NACK messges + unsigned int unknowncommands; // count of messages with unknown commands + unsigned int unplausibleresponse; // count of unplausible replies +} PROTOCOLCOUNT; +#pragma pack(pop) + + +typedef struct tag_PROTOCOLSTATE { + PROTOCOL_MSG2 curr_send_msg; // transmit message storage + char retries; // number of retries left to send message + int lastTXCI; // CI of last sent message + int lastRXCI; // CI of last received message in ACKed stream + unsigned long last_send_time; // last time a message requiring an ACK was sent + + PROTOCOLCOUNT counters; // Statistical data of the protocol performance + MACHINE_PROTOCOL_TX_BUFFER TxBuffer; // Buffer for Messages to be sent +} PROTOCOLSTATE; + + + +typedef struct tag_PROTOCOL_STAT { + char allow_ascii; // If set to 0, ascii protocol is not used + unsigned long last_tick_time; // last time the tick function was called + char state; // state used in protocol_byte to receive messages + unsigned long last_char_time; // last time a character was received + unsigned char CS; // temporary storage to calculate Checksum + unsigned char count; // index pointing to last received byte + PROTOCOL_MSG2 curr_msg; // received message storage + + char send_state; // message transmission state (ACK_TX_WAITING or IDLE) + + int timeout1; // ACK has to be received in this time + int timeout2; // when receiving a packet, longest time between characters + + int (*send_serial_data)( unsigned char *data, int len ); // Function Pointer to sending function + int (*send_serial_data_wait)( unsigned char *data, int len ); + + PROTOCOL_SUBSCRIBEDATA subscriptions[10]; // Subscriptions to periodic messages + + PROTOCOLSTATE ack; + PROTOCOLSTATE noack; +} PROTOCOL_STAT; + +struct tag_PARAMSTAT; +typedef struct tag_PARAMSTAT PARAMSTAT; + +// NOTE: content can be NULL if len == 0 +typedef void (*PARAMSTAT_FN)( PROTOCOL_STAT *s, PARAMSTAT *param, uint8_t fn_type, unsigned char *content, int len ); + +struct tag_PARAMSTAT { + unsigned char code; // code in protocol to refer to this + char *description; // if non-null, description + char *uistr; // if non-null, used in ascii protocol to adjust with fnum + char ui_type; // only UI_NONE or UI_SHORT + void *ptr; // pointer to value + int len; // length of value + char rw; // PARAM_R or PARAM_RW + + PARAMSTAT_FN fn; // function to handle events +}; + + + +///////////////////////////////////////////////////////// +// command definitions +// ack - no payload +#define PROTOCOL_CMD_ACK 'A' +// nack - no payload +#define PROTOCOL_CMD_NACK 'N' + +// a test command - normal payload - 'Test' +#define PROTOCOL_CMD_TEST 'T' +#define PROTOCOL_CMD_TESTRESPONSE 't' + +// cause unit to restart - no payload +#define PROTOCOL_CMD_REBOOT 'B' + +// response to an unkonwn command - maybe payload +#define PROTOCOL_CMD_UNKNOWN '?' + +#define PROTOCOL_SOM_ACK 2 +#define PROTOCOL_SOM_NOACK 4 +// +///////////////////////////////////////////////////////////////// + +#define PROTOCOL_CMD_READVAL 'R' +#define PROTOCOL_CMD_READVALRESPONSE 'r' +#define PROTOCOL_CMD_WRITEVAL 'W' +#define PROTOCOL_CMD_WRITEVALRESPONSE 'w' + + +/////////////////////////////////////////////////// +// structure used to gather variables we want to read/write. +#define PARAM_R 1 +#define PARAM_RW 3 +/////////////////////////////////////////////////// +// defines for simple variable types. +// generally: +// if first nibble is 1, second nibble is bytecount for single variable. +// if second nibble is 2, second nibble is bytecount for each of two variables. +// etc. +// if 2nd nibble is NOT a power of 2, all bets off -> custom type (note 2^0 = 1) +// these are as yet not implemented.... +#define UI_NONE 0 +#define UI_CHAR 0x11 +#define UI_SHORT 0x12 +#define UI_LONG 0x14 +#define UI_LONGLONG 0x18 +#define UI_2CHAR 0x21 +#define UI_2SHORT 0x22 +#define UI_2LONG 0x24 +#define UI_2LONGLONG 0x28 +#define UI_3LONG 0x34 +// e.g. +// #define UI_8CHAR 0x81 +// #define UI_POSN 0x03 - custom structure type. + +#define FN_TYPE_PRE_READ 1 +#define FN_TYPE_POST_READ 2 +#define FN_TYPE_PRE_WRITE 3 +#define FN_TYPE_POST_WRITE 4 +#define FN_TYPE_PRE_READRESPONSE 5 +#define FN_TYPE_POST_READRESPONSE 6 + + + + +////////////////////////////////////////////////////// +// PUBLIC functions +///////////////////////////////////////////////////////// +extern void ascii_add_immediate( unsigned char letter, int (*fn)(PROTOCOL_STAT *s, char byte, char *ascii_out), char* description ); +extern void ascii_add_line_fn( unsigned char letter, int (*fn)(PROTOCOL_STAT *s, char *line, char *ascii_out), char *description ); +extern int ascii_init(); +// Set entry in params +extern int setParam(PARAMSTAT *param); +///////////////////////////////////////////////////////////////// +// Change variable at runtime +extern int setParamVariable(unsigned char code, char ui_type, void *ptr, int len, char rw); +///////////////////////////////////////////////////////////////// +// Register new function handler at runtime +extern int setParamHandler(unsigned char code, PARAMSTAT_FN callback); +///////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////// +// call this with received bytes; normally from main loop +void protocol_byte( PROTOCOL_STAT *s, unsigned char byte ); +///////////////////////////////////////////////////////////////// +// call this schedule a message. CI and Checksum are added +int protocol_post(PROTOCOL_STAT *s, PROTOCOL_MSG2 *msg); +///////////////////////////////////////////////////////////////// +// call this regularly from main.c +void protocol_tick(PROTOCOL_STAT *s); +///////////////////////////////////////////////////////////////// +// initialize protocol +int protocol_init(PROTOCOL_STAT *s); +///////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////// +// PUBLIC variables +////////////////////////////////////////////////////////// + +// used to enable immediate mode (action on keypress) +extern int enable_immediate; +// used to display help +extern PARAMSTAT *params[256]; + + + +/////////////////////////////////////////////////////// +// Function Pointers to system functions +////////////////////////////////////////////////////////// + +// Need to be assigned to functions "real" system fucntions +extern void (*protocol_Delay)(uint32_t Delay); +extern void (*protocol_SystemReset)(void); +extern uint32_t (*protocol_GetTick)(void); + + +#ifdef __cplusplus +} +#endif diff --git a/src/hbprotocol/protocol_private.h b/src/hbprotocol/protocol_private.h new file mode 100644 index 0000000..fd83b76 --- /dev/null +++ b/src/hbprotocol/protocol_private.h @@ -0,0 +1,99 @@ +/* +* This file is part of the hoverboard-firmware-hack project. +* +* Copyright (C) 2018 Simon Hailes +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +#pragma once + +#include +#include "protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +///////////////////////////////////////////////////////////////// +// 'machine' protocol structures and definitions +// +// examples: +// ack - 02 02 41 BD +// nack - 02 02 4E B0 +// test - 02 06 54 54 65 73 74 06 +///////////////////////////////////////////////////////////////// + + +#pragma pack(push, 1) +typedef struct tag_PROTOCOL_LEN_ONWARDS { + unsigned char len; // len is len of ALL bytes to follow, including CS + unsigned char bytes[sizeof( ((PROTOCOL_MSG2 *)0)->bytes )]; // variable number of data bytes, with a checksum on the end, cmd is first +} PROTOCOL_LEN_ONWARDS; +#pragma pack(pop) + +// content of 'bytes' above, for single byte commands +#pragma pack(push, 1) +typedef struct tag_PROTOCOL_BYTES { + unsigned char cmd; // + unsigned char bytes[sizeof( ((PROTOCOL_MSG2 *)0)->bytes ) - sizeof(unsigned char)]; // cmd is part of bytes and needs to be substracted +} PROTOCOL_BYTES; +#pragma pack(pop) + + + +// content of 'bytes' above, for single byte commands +#pragma pack(push, 1) +typedef struct tag_PROTOCOL_BYTES_READVALS { + unsigned char cmd; // 'R' + unsigned char code; // code of value to read +} PROTOCOL_BYTES_READVALS; +#pragma pack(pop) + + +#pragma pack(push, 1) +typedef struct tag_PROTOCOL_BYTES_WRITEVALS { + unsigned char cmd; // 'W' + unsigned char code; // code of value to write + unsigned char content[sizeof( ((PROTOCOL_MSG2 *)0)->bytes ) - sizeof(unsigned char) - sizeof(unsigned char)]; // cmd and code are part of bytes and need to be substracted +} PROTOCOL_BYTES_WRITEVALS; +#pragma pack(pop) + + + + + +///////////////////////////////////////////////////////////////// +// processes ASCII characters +void ascii_byte(PROTOCOL_STAT *s, unsigned char byte ); + +///////////////////////////////////////////////////////////////// +// processes machine protocol messages +void protocol_process_message(PROTOCOL_STAT *s, PROTOCOL_MSG2 *msg); + +///////////////////////////////////////////////////////////////// +// get buffer level +int mpTxQueued(MACHINE_PROTOCOL_TX_BUFFER *buf); + +///////////////////////////////////////////////////////////////// +// callback which can be used for "debugging" +extern void (*debugprint)(const char str[]); + +// get param function handler +PARAMSTAT_FN getParamHandler(unsigned char code); + + +#ifdef __cplusplus +} +#endif