├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── scripts │ ├── install-arduino-core-esp32.sh │ ├── install-arduino-ide.sh │ ├── install-platformio.sh │ └── on-push.sh └── workflows │ └── push.yml ├── .gitignore ├── .travis.yml ├── Documentation.md ├── LICENSE.txt ├── README.md ├── doc └── images │ ├── about_screen.png │ ├── add_rotary_encoder_dialog.png │ ├── add_stepper_config_dialog.png │ ├── add_switch_dialog.png │ ├── compiled_size.png │ ├── connection_setup_example.png │ ├── motor_control_screen.png │ ├── rest_api_doc_screen.png │ ├── server_startup_screen.png │ └── setup_screen.png ├── examples ├── Example1_BasicESPStepperMotorServer │ ├── Example1_BasicESPStepperMotorServer.ino │ └── data │ │ ├── config.json │ │ ├── favicon.ico │ │ ├── img │ │ ├── emergencyStopSwitch.svg │ │ ├── logo.svg │ │ ├── rotaryEncoderWheel.svg │ │ ├── stepper.svg │ │ └── switch.svg │ │ ├── index.html │ │ └── js │ │ └── app.js.gz ├── Example2_StepperMotorServer_with_hardcoded_config │ └── Example2_StepperMotorServer_with_hardcoded_config.ino ├── Example3_StepperMotorServer_with_static_IP_address │ └── Example3_StepperMotorServer_with_static_IP_address.ino └── Example4_StepperMotorServer_with_user_cli_command │ └── Example4_StepperMotorServer_with_user_cli_command.ino ├── library.json ├── library.properties ├── platformio.ini └── src ├── ESPStepperMotorServer.cpp ├── ESPStepperMotorServer.h ├── ESPStepperMotorServer_CLI.cpp ├── ESPStepperMotorServer_CLI.h ├── ESPStepperMotorServer_Configuration.cpp ├── ESPStepperMotorServer_Configuration.h ├── ESPStepperMotorServer_Logger.cpp ├── ESPStepperMotorServer_Logger.h ├── ESPStepperMotorServer_MacroAction.cpp ├── ESPStepperMotorServer_MacroAction.h ├── ESPStepperMotorServer_MotionController.cpp ├── ESPStepperMotorServer_MotionController.h ├── ESPStepperMotorServer_PositionSwitch.cpp ├── ESPStepperMotorServer_PositionSwitch.h ├── ESPStepperMotorServer_RestAPI.cpp ├── ESPStepperMotorServer_RestAPI.h ├── ESPStepperMotorServer_RotaryEncoder.cpp ├── ESPStepperMotorServer_RotaryEncoder.h ├── ESPStepperMotorServer_StepperConfiguration.cpp ├── ESPStepperMotorServer_StepperConfiguration.h ├── ESPStepperMotorServer_WebInterface.cpp └── ESPStepperMotorServer_WebInterface.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [pkerspe] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Environment** 14 | Please describe which IDE you are using (Arduino or PlatformIO) and which Version of the Library you are using. Also Please provide the Library versions of the depency libraries (ESP-FlexyStepper, AsyncWebserver, ArduinoJSON) 15 | 16 | **To Reproduce** 17 | PLease describe the steps to reproduce the behavior: 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. Scroll down to '....' 21 | 4. See error 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | If applicable, add screenshots to help explain your problem. 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/scripts/install-arduino-core-esp32.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export ARDUINO_ESP32_PATH="$ARDUINO_USR_PATH/hardware/espressif/esp32" 4 | if [ ! -d "$ARDUINO_ESP32_PATH" ]; then 5 | echo "Installing ESP32 Arduino Core ..." 6 | script_init_path="$PWD" 7 | mkdir -p "$ARDUINO_USR_PATH/hardware/espressif" 8 | cd "$ARDUINO_USR_PATH/hardware/espressif" 9 | 10 | echo "Installing Python Serial ..." 11 | pip install pyserial > /dev/null 12 | 13 | if [ "$OS_IS_WINDOWS" == "1" ]; then 14 | echo "Installing Python Requests ..." 15 | pip install requests > /dev/null 16 | fi 17 | 18 | if [ "$GITHUB_REPOSITORY" == "espressif/arduino-esp32" ]; then 19 | echo "Linking Core..." 20 | ln -s $GITHUB_WORKSPACE esp32 21 | else 22 | echo "Cloning Core Repository..." 23 | git clone https://github.com/espressif/arduino-esp32.git esp32 > /dev/null 2>&1 24 | fi 25 | 26 | echo "Updating Submodules ..." 27 | cd esp32 28 | git submodule update --init --recursive > /dev/null 2>&1 29 | 30 | echo "Installing Platform Tools ..." 31 | cd tools && python get.py 32 | cd $script_init_path 33 | 34 | echo "ESP32 Arduino has been installed in '$ARDUINO_ESP32_PATH'" 35 | echo "" 36 | fi 37 | -------------------------------------------------------------------------------- /.github/scripts/install-arduino-ide.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #OSTYPE: 'linux-gnu', ARCH: 'x86_64' => linux64 4 | #OSTYPE: 'msys', ARCH: 'x86_64' => win32 5 | #OSTYPE: 'darwin18', ARCH: 'i386' => macos 6 | 7 | OSBITS=$(arch) 8 | if [[ "$OSTYPE" == "linux"* ]]; then 9 | export OS_IS_LINUX="1" 10 | ARCHIVE_FORMAT="tar.xz" 11 | if [[ "$OSBITS" == "i686" ]]; then 12 | OS_NAME="linux32" 13 | elif [[ "$OSBITS" == "x86_64" ]]; then 14 | OS_NAME="linux64" 15 | elif [[ "$OSBITS" == "armv7l" || "$OSBITS" == "aarch64" ]]; then 16 | OS_NAME="linuxarm" 17 | else 18 | OS_NAME="$OSTYPE-$OSBITS" 19 | echo "Unknown OS '$OS_NAME'" 20 | exit 1 21 | fi 22 | elif [[ "$OSTYPE" == "darwin"* ]]; then 23 | export OS_IS_MACOS="1" 24 | ARCHIVE_FORMAT="zip" 25 | OS_NAME="macosx" 26 | elif [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then 27 | export OS_IS_WINDOWS="1" 28 | ARCHIVE_FORMAT="zip" 29 | OS_NAME="windows" 30 | else 31 | OS_NAME="$OSTYPE-$OSBITS" 32 | echo "Unknown OS '$OS_NAME'" 33 | exit 1 34 | fi 35 | export OS_NAME 36 | 37 | ARDUINO_BUILD_DIR="$HOME/.arduino/build.tmp" 38 | ARDUINO_CACHE_DIR="$HOME/.arduino/cache.tmp" 39 | 40 | if [ "$OS_IS_MACOS" == "1" ]; then 41 | export ARDUINO_IDE_PATH="/Applications/Arduino.app/Contents/Java" 42 | export ARDUINO_USR_PATH="$HOME/Documents/Arduino" 43 | elif [ "$OS_IS_WINDOWS" == "1" ]; then 44 | export ARDUINO_IDE_PATH="$HOME/arduino_ide" 45 | export ARDUINO_USR_PATH="$HOME/Documents/Arduino" 46 | else 47 | export ARDUINO_IDE_PATH="$HOME/arduino_ide" 48 | export ARDUINO_USR_PATH="$HOME/Arduino" 49 | fi 50 | 51 | if [ ! -d "$ARDUINO_IDE_PATH" ]; then 52 | echo "Installing Arduino IDE on $OS_NAME ..." 53 | echo "Downloading 'arduino-nightly-$OS_NAME.$ARCHIVE_FORMAT' to 'arduino.$ARCHIVE_FORMAT' ..." 54 | if [ "$OS_IS_LINUX" == "1" ]; then 55 | wget -O "arduino.$ARCHIVE_FORMAT" "https://www.arduino.cc/download.php?f=/arduino-nightly-$OS_NAME.$ARCHIVE_FORMAT" > /dev/null 2>&1 56 | echo "Extracting 'arduino.$ARCHIVE_FORMAT' ..." 57 | tar xf "arduino.$ARCHIVE_FORMAT" > /dev/null 58 | mv arduino-nightly "$ARDUINO_IDE_PATH" 59 | else 60 | curl -o "arduino.$ARCHIVE_FORMAT" -L "https://www.arduino.cc/download.php?f=/arduino-nightly-$OS_NAME.$ARCHIVE_FORMAT" > /dev/null 2>&1 61 | echo "Extracting 'arduino.$ARCHIVE_FORMAT' ..." 62 | unzip "arduino.$ARCHIVE_FORMAT" > /dev/null 63 | if [ "$OS_IS_MACOS" == "1" ]; then 64 | mv "Arduino.app" "/Applications/Arduino.app" 65 | else 66 | mv arduino-nightly "$ARDUINO_IDE_PATH" 67 | fi 68 | fi 69 | rm -rf "arduino.$ARCHIVE_FORMAT" 70 | 71 | mkdir -p "$ARDUINO_USR_PATH/libraries" 72 | mkdir -p "$ARDUINO_USR_PATH/hardware" 73 | 74 | echo "Arduino IDE Installed in '$ARDUINO_IDE_PATH'" 75 | echo "" 76 | fi 77 | 78 | function build_sketch(){ # build_sketch [extra-options] 79 | if [ "$#" -lt 2 ]; then 80 | echo "ERROR: Illegal number of parameters" 81 | echo "USAGE: build_sketch [extra-options]" 82 | return 1 83 | fi 84 | 85 | local fqbn="$1" 86 | local sketch="$2" 87 | local build_flags="$3" 88 | local xtra_opts="$4" 89 | local win_opts="" 90 | if [ "$OS_IS_WINDOWS" == "1" ]; then 91 | local ctags_version="ls \"$ARDUINO_IDE_PATH/tools-builder/ctags/\"" 92 | local preprocessor_version="ls \"$ARDUINO_IDE_PATH/tools-builder/arduino-preprocessor/\"" 93 | win_opts="-prefs=runtime.tools.ctags.path=$ARDUINO_IDE_PATH/tools-builder/ctags/$ctags_version -prefs=runtime.tools.arduino-preprocessor.path=$ARDUINO_IDE_PATH/tools-builder/arduino-preprocessor/$preprocessor_version" 94 | fi 95 | 96 | echo "" 97 | echo "Compiling '\"$(basename "$sketch")\"' ..." 98 | mkdir -p "$ARDUINO_BUILD_DIR" 99 | mkdir -p "$ARDUINO_CACHE_DIR" 100 | "$ARDUINO_IDE_PATH/arduino-builder -compile -logger=human -core-api-version=10810 \ 101 | -fqbn=$fqbn \ 102 | -warnings=\"all\" \ 103 | -tools \"$ARDUINO_IDE_PATH/tools-builder\" \ 104 | -tools \"$ARDUINO_IDE_PATH/tools\" \ 105 | -built-in-libraries \"$ARDUINO_IDE_PATH/libraries\" \ 106 | -hardware \"$ARDUINO_IDE_PATH/hardware\" \ 107 | -hardware \"$ARDUINO_USR_PATH/hardware\" \ 108 | -libraries \"$ARDUINO_USR_PATH/libraries\" \ 109 | -build-cache \"$ARDUINO_CACHE_DIR\" \ 110 | -build-path \"$ARDUINO_BUILD_DIR\" \ 111 | -prefs=compiler.cpp.extra_flags=\"$build_flags\" \ 112 | $win_opts $xtra_opts \"$sketch\"" 113 | } 114 | 115 | function count_sketches() # count_sketches 116 | { 117 | local examples="$1" 118 | rm -rf sketches.txt 119 | if [ ! -d "$examples" ]; then 120 | touch sketches.txt 121 | return 0 122 | fi 123 | local sketches=$(find $examples -name *.ino) 124 | local sketchnum=0 125 | for sketch in $sketches; do 126 | echo "found $sketch" 127 | local sketchdir=$(dirname $sketch) 128 | local sketchdirname=$(basename $sketchdir) 129 | local sketchname=$(basename $sketch) 130 | if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then 131 | continue 132 | fi; 133 | if [[ -f "$sketchdir/.test.skip" ]]; then 134 | continue 135 | fi 136 | echo $sketch >> sketches.txt 137 | sketchnum=($sketchnum + 1) 138 | done 139 | return $sketchnum 140 | } 141 | 142 | function build_sketches() # build_sketches [extra-options] 143 | { 144 | local fqbn=$1 145 | local examples=$2 146 | local chunk_idex=$3 147 | local chunks_num=$4 148 | local xtra_opts=$5 149 | 150 | if [ "$#" -lt 2 ]; then 151 | echo "ERROR: Illegal number of parameters" 152 | echo "USAGE: build_sketches [ ] [extra-options]" 153 | return 1 154 | fi 155 | 156 | if [ "$#" -lt 4 ]; then 157 | chunk_idex="0" 158 | chunks_num="1" 159 | xtra_opts=$3 160 | fi 161 | 162 | if [ "$chunks_num" -le 0 ]; then 163 | echo "ERROR: Chunks count must be positive number" 164 | return 1 165 | fi 166 | if [ "$chunk_idex" -ge "$chunks_num" ]; then 167 | echo "ERROR: Chunk index must be less than chunks count" 168 | return 1 169 | fi 170 | 171 | set +e 172 | count_sketches "$examples" 173 | local sketchcount=$? 174 | set -e 175 | local sketches=$(cat sketches.txt) 176 | rm -rf sketches.txt 177 | 178 | local chunk_size=( $sketchcount / $chunks_num ) 179 | local all_chunks=( $chunks_num * $chunk_size ) 180 | if [ "$all_chunks" -lt "$sketchcount" ]; then 181 | chunk_size=( $chunk_size + 1 ) 182 | fi 183 | 184 | local start_index=( $chunk_idex * $chunk_size ) 185 | if [ "$sketchcount" -le "$start_index" ]; then 186 | echo "Skipping job" 187 | return 0 188 | fi 189 | 190 | local end_index=( $chunk_idex + 1 ) * $chunk_size 191 | if [ "$end_index" -gt "$sketchcount" ]; then 192 | end_index=$sketchcount 193 | fi 194 | 195 | local start_num=( $start_index + 1 ) 196 | echo "Found $sketchcount Sketches"; 197 | echo "Chunk Count : $chunks_num" 198 | echo "Chunk Size : $chunk_size" 199 | echo "Start Sketch: $start_num" 200 | echo "End Sketch : $end_index" 201 | 202 | local sketchnum=0 203 | for sketch in $sketches; do 204 | local sketchdir=$(dirname $sketch) 205 | local sketchdirname=$(basename $sketchdir) 206 | local sketchname=$(basename $sketch) 207 | if [ "${sketchdirname}.ino" != "$sketchname" ] \ 208 | || [ -f "$sketchdir/.test.skip" ]; then 209 | continue 210 | fi 211 | sketchnum=$(($sketchnum + 1)) 212 | if [ "$sketchnum" -le "$start_index" ] \ 213 | || [ "$sketchnum" -gt "$end_index" ]; then 214 | continue 215 | fi 216 | local sketchBuildFlags="" 217 | if [ -f "$sketchdir/.test.build_flags" ]; then 218 | while read line; do 219 | sketchBuildFlags="$sketchBuildFlags $line" 220 | done < "$sketchdir/.test.build_flags" 221 | fi 222 | build_sketch "$fqbn" "$sketch" "$sketchBuildFlags" "$xtra_opts" 223 | local result=$? 224 | if [ $result -ne 0 ]; then 225 | return $result 226 | fi 227 | done 228 | return 0 229 | } 230 | -------------------------------------------------------------------------------- /.github/scripts/install-platformio.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Installing Python Wheel ..." 4 | pip install wheel > /dev/null 2>&1 5 | 6 | echo "Installing PlatformIO ..." 7 | pip install -U platformio > /dev/null 2>&1 8 | 9 | echo "PlatformIO has been installed" 10 | echo "" 11 | 12 | 13 | function build_pio_sketch(){ # build_pio_sketch 14 | if [ "$#" -lt 3 ]; then 15 | echo "ERROR: Illegal number of parameters" 16 | echo "USAGE: build_pio_sketch " 17 | return 1 18 | fi 19 | 20 | local board="$1" 21 | local sketch="$2" 22 | local buildFlags="$3" 23 | local sketch_dir=$(dirname "$sketch") 24 | echo "" 25 | echo "Compiling '"$(basename "$sketch")"' ..." 26 | python -m platformio ci -l '.' --board "$board" "$sketch_dir" --project-option="board_build.partitions = huge_app.csv" --project-option="build_flags=$buildFlags" 27 | } 28 | 29 | function count_sketches() # count_sketches 30 | { 31 | local examples="$1" 32 | rm -rf sketches.txt 33 | if [ ! -d "$examples" ]; then 34 | touch sketches.txt 35 | return 0 36 | fi 37 | local sketches=$(find $examples -name *.ino) 38 | local sketchnum=0 39 | for sketch in $sketches; do 40 | local sketchdir=$(dirname $sketch) 41 | local sketchdirname=$(basename $sketchdir) 42 | local sketchname=$(basename $sketch) 43 | if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then 44 | continue 45 | fi; 46 | if [[ -f "$sketchdir/.test.skip" ]]; then 47 | continue 48 | fi 49 | echo $sketch >> sketches.txt 50 | sketchnum=$(($sketchnum + 1)) 51 | done 52 | return $sketchnum 53 | } 54 | 55 | function build_pio_sketches() # build_pio_sketches 56 | { 57 | if [ "$#" -lt 2 ]; then 58 | echo "ERROR: Illegal number of parameters" 59 | echo "USAGE: build_pio_sketches [ ]" 60 | return 1 61 | fi 62 | 63 | local board=$1 64 | local examples=$2 65 | local chunk_idex=$3 66 | local chunks_num=$4 67 | 68 | if [ "$#" -lt 4 ]; then 69 | chunk_idex="0" 70 | chunks_num="1" 71 | fi 72 | 73 | if [ "$chunks_num" -le 0 ]; then 74 | echo "ERROR: Chunks count must be positive number" 75 | return 1 76 | fi 77 | if [ "$chunk_idex" -ge "$chunks_num" ]; then 78 | echo "ERROR: Chunk index must be less than chunks count" 79 | return 1 80 | fi 81 | 82 | set +e 83 | count_sketches "$examples" 84 | local sketchcount=$? 85 | set -e 86 | local sketches=$(cat sketches.txt) 87 | rm -rf sketches.txt 88 | 89 | local chunk_size=$(( $sketchcount / $chunks_num )) 90 | local all_chunks=$(( $chunks_num * $chunk_size )) 91 | if [ "$all_chunks" -lt "$sketchcount" ]; then 92 | chunk_size=$(( $chunk_size + 1 )) 93 | fi 94 | 95 | local start_index=$(( $chunk_idex * $chunk_size )) 96 | if [ "$sketchcount" -le "$start_index" ]; then 97 | echo "Skipping job" 98 | return 0 99 | fi 100 | 101 | local end_index=$(( $(( $chunk_idex + 1 )) * $chunk_size )) 102 | if [ "$end_index" -gt "$sketchcount" ]; then 103 | end_index=$sketchcount 104 | fi 105 | 106 | local start_num=$(( $start_index + 1 )) 107 | echo "Found $sketchcount Sketches"; 108 | echo "Chunk Count : $chunks_num" 109 | echo "Chunk Size : $chunk_size" 110 | echo "Start Sketch: $start_num" 111 | echo "End Sketch : $end_index" 112 | 113 | local sketchnum=0 114 | for sketch in $sketches; do 115 | local sketchdir=$(dirname $sketch) 116 | local sketchdirname=$(basename $sketchdir) 117 | local sketchname=$(basename $sketch) 118 | if [ "${sketchdirname}.ino" != "$sketchname" ] \ 119 | || [ -f "$sketchdir/.test.skip" ]; then 120 | continue 121 | fi 122 | local sketchBuildFlags="" 123 | if [ -f "$sketchdir/.test.build_flags" ]; then 124 | while read line; do 125 | sketchBuildFlags="$sketchBuildFlags $line" 126 | done < "$sketchdir/.test.build_flags" 127 | fi 128 | sketchnum=$(($sketchnum + 1)) 129 | if [ "$sketchnum" -le "$start_index" ] \ 130 | || [ "$sketchnum" -gt "$end_index" ]; then 131 | continue 132 | fi 133 | build_pio_sketch "$board" "$sketch" "$sketchBuildFlags" 134 | local result=$? 135 | if [ $result -ne 0 ]; then 136 | return $result 137 | fi 138 | done 139 | return 0 140 | } 141 | -------------------------------------------------------------------------------- /.github/scripts/on-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ ! -z "$TRAVIS_BUILD_DIR" ]; then 6 | export GITHUB_WORKSPACE="$TRAVIS_BUILD_DIR" 7 | export GITHUB_REPOSITORY="$TRAVIS_REPO_SLUG" 8 | elif [ -z "$GITHUB_WORKSPACE" ]; then 9 | export GITHUB_WORKSPACE="$PWD" 10 | export GITHUB_REPOSITORY="me-no-dev/ESPAsyncWebServer" 11 | fi 12 | 13 | TARGET_PLATFORM="$1" 14 | CHUNK_INDEX=$2 15 | CHUNKS_CNT=$3 16 | BUILD_PIO=0 17 | if [ "$#" -lt 1 ]; then 18 | TARGET_PLATFORM="esp32" 19 | fi 20 | if [ "$#" -lt 3 ] || [ "$CHUNKS_CNT" -le 0 ]; then 21 | CHUNK_INDEX=0 22 | CHUNKS_CNT=1 23 | elif [ "$CHUNK_INDEX" -gt "$CHUNKS_CNT" ]; then 24 | CHUNK_INDEX=$CHUNKS_CNT 25 | elif [ "$CHUNK_INDEX" -eq "$CHUNKS_CNT" ]; then 26 | BUILD_PIO=1 27 | fi 28 | 29 | if [ "$BUILD_PIO" -eq 0 ]; then 30 | # ArduinoIDE Test 31 | source ./.github/scripts/install-arduino-ide.sh 32 | 33 | echo "Installing ESPAsyncWebserver ..." 34 | git clone https://github.com/me-no-dev/ESPAsyncWebServer "$ARDUINO_USR_PATH/libraries/ESPAsyncWebServer" 35 | 36 | echo "Installing ESP-StepperMotor-Server ..." 37 | cp -rf "$GITHUB_WORKSPACE" "$ARDUINO_USR_PATH/libraries/ESPAsyncWebServer" 38 | 39 | echo "Installing ESP-FlexyStepper ..." 40 | git clone https://github.com/pkerspe/ESP-FlexyStepper "$ARDUINO_USR_PATH/libraries/ESP-FlexyStepper" > /dev/null 2>&1 41 | echo "Installing ArduinoJson ..." 42 | git clone https://github.com/bblanchon/ArduinoJson "$ARDUINO_USR_PATH/libraries/ArduinoJson" > /dev/null 2>&1 43 | 44 | if [[ "$TARGET_PLATFORM" == "esp32" ]]; then 45 | echo "Installing AsyncTCP ..." 46 | git clone https://github.com/me-no-dev/AsyncTCP "$ARDUINO_USR_PATH/libraries/AsyncTCP" > /dev/null 2>&1 47 | FQBN="espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app" 48 | source ./.github/scripts/install-arduino-core-esp32.sh 49 | echo "BUILDING ESP32 EXAMPLES" 50 | else 51 | echo "Installing ESPAsyncTCP ..." 52 | git clone https://github.com/me-no-dev/ESPAsyncTCP "$ARDUINO_USR_PATH/libraries/ESPAsyncTCP" > /dev/null 2>&1 53 | FQBN="esp8266com:esp8266:generic:eesz=4M1M,ip=lm2f" 54 | source ./.github/scripts/install-arduino-core-esp8266.sh 55 | echo "BUILDING ESP8266 EXAMPLES" 56 | fi 57 | build_sketches "$FQBN" "$GITHUB_WORKSPACE/examples" "$CHUNK_INDEX" "$CHUNKS_CNT" 58 | else 59 | # PlatformIO Test 60 | source ./.github/scripts/install-platformio.sh 61 | 62 | python -m platformio lib --storage-dir "$GITHUB_WORKSPACE" install 63 | 64 | echo "Installing ESP-FlexyStepper ..." 65 | python -m platformio lib -g install https://github.com/pkerspe/ESP-FlexyStepper.git > /dev/null 2>&1 66 | echo "Installing ESPAsyncWebserver ..." 67 | python -m platformio lib -g install https://github.com/me-no-dev/ESPAsyncWebServer.git > /dev/null 2>&1 68 | 69 | echo "Installing ArduinoJson ..." 70 | python -m platformio lib -g install https://github.com/bblanchon/ArduinoJson.git > /dev/null 2>&1 71 | if [[ "$TARGET_PLATFORM" == "esp32" ]]; then 72 | BOARD="esp32dev" 73 | echo "Installing AsyncTCP ..." 74 | python -m platformio lib -g install https://github.com/me-no-dev/AsyncTCP.git > /dev/null 2>&1 75 | echo "BUILDING ESP32 EXAMPLES" 76 | else 77 | BOARD="esp12e" 78 | echo "Installing ESPAsyncTCP ..." 79 | python -m platformio lib -g install https://github.com/me-no-dev/ESPAsyncTCP.git > /dev/null 2>&1 80 | echo "BUILDING ESP8266 EXAMPLES" 81 | fi 82 | build_pio_sketches "$BOARD" "$GITHUB_WORKSPACE/examples" 83 | fi 84 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: ESP Async Web Server CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - release/* 8 | pull_request: 9 | 10 | jobs: 11 | 12 | build-arduino: 13 | name: Arduino for ${{ matrix.board }} on ${{ matrix.os }} 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest, windows-latest, macOS-latest] 18 | board: [esp32] 19 | steps: 20 | - uses: actions/checkout@v1 21 | - name: Build Tests 22 | run: bash ./.github/scripts/on-push.sh ${{ matrix.board }} 0 1 23 | 24 | build-pio: 25 | name: PlatformIO for ${{ matrix.board }} on ${{ matrix.os }} 26 | runs-on: ${{ matrix.os }} 27 | strategy: 28 | matrix: 29 | os: [ubuntu-latest, windows-latest, macOS-latest] 30 | board: [esp32] 31 | steps: 32 | - uses: actions/checkout@v1 33 | - name: Build Tests 34 | run: bash ./.github/scripts/on-push.sh ${{ matrix.board }} 1 1 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | 35 | .pio 36 | .pioenvs 37 | .piolibdeps 38 | .travis.yml 39 | .vscode 40 | data 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: python 4 | 5 | os: 6 | - linux 7 | 8 | git: 9 | depth: false 10 | 11 | stages: 12 | - build 13 | 14 | jobs: 15 | include: 16 | 17 | - name: "Build Arduino ESP32" 18 | if: tag IS blank AND (type = pull_request OR (type = push AND branch = master)) 19 | stage: build 20 | script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp32 21 | 22 | - name: "Build Platformio ESP32" 23 | if: tag IS blank AND (type = pull_request OR (type = push AND branch = master)) 24 | stage: build 25 | script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp32 1 1 26 | 27 | notifications: 28 | email: 29 | on_success: change 30 | on_failure: change 31 | -------------------------------------------------------------------------------- /Documentation.md: -------------------------------------------------------------------------------- 1 | # ESP-StepperMotor-Server 2 | 3 | This Library is ment for ESP32 modules for development in the Arduino or PlatfromIO IDE. It allows an easy configuration of an ESP32 based stepper motor control server. 4 | It provides multiple interfaces to configure and control 1-n stepper motors, that are connected to the ESP modules IO pins via stepper driver modules. 5 | Basically every stepper driver that provides an step and direction interface (IO Pins) can be controller with this library. 6 | 7 | The ESP-StepperMotor-Server starts a webserver with a user interface and also a REST API to configure and control stepper motor drivers as well as limit switches (homing and position switches) and software controled emergency shutdown switches. 8 | 9 | Also supports rotary encoders to control the position of connected stepper motors. 10 | 11 | For more details visit the ESP-StepperMotor-Server [github repository](https://github.com/pkerspe/ESP-StepperMotor-Server) and check out the detailed [README.md](https://github.com/pkerspe/ESP-StepperMotor-Server/blob/master/README.md) file. 12 | 13 | Copyright (c) 2019 Paul Kerspe - Licensed under the [MIT license](https://github.com/pkerspe/ESP-StepperMotor-Server/blob/master/LICENSE.txt). -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Paul Kerspe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /doc/images/about_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/about_screen.png -------------------------------------------------------------------------------- /doc/images/add_rotary_encoder_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/add_rotary_encoder_dialog.png -------------------------------------------------------------------------------- /doc/images/add_stepper_config_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/add_stepper_config_dialog.png -------------------------------------------------------------------------------- /doc/images/add_switch_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/add_switch_dialog.png -------------------------------------------------------------------------------- /doc/images/compiled_size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/compiled_size.png -------------------------------------------------------------------------------- /doc/images/connection_setup_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/connection_setup_example.png -------------------------------------------------------------------------------- /doc/images/motor_control_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/motor_control_screen.png -------------------------------------------------------------------------------- /doc/images/rest_api_doc_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/rest_api_doc_screen.png -------------------------------------------------------------------------------- /doc/images/server_startup_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/server_startup_screen.png -------------------------------------------------------------------------------- /doc/images/setup_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/doc/images/setup_screen.png -------------------------------------------------------------------------------- /examples/Example1_BasicESPStepperMotorServer/Example1_BasicESPStepperMotorServer.ino: -------------------------------------------------------------------------------- 1 | // ****************************************************************** 2 | // * Simple example for starting the stepper motor server * 3 | // * Paul Kerspe 31.5.2020 * 4 | // ****************************************************************** 5 | // 6 | // This is the simplest example of how to start the ESP Stepper Motor Server with the Webinterface to perform all setup steps via the Web UI 7 | // 8 | // This library requires that your stepper motor be connected to the ESP32 9 | // using an external driver that has a "Step and Direction" interface. 10 | // 11 | // For all driver boards, it is VERY important that you set the motor 12 | // current before running the example. This is typically done by adjusting 13 | // a potentiometer on the board or using dip switches. 14 | // Read the driver board's documentation to learn how to configure the driver 15 | // 16 | // all you need to do, to get started with this example, is fill in your wifi credentials in lines 26/27, then compile and upload to your ESP32. 17 | // In order to use the Web Interface of the server, you need to upload the contents of the "data" folder in this example to the SPIFFS of your ESP32 18 | // 19 | // for a detailed manual on how to use this library please visit: https://github.com/pkerspe/ESP-StepperMotor-Server/blob/master/README.md 20 | // *********************************************************************** 21 | #include 22 | #include 23 | 24 | ESPStepperMotorServer *stepperMotorServer; 25 | 26 | const char *wifiName= ""; // enter the SSID of the wifi network to connect to 27 | const char *wifiSecret = ""; // enter the password of the the existing wifi network here 28 | 29 | void setup() 30 | { 31 | // start the serial interface with 115200 baud 32 | // IMPORTANT: the following line is important, since the server relies on the serial console for log output 33 | // Do not remove this line! (you can modify the baud rate to your needs though, but keep in mind, that slower baud rates might cause timing issues especially if you set the log level to DEBUG) 34 | Serial.begin(115200); 35 | // now create a new ESPStepperMotorServer instance (this must be done AFTER the Serial interface has been started) 36 | // In this example We create the server instance with all modules activated and log level set to INFO (which is the default, you can also use ESPServerLogLevel_DEBUG to set it to debug instead) 37 | stepperMotorServer = new ESPStepperMotorServer(ESPServerRestApiEnabled | ESPServerWebserverEnabled | ESPServerSerialEnabled, ESPServerLogLevel_INFO); 38 | // connect to an existing WiFi network. Make sure you set the vairables wifiName and wifiSecret to match you SSID and wifi pasword (see above before the setup function) 39 | stepperMotorServer->setWifiCredentials(wifiName, wifiSecret); 40 | stepperMotorServer->setWifiMode(ESPServerWifiModeClient); //start the server as a wifi client (DHCP client of an existing wifi network) 41 | 42 | // NOTE: if you want to start the server in a stand alone mode that opens a wifi access point, then comment out the above two lines and uncomment the following line 43 | // stepperMotorServer->setWifiMode(ESPServerWifiModeAccessPoint); 44 | // you can define the AP name and the password using the following two lines, otherwise the defaults will be used (Name: ESP-StepperMotor-Server, password: Aa123456) 45 | // stepperMotorServer->setAccessPointName(""); 46 | // stepperMotorServer->setAccessPointPassword(""); 47 | 48 | //start the server 49 | stepperMotorServer->start(); 50 | // the server will now connect to the wifi and start the webserver, rest API and serial command line interface. 51 | // check the serial console for more details like the URL of the WebInterface 52 | } 53 | 54 | void loop() 55 | { 56 | //put your code here 57 | } 58 | -------------------------------------------------------------------------------- /examples/Example1_BasicESPStepperMotorServer/data/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "serverConfiguration": { 3 | "port": 80, 4 | "apName": "ESP-StepperMotor-Server", 5 | "apPassword": "ServerPassword", 6 | "wififMode": 2, 7 | "wifiSsid": "", 8 | "wifiPassword": "" 9 | }, 10 | "stepperConfigurations": [ 11 | { 12 | "id": 0, 13 | "stepPin": 32, 14 | "directionPin": 33, 15 | "name": "X-Axis", 16 | "stepsPerRev": 200, 17 | "stepsPerMM": 100, 18 | "microsteppingDivisor": 1, 19 | "rpmLimit": 1000 20 | }, 21 | { 22 | "id": 1, 23 | "stepPin": 22, 24 | "directionPin": 23, 25 | "name": "Y-Axis", 26 | "stepsPerRev": 200, 27 | "stepsPerMM": 100, 28 | "microsteppingDivisor": 1, 29 | "rpmLimit": 1000 30 | } 31 | ], 32 | "switchConfigurations": [ 33 | { 34 | "id": 0, 35 | "ioPin": 14, 36 | "stepperIndex": 0, 37 | "switchType": 64, 38 | "name": "Limit 1 X-Axis", 39 | "switchPosition": -1 40 | }, 41 | { 42 | "id": 1, 43 | "ioPin": 15, 44 | "stepperIndex": 1, 45 | "switchType": 64, 46 | "name": "Limit 2 X-Axis", 47 | "switchPosition": 200000 48 | } 49 | ], 50 | "rotaryEncoderConfigurations": [ 51 | { 52 | "id": 0, 53 | "pinA": 16, 54 | "pinB": 17, 55 | "name": "Encoder X-Axis", 56 | "stepMultiplier": 100, 57 | "stepperIndex": 0 58 | }, 59 | { 60 | "id": 1, 61 | "pinA": 18, 62 | "pinB": 19, 63 | "name": "Encoder Y-Axis", 64 | "stepMultiplier": 100, 65 | "stepperIndex": 1 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /examples/Example1_BasicESPStepperMotorServer/data/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/examples/Example1_BasicESPStepperMotorServer/data/favicon.ico -------------------------------------------------------------------------------- /examples/Example1_BasicESPStepperMotorServer/data/img/emergencyStopSwitch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | STOP 43 | 44 | 45 | -------------------------------------------------------------------------------- /examples/Example1_BasicESPStepperMotorServer/data/img/rotaryEncoderWheel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/Example1_BasicESPStepperMotorServer/data/img/switch.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/Example1_BasicESPStepperMotorServer/data/index.html: -------------------------------------------------------------------------------- 1 | ESP-StepperMotor-Server 0.4.1
2 | -------------------------------------------------------------------------------- /examples/Example1_BasicESPStepperMotorServer/data/js/app.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server/55cbe150b662a49f8718536aa9e634960b7be63d/examples/Example1_BasicESPStepperMotorServer/data/js/app.js.gz -------------------------------------------------------------------------------- /examples/Example2_StepperMotorServer_with_hardcoded_config/Example2_StepperMotorServer_with_hardcoded_config.ino: -------------------------------------------------------------------------------- 1 | // ***************************************************** 2 | // * Example how to configure via code * 3 | // * Paul Kerspe 31.5.2020 * 4 | // ***************************************************** 5 | // 6 | // This example shows how to configure the stepper server via code, to create a hardcoded configuation directly in the firmware. 7 | // this could be useful for example if you want to disable the Webinterface and REST API and solely control the steppers via the command line 8 | // and if you want to bundle a static configuration with the actual firmware to burn to multiple devices 9 | // this example will NOT start the webserver and REST API and will also not connect to a WiFi or open an access point. 10 | // If you intend to create a real server that can be controller via a User Interface or REST API see Example 1. 11 | // For this example you do not need SPIFFS and should not have a config.json uploaded to SPIFFS (since it would be loaded otherwise and possibly interfer with the coded config) 12 | // 13 | // This library requires that your stepper motor be connected to the ESP32 14 | // using an external driver that has a "Step and Direction" interface. 15 | // 16 | // For all driver boards, it is VERY important that you set the motor 17 | // current before running the example. This is typically done by adjusting 18 | // a potentiometer on the board or using dip switches. 19 | // Read the driver board's documentation to learn how to configure the driver 20 | // 21 | // for a detailed manual on how to use this library please visit: https://github.com/pkerspe/ESP-StepperMotor-Server/blob/master/README.md 22 | // *********************************************************************** 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | ESPStepperMotorServer *stepperMotorServer; 30 | ESPStepperMotorServer_StepperConfiguration *stepperConfiguration; 31 | 32 | #define STEP_PIN 16 33 | #define DIRECTION_PIN 17 34 | #define LIMIT_SWITCH_PIN 18 35 | 36 | //this function gets called whenever the stepper reaches the target position / ends the current movement 37 | //it is registerd in the line "stepperConfiguration->getFlexyStepper()->registerTargetPositionReachedCallback(targetPositionReachedCallback);" 38 | void targetPositionReachedCallback(long position) 39 | { 40 | Serial.printf("Stepper reached target position %ld\n", position); 41 | } 42 | 43 | void setup() 44 | { 45 | Serial.begin(115200); 46 | // now create a new ESPStepperMotorServer instance (this must be done AFTER the Serial interface has been started) 47 | // In this example We create the server instance with only the serial command line interface enabled 48 | stepperMotorServer = new ESPStepperMotorServer(ESPServerSerialEnabled, ESPServerLogLevel_DEBUG); 49 | stepperMotorServer->setWifiMode(ESPServerWifiModeDisabled); 50 | 51 | //create a new configuration for a stepper 52 | stepperConfiguration = new ESPStepperMotorServer_StepperConfiguration(STEP_PIN, DIRECTION_PIN); 53 | stepperConfiguration->setDisplayName("X-Axis"); 54 | //configure the step size and microstepping setup of the drive/motor 55 | stepperConfiguration->setMicrostepsPerStep(1); 56 | stepperConfiguration->setStepsPerMM(100); 57 | stepperConfiguration->setStepsPerRev(200); 58 | 59 | //optional: if your stepper has a physical brake, configure the IO Pin of the brake and the behaviour 60 | // stepperConfiguration->setBrakeIoPin(20, SWITCHTYPE_STATE_ACTIVE_HIGH_BIT); //set the ESP Pin number where the brake control is connected to an confifgure as active high (brake is engaged when pin goes high) 61 | // stepperConfiguration->setBrakeEngageDelayMs(0); //instantly engage the brake after the stepper stops 62 | // stepperConfiguration->setBrakeReleaseDelayMs(-1); //-1 = never reease the brake unless stepper moves 63 | 64 | //OPTIONAL: register a callback handler to get informed whenever the stepper reaches the requested target position 65 | stepperConfiguration->getFlexyStepper()->registerTargetPositionReachedCallback(targetPositionReachedCallback); 66 | 67 | // now add the configuration to the server 68 | unsigned int stepperId = stepperMotorServer->addOrUpdateStepper(stepperConfiguration); 69 | 70 | //you can now also add switch and rotary encoder configurations to the server and link them to the steppers id if needed 71 | //here an example for a limit switch connected to the previously created stepper motor configuration (make sure the pin is not floating (use pull up or pull down resistor if needed)) 72 | ESPStepperMotorServer_PositionSwitch *positionSwitch = new ESPStepperMotorServer_PositionSwitch(LIMIT_SWITCH_PIN, stepperId, SWITCHTYPE_LIMITSWITCH_COMBINED_BEGIN_END_BIT, "Limit Switch"); 73 | stepperMotorServer->addOrUpdatePositionSwitch(positionSwitch); 74 | 75 | //start the server 76 | stepperMotorServer->start(); 77 | // check the serial console for more details and to send control signals 78 | } 79 | 80 | void loop() 81 | { 82 | //put your custom code here or use the following code to let the stepper motor go back and forth in an endless loop...how useful is that? :-) 83 | //to move the stepper motor we need to get the ESP FlexyStepper instance for this motor like this: 84 | ESP_FlexyStepper *flexyStepper = stepperConfiguration->getFlexyStepper(); 85 | flexyStepper->moveRelativeInSteps(100); 86 | delay(2000); 87 | flexyStepper->moveRelativeInSteps(-100); 88 | delay(2000); 89 | //for more details on the functions of ESP_FlexyStepper see here: https://github.com/pkerspe/ESP-FlexyStepper (chapter "function overview") 90 | } 91 | -------------------------------------------------------------------------------- /examples/Example3_StepperMotorServer_with_static_IP_address/Example3_StepperMotorServer_with_static_IP_address.ino: -------------------------------------------------------------------------------- 1 | // ***************************************************** 2 | // * Example how to configure static IP address * 3 | // * Paul Kerspe 9.10.2020 * 4 | // ***************************************************** 5 | // 6 | // This example shows how to configure the stepper server with a static IP address, gateway and subnet mask 7 | // you only need to modify lines 16 / 17 to configure your wifi SSID and password then compile and upload to your ESP32 8 | // 9 | // for a detailed manual on how to use this library please visit: https://github.com/pkerspe/ESP-StepperMotor-Server/blob/master/README.md 10 | // *********************************************************************** 11 | #include 12 | #include 13 | 14 | ESPStepperMotorServer *stepperMotorServer; 15 | 16 | const char *wifiName = ""; // enter the SSID of the wifi network to connect to 17 | const char *wifiSecret = ""; // enter the password of the the existing wifi network here 18 | 19 | void setup() 20 | { 21 | // start the serial interface with 115200 baud 22 | // IMPORTANT: the following line is important, since the server relies on the serial console for log output 23 | // Do not remove this line! (you can modify the baud rate to your needs though, but keep in mind, that slower baud rates might cause timing issues especially if you set the log level to DEBUG) 24 | Serial.begin(115200); 25 | // now create a new ESPStepperMotorServer instance (this must be done AFTER the Serial interface has been started) 26 | // In this example We create the server instance with all modules activated and log level set to INFO (which is the default, you can also use ESPServerLogLevel_DEBUG to set it to debug instead) 27 | stepperMotorServer = new ESPStepperMotorServer(ESPServerRestApiEnabled | ESPServerWebserverEnabled | ESPServerSerialEnabled, ESPServerLogLevel_INFO); 28 | // connect to an existing WiFi network. Make sure you set the vairables wifiName and wifiSecret to match you SSID and wifi pasword (see above before the setup function) 29 | stepperMotorServer->setWifiCredentials(wifiName, wifiSecret); 30 | stepperMotorServer->setWifiMode(ESPServerWifiModeClient); //start the server as a wifi client (DHCP client of an existing wifi network) 31 | 32 | // here we set the static IP address: 33 | IPAddress staticIp(192, 168, 178, 49); 34 | IPAddress gatewayIp(192, 168, 178, 1); 35 | IPAddress subnetMask(255, 255, 255, 0); 36 | stepperMotorServer->setStaticIpAddress(staticIp, gatewayIp, subnetMask); 37 | 38 | //start the server 39 | stepperMotorServer->start(); 40 | 41 | // the server will now connect to the wifi and start the webserver, rest API and serial command line interface. 42 | // check the serial console for more details like the URL of the WebInterface 43 | } 44 | 45 | void loop() 46 | { 47 | //put your code here 48 | } 49 | -------------------------------------------------------------------------------- /examples/Example4_StepperMotorServer_with_user_cli_command/Example4_StepperMotorServer_with_user_cli_command.ino: -------------------------------------------------------------------------------- 1 | // ***************************************************** 2 | // * Example how to register a new CLI command * 3 | // * Tobias Ellinghaus 05.12.2023 * 4 | // ***************************************************** 5 | // 6 | // This examples show how to register a custom command for the CLI interface. 7 | // This could be useful if you want to control specific GPIO pins or trigger some other control logic. 8 | // This example is based on the hardcoded config example, so all remarks of that apply here, too. 9 | // 10 | // This library requires that your stepper motor be connected to the ESP32 11 | // using an external driver that has a "Step and Direction" interface. 12 | // 13 | // For all driver boards, it is VERY important that you set the motor 14 | // current before running the example. This is typically done by adjusting 15 | // a potentiometer on the board or using dip switches. 16 | // Read the driver board's documentation to learn how to configure the driver 17 | // 18 | // for a detailed manual on how to use this library please visit: https://github.com/pkerspe/ESP-StepperMotor-Server/blob/master/README.md 19 | // *********************************************************************** 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | ESPStepperMotorServer *stepperMotorServer; 27 | ESPStepperMotorServer_StepperConfiguration *stepperConfiguration; 28 | ESPStepperMotorServer_CLI *cli_handler; 29 | 30 | #define STEP_PIN 16 31 | #define DIRECTION_PIN 17 32 | #define ENABLE_PIN 18 33 | 34 | void toggle_enable(char *cmd, char *args) 35 | { 36 | const int stepperid = cli_handler->getValidStepperIdFromArg(args); 37 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 38 | ESPStepperMotorServer_Logger::logDebugf("%s called for stepper id %i\n", cmd, stepperid); 39 | #endif 40 | if (stepperid > -1) 41 | { 42 | char value[20]; 43 | cli_handler->getParameterValue(args, "v", value); 44 | if (value[0] != '\0') 45 | { 46 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 47 | ESPStepperMotorServer_Logger::logDebugf("enabled called with v = %s\n", value); 48 | #endif 49 | 50 | const int on_off = (String(value).toInt()); 51 | digitalWrite(ENABLE_PIN, on_off ? HIGH : LOW); 52 | 53 | Serial.println(cmd); 54 | } 55 | else 56 | { 57 | Serial.println("error: missing required v parameter"); 58 | } 59 | } 60 | } 61 | 62 | void setup() 63 | { 64 | // configure our custom pin as an output and initialize it to LOW 65 | pinMode(ENABLE_PIN, OUTPUT); 66 | digitalWrite(ENABLE_PIN, LOW); 67 | 68 | Serial.begin(115200); 69 | // now create a new ESPStepperMotorServer instance (this must be done AFTER the Serial interface has been started) 70 | // In this example we create the server instance with only the serial command line interface enabled 71 | stepperMotorServer = new ESPStepperMotorServer(ESPServerSerialEnabled, ESPServerLogLevel_DEBUG); 72 | stepperMotorServer->setWifiMode(ESPServerWifiModeDisabled); 73 | 74 | // register a new CLI command to set the ENABLE pin 75 | cli_handler = stepperMotorServer->getCLIHandler(); 76 | cli_handler->registerNewUserCommand({String("enable"), String("e"), String("set the ENABLE signal of a specific stepper. requires the id of the stepper and also the value. E.g. e=0&v:1 to enable the ENABLE input on the stepper with id 0"), true}, &toggle_enable); 77 | 78 | //create a new configuration for a stepper 79 | stepperConfiguration = new ESPStepperMotorServer_StepperConfiguration(STEP_PIN, DIRECTION_PIN); 80 | stepperConfiguration->setDisplayName("X-Axis"); 81 | //configure the step size and microstepping setup of the drive/motor 82 | stepperConfiguration->setMicrostepsPerStep(1); 83 | stepperConfiguration->setStepsPerMM(100); 84 | stepperConfiguration->setStepsPerRev(200); 85 | 86 | // now add the configuration to the server 87 | unsigned int stepperId = stepperMotorServer->addOrUpdateStepper(stepperConfiguration); 88 | 89 | //start the server 90 | stepperMotorServer->start(); 91 | // check the serial console for more details and to send control signals 92 | } 93 | 94 | void loop() 95 | { 96 | //put your custom code here 97 | } 98 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESP-StepperMotor-Server", 3 | "description": "A stepper motor control server for ESP32 modules that provides a Web UI, a REST API and a serial control interface. Support for limit switches and rotary encoders.", 4 | "keywords": "stepper,motor,http,websocket,webserver,rest,api,serial,controller,server,device-control,esp32", 5 | "authors": { 6 | "name": "Paul Kerspe", 7 | "maintainer": true 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/pkerspe/ESP-StepperMotor-Server.git" 12 | }, 13 | "version": "0.4.12", 14 | "license": "MIT", 15 | "frameworks": "arduino", 16 | "platforms": [ 17 | "espressif32" 18 | ], 19 | "dependencies": [{ 20 | "name": "ESP Async WebServer", 21 | "version": "1.2.3", 22 | "platforms": "espressif32" 23 | }, 24 | { 25 | "name": "ESP-FlexyStepper", 26 | "version": "^1.4.7", 27 | "platforms": "espressif32" 28 | }, 29 | { 30 | "name": "ArduinoJSON", 31 | "version": "6.21.2", 32 | "platforms": "arduino" 33 | }, 34 | { 35 | "name": "SPIFFS", 36 | "version": "2.0.0", 37 | "platforms": "arduino" 38 | } 39 | ], 40 | "examples": [ 41 | "[Ee]xamples/*/*.ino" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP-StepperMotor-Server 2 | version=0.4.12 3 | author=Paul Kerspe 4 | maintainer=Paul Kerspe 5 | sentence=A stepper motor control server for ESP32 with Web UI, REST API and CLI 6 | paragraph=A stepper motor control server for ESP32 modules that provides a Web UI, a REST API and a serial control interface. Support for limit switches and rotary encoders. 7 | category=Device Control 8 | url=https://github.com/pkerspe/ESP-StepperMotor-Server 9 | architectures=esp32,espressif32 10 | includes=ESPStepperMotorServer.h 11 | depends=ArduinoJSON (=6.21.2), ESP Async WebServer, ESP-FlexyStepper (>=1.4.7), SPIFFS 12 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | [env:esp32devServer] 11 | platform = espressif32 12 | board = esp32dev 13 | framework = arduino 14 | lib_deps = ESP-FlexyStepper@^1.4.7 15 | https://github.com/me-no-dev/ESPAsyncWebServer.git ;need this direct link since latest version on platform io registry (1.2.3) has issue with latest espressif version 16 | ArduinoJSON@6.21.2 17 | SPIFFS 18 | ;ESP Async WebServer, 19 | monitor_speed = 115200 20 | monitor_filters = esp32_exception_decoder, default 21 | 22 | ;monitor_port = /dev/cu.SLAB_USBtoUART 23 | ;upload_port = /dev/cu.SLAB_USBtoUART 24 | 25 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer.h: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Header file for ESPStepperMotorServer.cpp * 5 | // * * 6 | // * Copyright (c) Paul Kerspe, 2019 * 7 | // * * 8 | // ****************************************************************** 9 | 10 | // this project is not supposed to replace a controller of a CNC machine but more of a general approach on working with stepper motors 11 | // for a good Arduino/ESP base Gerber compatible controller Project see: 12 | // https://github.com/gnea/grbl 13 | // and for ESP32: https://github.com/bdring/Grbl_Esp32 14 | // currently no G-Code (http://linuxcnc.org/docs/html/gcode.html) parser is implemented, yet it might be part of a future release 15 | // other usefull informaion when connecting your ESP32 board to your driver boards and you are not sure which pins to use: https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ 16 | 17 | // MIT License 18 | // 19 | // Copyright (c) 2019 Paul Kerspe 20 | // 21 | // Permission is hereby granted, free of charge, to any person obtaining a copy 22 | // of this software and associated documentation files (the "Software"), to deal 23 | // in the Software without restriction, including without limitation the rights 24 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | // copies of the Software, and to permit persons to whom the Software is furnished 26 | // to do so, subject to the following conditions: 27 | // 28 | // The above copyright notice and this permission notice shall be included in all 29 | // copies or substantial portions of the Software. 30 | // 31 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | // SOFTWARE. 38 | 39 | #ifndef ESPStepperMotorServer_h 40 | #define ESPStepperMotorServer_h 41 | 42 | #define ESPServerMaxSwitches 10 43 | #define ESPServerSwitchStatusRegisterCount 2 //NOTE: this value must be chosen according to the value of ESPServerMaxSwitches: val = ceil(ESPServerMaxSwitches / 8) 44 | #define ESPServerMaxSteppers 10 45 | #define ESPServerMaxRotaryEncoders 5 46 | #define ESPStepperMotorServer_SwitchDisplayName_MaxLength 20 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #ifndef ESPStepperMotorServer_COMPILE_NO_WEB 54 | #include 55 | #include 56 | #endif 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | 67 | #ifndef ESPStepperMotorServer_COMPILE_NO_WEB 68 | #include 69 | #endif 70 | 71 | #define ESPServerWifiModeDisabled 0 72 | #define ESPServerWifiModeAccessPoint 1 73 | #define ESPServerWifiModeClient 2 74 | 75 | #define ESPServerRestApiEnabled 2 76 | #define ESPServerWebserverEnabled 4 77 | #define ESPServerSerialEnabled 8 78 | 79 | #define ESPServerSwitchType_ActiveHigh 1 80 | #define ESPServerSwitchType_ActiveLow 2 81 | 82 | #define ESPServerSwitchType_HomingSwitchBegin 4 83 | #define ESPServerSwitchType_HomingSwitchEnd 8 84 | #define ESPServerSwitchType_GeneralPositionSwitch 16 85 | #define ESPServerSwitchType_EmergencyStopSwitch 32 86 | 87 | #define ESPStepperHighestAllowedIoPin 50 88 | 89 | //just forward declare class here for compiler, since we have a circular dependency (due to bad api design :-)) 90 | class ESPStepperMotorServer_CLI; 91 | class ESPStepperMotorServer_RestAPI; 92 | class ESPStepperMotorServer_Configuration; 93 | class ESPStepperMotorServer_MotionController; 94 | class ESPStepperMotorServer_MacroAction; 95 | 96 | #ifndef ESPStepperMotorServer_COMPILE_NO_WEB 97 | class ESPStepperMotorServer_WebInterface; 98 | class ESPStepperMotorServer_RestAPI; 99 | #endif 100 | // 101 | // the ESPStepperMotorServer class 102 | // TODO: remove all wifi stuff if not needed using: #if defined(ESPServerWifiModeClient) || defined(ESPServerWifiModeAccessPoint) 103 | class ESPStepperMotorServer 104 | { 105 | friend class ESPStepperMotorServer_MotionController; 106 | 107 | public: 108 | ESPStepperMotorServer(byte serverMode, byte logLevel = ESPServerLogLevel_INFO); 109 | ESPStepperMotorServer(const ESPStepperMotorServer &espStepperMotorServer); 110 | ~ESPStepperMotorServer(); 111 | 112 | #ifndef ESPStepperMotorServer_COMPILE_NO_WEB 113 | void setHttpPort(int portNumber); 114 | void onWebSocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len); 115 | void sendSocketMessageToAllClients(const char *message, size_t len); 116 | #endif 117 | 118 | void setAccessPointName(const char *accessPointSSID); 119 | void setAccessPointPassword(const char *accessPointPassword); 120 | void setWifiCredentials(const char *ssid, const char *pwd); 121 | void setWifiSSID(const char *ssid); 122 | void setWifiPassword(const char *pwd); 123 | void setWifiMode(byte wifiMode); 124 | void setStaticIpAddress(IPAddress staticIP, IPAddress gatewayIP, IPAddress subnetMask, IPAddress dns1 = (uint32_t) 0x00000000, IPAddress dns2 = (uint32_t) 0x00000000); 125 | void printWifiStatus(); 126 | void printCompileSettings(); 127 | int addOrUpdateStepper(ESPStepperMotorServer_StepperConfiguration *stepper, int stepperIndex = -1); 128 | int addOrUpdatePositionSwitch(ESPStepperMotorServer_PositionSwitch *posSwitchToAdd, int switchIndex = -1); 129 | int addOrUpdateRotaryEncoder(ESPStepperMotorServer_RotaryEncoder *rotaryEncoder, int encoderIndex = -1); 130 | void removePositionSwitch(int positionSwitchIndex); 131 | void removeStepper(byte stepperConfigurationIndex); 132 | void removeRotaryEncoder(byte rotaryEncoderConfigurationIndex); 133 | void getFormattedPositionSwitchStatusRegister(byte registerIndex, String &output); 134 | void printPositionSwitchStatus(); 135 | void performEmergencyStop(int stepperIndex = -1); 136 | void revokeEmergencyStop(); 137 | void start(); 138 | void stop(); 139 | byte getPositionSwitchStatus(int positionSwitchIndex); 140 | signed char updateSwitchStatusRegister(); 141 | String getIpAddress(); 142 | ESPStepperMotorServer_Configuration *getCurrentServerConfiguration(); 143 | ESPStepperMotorServer_CLI *getCLIHandler() const; 144 | void requestReboot(String rebootReason); 145 | bool isSPIFFSMounted(); 146 | 147 | //delegator functions only 148 | void setLogLevel(byte); 149 | void getServerStatusAsJsonString(String &statusString); 150 | byte getFirstAvailableConfigurationSlotForRotaryEncoder(); 151 | bool isIoPinUsed(int); 152 | 153 | // 154 | // public member variables 155 | // 156 | const char *defaultConfigurationFilename = "/config.json"; 157 | int wifiClientConnectionTimeoutSeconds = 25; 158 | // a boolean indicating if a position switch that has been configure as emegrency switch, has been triggered 159 | volatile boolean emergencySwitchIsActive = false; 160 | 161 | const char *version = "0.4.7"; 162 | 163 | private: 164 | void scanWifiNetworks(); 165 | void connectToWifiNetwork(); 166 | void startAccessPoint(); 167 | 168 | #ifndef ESPStepperMotorServer_COMPILE_NO_WEB 169 | void startWebserver(); 170 | void registerWebInterfaceUrls(); 171 | bool checkIfGuiExistsInSpiffs(); 172 | bool downloadFileToSpiffs(const char *url, const char *targetPath); 173 | #endif 174 | 175 | void startSPIFFS(); 176 | void printSPIFFSStats(); 177 | int getSPIFFSFreeSpace(); 178 | void printSPIFFSRootFolderContents(); 179 | void setupAllIOPins(); 180 | void setupPositionSwitchIOPin(ESPStepperMotorServer_PositionSwitch *posSwitch); 181 | void setupRotaryEncoderIOPin(ESPStepperMotorServer_RotaryEncoder *rotaryEncoder); 182 | void detachInterruptForPositionSwitch(ESPStepperMotorServer_PositionSwitch *posSwitch); 183 | void detachInterruptForRotaryEncoder(ESPStepperMotorServer_RotaryEncoder *rotaryEncoder); 184 | void detachAllInterrupts(); 185 | void attachAllInterrupts(); 186 | void setPositionSwitchStatus(int positionSwitchIndex, byte status); 187 | 188 | // ISR handling 189 | static void staticPositionSwitchISR(); 190 | static void staticEmergencySwitchISR(); 191 | static void staticLimitSwitchISR_POS_END(); 192 | static void staticLimitSwitchISR_POS_BEGIN(); 193 | static void staticLimitSwitchISR_COMBINED(); 194 | static void staticRotaryEncoderISR(); 195 | 196 | void internalEmergencySwitchISR(); 197 | void internalSwitchISR(byte switchType); 198 | void internalRotaryEncoderISR(); 199 | 200 | // 201 | // private member variables 202 | // 203 | byte enabledServices; 204 | boolean isWebserverEnabled = false; 205 | boolean isRestApiEnabled = false; 206 | boolean isCLIEnabled = false; 207 | boolean isServerStarted = false; 208 | boolean isSPIFFSactive = false; 209 | boolean _isRebootScheduled = false; 210 | 211 | ESPStepperMotorServer_Configuration *serverConfiguration; 212 | 213 | #ifndef ESPStepperMotorServer_COMPILE_NO_WEB 214 | ESPStepperMotorServer_WebInterface *webInterfaceHandler; 215 | ESPStepperMotorServer_RestAPI *restApiHandler; 216 | AsyncWebServer *httpServer; 217 | AsyncWebSocket *webSockerServer; 218 | #endif 219 | 220 | ESPStepperMotorServer_CLI *cliHandler; 221 | ESPStepperMotorServer_MotionController *motionControllerHandler; 222 | static ESPStepperMotorServer *anchor; //used for self-reference in ISR 223 | // the button status register for all configured button switches 224 | volatile byte buttonStatus[ESPServerSwitchStatusRegisterCount] = {0}; 225 | }; 226 | 227 | // ------------------------------------ End --------------------------------- 228 | #endif 229 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_CLI.h: -------------------------------------------------------------------------------- 1 | // ****************************************************************** 2 | // * * 3 | // * Header file for ESPStepperMotorServer_CLI.cpp * 4 | // * * 5 | // * Copyright (c) Paul Kerspe, 2019 * 6 | // * * 7 | // ****************************************************************** 8 | 9 | // MIT License 10 | // 11 | // Copyright (c) 2019 Paul Kerspe 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is furnished 18 | // to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | 31 | #ifndef ESPStepperMotorServer_CLI_h 32 | #define ESPStepperMotorServer_CLI_h 33 | 34 | #define MAX_CLI_CMD_COUNTER 50 35 | #define MAX_CLI_USER_CMD_COUNTER 5 36 | 37 | #include 38 | #include 39 | 40 | //need this forward declaration here due to circular dependency (use in constructor/member variable) 41 | class ESPStepperMotorServer; 42 | 43 | struct commandDetailsStructure 44 | { 45 | String command; 46 | String shortCut; 47 | String description; 48 | bool hasParameters; 49 | }; 50 | 51 | class ESPStepperMotorServer_CLI 52 | { 53 | public: 54 | ESPStepperMotorServer_CLI(ESPStepperMotorServer *serverRef); 55 | ~ESPStepperMotorServer_CLI(); 56 | static void processSerialInput(void *parameter); 57 | void executeCommand(String cmd); 58 | void start(); 59 | void stop(); 60 | void registerNewUserCommand(commandDetailsStructure commandDetails, void (*f)(char *, char *)); 61 | int getValidStepperIdFromArg(char *arg); 62 | void getParameterValue(const char *args, const char *parameterNameToGetValueFor, char *result); 63 | void getUnitWithFallback(char *args, char *unit); 64 | 65 | private: 66 | void cmdHelp(char *cmd, char *args); 67 | void cmdEmergencyStop(char *cmd, char *args); 68 | void cmdRevokeEmergencyStop(char *cmd, char *args); 69 | void cmdGetPosition(char *cmd, char *args); 70 | void cmdGetCurrentVelocity(char *cmd, char *args); 71 | void cmdMoveBy(char *cmd, char *args); 72 | void cmdMoveTo(char *cmd, char *args); 73 | void cmdPrintConfig(char *cmd, char *args); 74 | void cmdRemoveSwitch(char *cmd, char *args); 75 | void cmdReboot(char *cmd, char *args); 76 | void cmdRemoveStepper(char *cmd, char *args); 77 | void cmdRemoveEncoder(char *cmd, char *args); 78 | void cmdStopServer(char *cmd, char *args); 79 | void cmdSwitchStatus(char *cmd, char *args); 80 | void cmdServerStatus(char *cmd, char *args); 81 | void cmdSetLogLevel(char *cmd, char *args); 82 | void cmdSaveConfiguration(char *cmd, char *args); 83 | void cmdSetApName(char *cmd, char *args); 84 | void cmdSetApPassword(char *cmd, char *args); 85 | #ifndef ESPStepperMotorServer_COMPILE_NO_WEB 86 | void cmdSetHttpPort(char *cmd, char *args); 87 | #endif 88 | void cmdSetSSID(char *cmd, char *args); 89 | void cmdSetWifiPassword(char *cmd, char *args); 90 | void registerCommands(); 91 | void registerNewCommand(commandDetailsStructure commandDetails, void (ESPStepperMotorServer_CLI::*f)(char *, char *)); 92 | void setMoveSpeedAccelHelper(ESP_FlexyStepper *flexyStepper, char *args); 93 | 94 | TaskHandle_t xHandle = NULL; 95 | ESPStepperMotorServer *serverRef; 96 | void (ESPStepperMotorServer_CLI::*command_functions[MAX_CLI_CMD_COUNTER + 1])(char *, char *); 97 | void (*user_command_functions[MAX_CLI_USER_CMD_COUNTER + 1])(char *, char *); 98 | //const char *command_details[MAX_CLI_CMD_COUNTER +1 ][4]; 99 | commandDetailsStructure allRegisteredCommands[MAX_CLI_CMD_COUNTER + 1]; 100 | commandDetailsStructure allRegisteredUserCommands[MAX_CLI_USER_CMD_COUNTER + 1]; 101 | unsigned int commandCounter = 0; 102 | unsigned int userCommandCounter = 0; 103 | 104 | const char *_CMD_PARAM_SEPRATOR = "="; 105 | const char *_PARAM_PARAM_SEPRATOR = "&"; 106 | const char *_PARAM_VALUE_SEPRATOR = ":"; 107 | }; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_Configuration.h: -------------------------------------------------------------------------------- 1 | 2 | // ****************************************************************** 3 | // * * 4 | // * Header file for ESPStepperMotorServer_Configuration.cpp * 5 | // * * 6 | // * Copyright (c) Paul Kerspe, 2019 * 7 | // * * 8 | // ****************************************************************** 9 | 10 | // this class repesents the complete configuration object and provide 11 | // helper functions to persist and load the configuration form the SPIFFS of the ESP 12 | 13 | // MIT License 14 | // 15 | // Copyright (c) 2019 Paul Kerspe 16 | // 17 | // Permission is hereby granted, free of charge, to any person obtaining a copy 18 | // of this software and associated documentation files (the "Software"), to deal 19 | // in the Software without restriction, including without limitation the rights 20 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | // copies of the Software, and to permit persons to whom the Software is furnished 22 | // to do so, subject to the following conditions: 23 | // 24 | // The above copyright notice and this permission notice shall be included in all 25 | // copies or substantial portions of the Software. 26 | // 27 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | // SOFTWARE. 34 | 35 | #ifndef ESPStepperMotorServer_Configuration_h 36 | #define ESPStepperMotorServer_Configuration_h 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | // TODO: get IO pin configration rules implemented and helper to provide information about which pins to use and which not 48 | // https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ 49 | // Input only pins 50 | // GPIOs 34 to 39 are GPIs – input only pins. These pins don’t have internal pull-ups or pull-down resistors. They can’t be used as outputs, so use these pins only as inputs: 51 | // GPIO 34, GPIO 35, GPIO 36, GPIO 39 52 | // GPIO 12 boot fail if pulled high, better not use as active low 53 | 54 | #define DEFAULT_SERVER_PORT 80 55 | #define DEFAULT_WIFI_MODE 1 56 | 57 | class ESPStepperMotorServer_PositionSwitch; 58 | // 59 | // the ESPStepperMotorServer_Configuration class 60 | class ESPStepperMotorServer_Configuration 61 | { 62 | friend class ESPStepperMotorServer; 63 | friend class ESPStepperMotorServer_RestAPI; 64 | 65 | public: 66 | ESPStepperMotorServer_Configuration(const char *configFilePath, bool isSPIFFSactive); 67 | String getCurrentConfigurationAsJSONString(bool prettyPrint = true, bool includePasswords = false); 68 | unsigned int calculateRequiredJsonDocumentSizeForCurrentConfiguration(); 69 | void printCurrentConfigurationAsJsonToSerial(); 70 | bool saveCurrentConfiguationToSpiffs(String filename = ""); 71 | bool loadConfiguationFromSpiffs(String filename = ""); 72 | void serializeServerConfiguration(JsonDocument &doc, bool includePasswords = false); 73 | 74 | byte addStepperConfiguration(ESPStepperMotorServer_StepperConfiguration *stepperConfig); 75 | byte addSwitch(ESPStepperMotorServer_PositionSwitch *positionSwitch); 76 | byte addRotaryEncoder(ESPStepperMotorServer_RotaryEncoder *encoder); 77 | void setStepperConfiguration(ESPStepperMotorServer_StepperConfiguration *stepperConfig, byte id); 78 | void setSwitch(ESPStepperMotorServer_PositionSwitch *positionSwitch, byte id); 79 | void setRotaryEncoder(ESPStepperMotorServer_RotaryEncoder *encoder, byte id); 80 | void removeStepperConfiguration(byte id); 81 | void removeSwitch(byte id); 82 | 83 | void removeRotaryEncoder(byte id); 84 | ESPStepperMotorServer_StepperConfiguration *getStepperConfiguration(unsigned char id); 85 | ESPStepperMotorServer_PositionSwitch *getSwitch(byte id); 86 | ESPStepperMotorServer_PositionSwitch *getFirstConfiguredLimitSwitchForStepper(unsigned char id); 87 | ESPStepperMotorServer_RotaryEncoder *getRotaryEncoder(unsigned char id); 88 | ESP_FlexyStepper **getConfiguredFlexySteppers(); 89 | // a cache containing all IO pins that are used by switches. The indexes matches the indexes in the configuredSwitches (=switch ID) 90 | // -1 is used to indicate an emtpy array slot 91 | signed char allSwitchIoPins[ESPServerMaxSwitches]; 92 | int serverPort = DEFAULT_SERVER_PORT; 93 | int wifiMode = 1; 94 | const char *apName = "ESPStepperMotorServer"; 95 | const char *apPassword = "Aa123456"; 96 | const char *wifiSsid = "undefined"; 97 | const char *wifiPassword = "undefined"; 98 | int motionControllerCpuCore = 0; 99 | 100 | IPAddress staticIP; 101 | IPAddress gatewayIP; 102 | IPAddress subnetMask; 103 | IPAddress dns1IP; 104 | IPAddress dns2IP; 105 | 106 | //this "cache" should not be private since we need to use it in the ISRs and any getter to retrieve it would slow down processing 107 | ESPStepperMotorServer_PositionSwitch *configuredEmergencySwitches[ESPServerMaxSwitches] = {NULL}; 108 | 109 | private: 110 | // 111 | // private member variables 112 | // 113 | bool isCurrentConfigurationSaved = false; 114 | const char *_configFilePath; 115 | bool _isSPIFFSactive = false; 116 | 117 | /**** the follwoing variables represent the in-memory configuration settings *******/ 118 | // an array to hold all configured stepper configurations 119 | ESPStepperMotorServer_StepperConfiguration *configuredSteppers[ESPServerMaxSteppers] = {NULL}; 120 | 121 | // this is a shortcut/cache for all configured flexy stepper instances, yet it will not have the same indexes as the configuredSteppers, 122 | // but solely an array that is filled from the beginnnig without emtpy slots. 123 | // it is used to have a quick access to configured flexy steppers in time critical functions 124 | void updateConfiguredFlexyStepperCache(void); 125 | ESP_FlexyStepper *configuredFlexySteppers[ESPServerMaxSteppers] = {NULL}; 126 | // an array to hold all configured switches 127 | ESPStepperMotorServer_PositionSwitch *allConfiguredSwitches[ESPServerMaxSwitches] = {NULL}; 128 | // update the caches for emergency and limit switches 129 | void updateSwitchCaches(); 130 | ESPStepperMotorServer_PositionSwitch *configuredLimitSwitches[ESPServerMaxSwitches] = {NULL}; 131 | // an array to hold all configured rotary encoders 132 | ESPStepperMotorServer_RotaryEncoder *configuredRotaryEncoders[ESPServerMaxRotaryEncoders] = {NULL}; 133 | 134 | ///////////////////////////////////////////////////// 135 | // CONSTANTS FOR JSON CONFIGURATION PROPERTY NAMES // 136 | ///////////////////////////////////////////////////// 137 | // GENERAL SERVER CONFIGURATION // 138 | const char *JSON_SECTION_NAME_SERVER_CONFIGURATION = "serverConfiguration"; 139 | const char *JSON_PROPERTY_NAME_PORT_NUMBER = "port"; 140 | const char *JSON_PROPERTY_NAME_WIFI_MODE = "wififMode"; //allowed values are 0 (wifi off = ESPServerWifiModeDisabled),1 (AP mode = ESPServerWifiModeAccessPoint) and 2 (client mode = ESPServerWifiModeClient) 141 | const char *JSON_PROPERTY_NAME_WIFI_SSID = "wifiSsid"; 142 | const char *JSON_PROPERTY_NAME_WIFI_PASSWORD = "wifiPassword"; 143 | const char *JSON_PROPERTY_NAME_WIFI_AP_NAME = "apName"; 144 | const char *JSON_PROPERTY_NAME_WIFI_AP_PASSWORD = "apPassword"; 145 | const char *JSON_PROPERTY_NAME_CPUCORE_FOR_MOTIONCONTROLLER_SERVICE = "motionControllerCpuCore"; 146 | 147 | //for static IP settings 148 | const char *JSON_PROPERTY_NAME_WIFI_STATIC_IP_ADDRESS = "staticIP"; 149 | const char *JSON_PROPERTY_NAME_WIFI_STATIC_IP_GATEWAY = "gatewayIP"; 150 | const char *JSON_PROPERTY_NAME_WIFI_STATIC_IP_SUBNETMASK = "subnetMask"; 151 | const char *JSON_PROPERTY_NAME_WIFI_STATIC_IP_DNS1 = "dns1IP"; 152 | const char *JSON_PROPERTY_NAME_WIFI_STATIC_IP_DNS2 = "dns2IP"; 153 | 154 | 155 | // STEPPER SPECIFIC CONFIGURATION // 156 | const char *JSON_SECTION_NAME_STEPPER_CONFIGURATIONS = "stepperConfigurations"; 157 | 158 | // SWITCH SPECIFIC CONFIGURATION // 159 | const char *JSON_SECTION_NAME_SWITCH_CONFIGURATIONS = "switchConfigurations"; 160 | const char *JSON_SECTION_NAME_SWITCH_CONFIGURATION_MACROACTIONS = "macroActions"; 161 | 162 | // ROTARY ENCODER SPECIFIC CONFIGURATION // 163 | const char *JSON_SECTION_NAME_ROTARY_ENCODER_CONFIGURATIONS = "rotaryEncoderConfigurations"; 164 | }; 165 | #endif 166 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_Logger.cpp: -------------------------------------------------------------------------------- 1 | // ******************************************************************* 2 | // * * 3 | // * ESP8266 and ESP32 Stepper Motor Server - Logging class * 4 | // * Copyright (c) Paul Kerspe, 2019 * 5 | // * * 6 | // ******************************************************************* 7 | // 8 | // MIT License 9 | // 10 | // Copyright (c) 2019 Paul Kerspe 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is furnished 17 | // to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in all 20 | // copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | // SOFTWARE. 29 | // 30 | 31 | #include 32 | 33 | byte ESPStepperMotorServer_Logger::_logLevel = ESPServerLogLevel_INFO; 34 | bool ESPStepperMotorServer_Logger::_isDebugLevelSet = false; 35 | 36 | const char *LEVEL_STRING_ALL = "ALL"; 37 | const char *LEVEL_STRING_DEBUG = "DEBUG"; 38 | const char *LEVEL_STRING_INFO = "INFO"; 39 | const char *LEVEL_STRING_WARNING = "WARNING"; 40 | 41 | ESPStepperMotorServer_Logger::ESPStepperMotorServer_Logger(String loggerName) 42 | { 43 | this->_loggerName = loggerName; 44 | } 45 | 46 | ESPStepperMotorServer_Logger::ESPStepperMotorServer_Logger() 47 | { 48 | this->_loggerName = "root"; 49 | } 50 | 51 | void ESPStepperMotorServer_Logger::printBinaryWithLeadingZeros(char *result, byte var) 52 | { 53 | int charIndex = 0; 54 | for (byte test = 0x80; test; test >>= 1) 55 | { 56 | result[charIndex] = (var & test) ? '1' : '0'; 57 | charIndex++; 58 | // Serial.write(var & test ? '1' : '0'); 59 | } 60 | result[charIndex] = '\0'; 61 | } 62 | 63 | void ESPStepperMotorServer_Logger::setLogLevel(byte logLevel) 64 | { 65 | ESPStepperMotorServer_Logger::_isDebugLevelSet = false; 66 | const char *msgTemplate = "Setting log level to %s\n"; 67 | switch (logLevel) 68 | { 69 | case ESPServerLogLevel_ALL: 70 | ESPStepperMotorServer_Logger::logInfof(msgTemplate, LEVEL_STRING_ALL); 71 | ESPStepperMotorServer_Logger::_isDebugLevelSet = true; 72 | break; 73 | case ESPServerLogLevel_DEBUG: 74 | ESPStepperMotorServer_Logger::logInfof(msgTemplate, LEVEL_STRING_DEBUG); 75 | ESPStepperMotorServer_Logger::_isDebugLevelSet = true; 76 | break; 77 | case ESPServerLogLevel_INFO: 78 | ESPStepperMotorServer_Logger::logInfof(msgTemplate, LEVEL_STRING_INFO); 79 | break; 80 | case ESPServerLogLevel_WARNING: 81 | ESPStepperMotorServer_Logger::logInfof(msgTemplate, LEVEL_STRING_WARNING); 82 | break; 83 | default: 84 | ESPStepperMotorServer_Logger::logWarning("Invalid log level given, log level will be set to info"); 85 | ESPStepperMotorServer_Logger::_logLevel = ESPServerLogLevel_INFO; 86 | return; 87 | } 88 | ESPStepperMotorServer_Logger::_logLevel = logLevel; 89 | } 90 | 91 | byte ESPStepperMotorServer_Logger::getLogLevel() 92 | { 93 | return ESPStepperMotorServer_Logger::_logLevel; 94 | } 95 | 96 | void ESPStepperMotorServer_Logger::logf(const char *level, const char *format, va_list args) 97 | { 98 | char buf[1000]; 99 | vsnprintf(buf, sizeof(buf), format, args); 100 | ESPStepperMotorServer_Logger::log(level, buf, false, false); 101 | } 102 | 103 | void ESPStepperMotorServer_Logger::log(const char *level, const char *msg, boolean newLine, boolean ommitLogLevel) 104 | { 105 | if (!ommitLogLevel) 106 | { 107 | Serial.printf("[%s] ", level); 108 | } 109 | if (newLine == true) 110 | { 111 | Serial.println(msg); 112 | } 113 | else 114 | { 115 | Serial.print(msg); 116 | } 117 | } 118 | 119 | bool ESPStepperMotorServer_Logger::isDebugEnabled() 120 | { 121 | return ESPStepperMotorServer_Logger::_isDebugLevelSet; 122 | } 123 | 124 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 125 | void ESPStepperMotorServer_Logger::logDebug(const char *msg, boolean newLine, boolean ommitLogLevel) 126 | { 127 | if (ESPStepperMotorServer_Logger::_isDebugLevelSet) 128 | { 129 | ESPStepperMotorServer_Logger::log(LEVEL_STRING_DEBUG, msg, newLine, ommitLogLevel); 130 | } 131 | } 132 | 133 | void ESPStepperMotorServer_Logger::logDebugf(const char *format, ...) 134 | { 135 | if (ESPStepperMotorServer_Logger::_isDebugLevelSet) 136 | { 137 | va_list _argumentList; 138 | va_start(_argumentList, format); 139 | ESPStepperMotorServer_Logger::logf(LEVEL_STRING_DEBUG, format, _argumentList); 140 | va_end(_argumentList); 141 | } 142 | } 143 | 144 | void ESPStepperMotorServer_Logger::logDebug(String msg, boolean newLine, boolean ommitLogLevel) 145 | { 146 | if (ESPStepperMotorServer_Logger::_isDebugLevelSet) 147 | { 148 | ESPStepperMotorServer_Logger::logDebug(msg.c_str(), newLine, ommitLogLevel); 149 | } 150 | } 151 | #endif 152 | 153 | void ESPStepperMotorServer_Logger::logInfo(const char *msg, boolean newLine, boolean ommitLogLevel) 154 | { 155 | if (getLogLevel() >= ESPServerLogLevel_INFO) 156 | { 157 | ESPStepperMotorServer_Logger::log(LEVEL_STRING_INFO, msg, newLine, ommitLogLevel); 158 | } 159 | } 160 | void ESPStepperMotorServer_Logger::logInfof(const char *format, ...) 161 | { 162 | if (getLogLevel() >= ESPServerLogLevel_INFO) 163 | { 164 | va_list args; 165 | va_start(args, format); 166 | ESPStepperMotorServer_Logger::logf(LEVEL_STRING_INFO, format, args); 167 | va_end(args); 168 | } 169 | } 170 | 171 | void ESPStepperMotorServer_Logger::logWarning(const char *msg, boolean newLine, boolean ommitLogLevel) 172 | { 173 | ESPStepperMotorServer_Logger::log(LEVEL_STRING_WARNING, msg, newLine, ommitLogLevel); 174 | } 175 | 176 | void ESPStepperMotorServer_Logger::logWarningf(const char *format, ...) 177 | { 178 | va_list args; 179 | va_start(args, format); 180 | ESPStepperMotorServer_Logger::logf(LEVEL_STRING_WARNING, format, args); 181 | va_end(args); 182 | } -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_Logger.h: -------------------------------------------------------------------------------- 1 | // ****************************************************************** 2 | // * * 3 | // * Header file for ESPStepperMotorServer_Logger.cpp * 4 | // * * 5 | // * Copyright (c) Paul Kerspe, 2019 * 6 | // * * 7 | // ****************************************************************** 8 | 9 | // MIT License 10 | // 11 | // Copyright (c) 2019 Paul Kerspe 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is furnished 18 | // to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | 31 | #ifndef ESPStepperMotorServer_Logger_h 32 | #define ESPStepperMotorServer_Logger_h 33 | 34 | #include 35 | 36 | #define ESPServerLogLevel_ALL 4 37 | #define ESPServerLogLevel_DEBUG 3 38 | #define ESPServerLogLevel_INFO 2 39 | #define ESPServerLogLevel_WARNING 1 40 | 41 | // 42 | // the ESPStepperMotorServer_Logger class 43 | class ESPStepperMotorServer_Logger 44 | { 45 | public: 46 | ESPStepperMotorServer_Logger(); 47 | ESPStepperMotorServer_Logger(String logName); 48 | static void setLogLevel(byte); 49 | static byte getLogLevel(void); 50 | static void logDebug(const char *msg, boolean newLine = true, boolean ommitLogLevel = false); 51 | static void logDebugf(const char *format, ...); 52 | static void logDebug(String msg, boolean newLine = true, boolean ommitLogLevel = false); 53 | static void logInfo(const char *msg, boolean newLine = true, boolean ommitLogLevel = false); 54 | static void logInfof(const char *format, ...); 55 | static void logWarning(const char *msg, boolean newLine = true, boolean ommitLogLevel = false); 56 | static void logWarningf(const char *format, ...); 57 | static bool isDebugEnabled(); 58 | 59 | private: 60 | static void log(const char *level, const char *msg, boolean newLine, boolean ommitLogLevel); 61 | static void printBinaryWithLeadingZeros(char *result, byte var); 62 | static void logf(const char *level, const char *format, va_list args); 63 | static byte _logLevel; 64 | static bool _isDebugLevelSet; 65 | String _loggerName; 66 | }; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_MacroAction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ESPStepperMotorServer.h" 3 | 4 | ESPStepperMotorServer_MacroAction::ESPStepperMotorServer_MacroAction(MacroActionType actionType, int val1, long val2) { 5 | this->actionType = actionType; 6 | this->val1 = val1; 7 | this->val2 = val2; 8 | } 9 | 10 | bool ESPStepperMotorServer_MacroAction::execute(ESPStepperMotorServer *serverRef) { 11 | Serial.println("Execute called for MacroAction"); 12 | switch (this->actionType) { 13 | case moveBy: { 14 | ESPStepperMotorServer_StepperConfiguration *stepper = serverRef->getCurrentServerConfiguration()->getStepperConfiguration(this->val1); 15 | if (stepper && stepper->getFlexyStepper()) { 16 | stepper->getFlexyStepper()->moveRelativeInSteps(this->val2); 17 | } 18 | break; 19 | } 20 | case MacroActionType::moveTo: { 21 | ESPStepperMotorServer_StepperConfiguration *stepper = serverRef->getCurrentServerConfiguration()->getStepperConfiguration(this->val1); 22 | if (stepper && stepper->getFlexyStepper()) { 23 | stepper->getFlexyStepper()->setTargetPositionInSteps(this->val2); 24 | } 25 | break; 26 | } 27 | case MacroActionType::releaseEmergencyStop: { 28 | serverRef->revokeEmergencyStop(); 29 | break; 30 | } 31 | case MacroActionType::triggerEmergencyStop: { 32 | serverRef->performEmergencyStop(); 33 | break; 34 | } 35 | case MacroActionType::setAcceleration: { 36 | ESPStepperMotorServer_StepperConfiguration *stepper = serverRef->getCurrentServerConfiguration()->getStepperConfiguration(this->val1); 37 | if (stepper && stepper->getFlexyStepper()) { 38 | stepper->getFlexyStepper()->setAccelerationInStepsPerSecondPerSecond((float)this->val2); 39 | } 40 | break; 41 | } 42 | case MacroActionType::setDeceleration: { 43 | ESPStepperMotorServer_StepperConfiguration *stepper = serverRef->getCurrentServerConfiguration()->getStepperConfiguration(this->val1); 44 | if (stepper && stepper->getFlexyStepper()) { 45 | stepper->getFlexyStepper()->setDecelerationInStepsPerSecondPerSecond((float)this->val2); 46 | } 47 | break; 48 | } 49 | case MacroActionType::setHome: { 50 | ESPStepperMotorServer_StepperConfiguration *stepper = serverRef->getCurrentServerConfiguration()->getStepperConfiguration(this->val1); 51 | if (stepper && stepper->getFlexyStepper()) { 52 | stepper->getFlexyStepper()->setCurrentPositionAsHomeAndStop(); 53 | } 54 | break; 55 | } 56 | case MacroActionType::setLimitA: { 57 | ESPStepperMotorServer_StepperConfiguration *stepper = serverRef->getCurrentServerConfiguration()->getStepperConfiguration(this->val1); 58 | if (stepper && stepper->getFlexyStepper()) { 59 | stepper->getFlexyStepper()->setLimitSwitchActive(ESP_FlexyStepper::LIMIT_SWITCH_BEGIN); 60 | } 61 | break; 62 | } 63 | case MacroActionType::setLimitB: { 64 | ESPStepperMotorServer_StepperConfiguration *stepper = serverRef->getCurrentServerConfiguration()->getStepperConfiguration(this->val1); 65 | if (stepper && stepper->getFlexyStepper()) { 66 | stepper->getFlexyStepper()->setLimitSwitchActive(ESP_FlexyStepper::LIMIT_SWITCH_END); 67 | } 68 | break; 69 | } 70 | case MacroActionType::setSpeed: { 71 | ESPStepperMotorServer_StepperConfiguration *stepper = serverRef->getCurrentServerConfiguration()->getStepperConfiguration(this->val1); 72 | if (stepper && stepper->getFlexyStepper()) { 73 | stepper->getFlexyStepper()->setSpeedInStepsPerSecond(this->val2); 74 | } 75 | break; 76 | } 77 | case MacroActionType::setOutputHigh: 78 | digitalWrite(this->val1, HIGH); 79 | break; 80 | case MacroActionType::setOutputLow: 81 | digitalWrite(this->val1, LOW); 82 | break; 83 | default: 84 | break; 85 | } 86 | return true; 87 | } 88 | 89 | void ESPStepperMotorServer_MacroAction::addSerializedInstanceToJsonArray(JsonArray jsonArray) { 90 | JsonObject nestedMacroAction = jsonArray.createNestedObject(); 91 | nestedMacroAction["type"] = this->actionType; 92 | nestedMacroAction["val1"] = this->val1; 93 | nestedMacroAction["val2"] = this->val2; 94 | } 95 | 96 | ESPStepperMotorServer_MacroAction *ESPStepperMotorServer_MacroAction::fromJsonObject(JsonObject macroActionJson) { 97 | int val1 = macroActionJson["val1"]; 98 | long val2 = (long)macroActionJson["val2"]; 99 | MacroActionType type = macroActionJson["type"]; 100 | return new ESPStepperMotorServer_MacroAction(type, val1, val2 101 | ); 102 | } 103 | 104 | MacroActionType ESPStepperMotorServer_MacroAction::getType(void) { 105 | return this->actionType; 106 | } 107 | 108 | int ESPStepperMotorServer_MacroAction::getVal1(void) { 109 | return this->val1; 110 | } 111 | 112 | long ESPStepperMotorServer_MacroAction::getVal2(void) { 113 | return this->val2; 114 | } 115 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_MacroAction.h: -------------------------------------------------------------------------------- 1 | #ifndef ESPStepperMotorServer_MacroAction_h 2 | #define ESPStepperMotorServer_MacroAction_h 3 | 4 | #include 5 | #include 6 | 7 | class ESPStepperMotorServer; 8 | 9 | enum MacroActionType { 10 | moveTo, moveBy, setSpeed, setAcceleration, setDeceleration, setHome, setLimitA, setLimitB, setOutputHigh, setOutputLow, triggerEmergencyStop, releaseEmergencyStop 11 | }; 12 | 13 | class ESPStepperMotorServer_MacroAction 14 | { 15 | public: 16 | ESPStepperMotorServer_MacroAction(MacroActionType actionType, int val1, long val2 = 0); 17 | bool execute(ESPStepperMotorServer *serverRef); 18 | void addSerializedInstanceToJsonArray(JsonArray jsonArray); 19 | static ESPStepperMotorServer_MacroAction * fromJsonObject(JsonObject macroActionJson); 20 | MacroActionType getType(void); 21 | int getVal1(void); 22 | long getVal2(void); 23 | private: 24 | MacroActionType actionType; 25 | int val1 = 0; 26 | long val2 = 0; 27 | }; 28 | #endif 29 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_MotionController.cpp: -------------------------------------------------------------------------------- 1 | 2 | // ********************************************************* 3 | // * * 4 | // * ESP32 Stepper Motor Server - Motion Controller * 5 | // * * 6 | // * Copyright (c) Paul Kerspe, 2019 * 7 | // * * 8 | // ********************************************************** 9 | 10 | // MIT License 11 | // 12 | // Copyright (c) 2019 Paul Kerspe 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy 15 | // of this software and associated documentation files (the "Software"), to deal 16 | // in the Software without restriction, including without limitation the rights 17 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the Software is furnished 19 | // to do so, subject to the following conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all 22 | // copies or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | // SOFTWARE. 31 | 32 | #include 33 | 34 | // 35 | // constructor for the motion controller module 36 | // creates a freeRTOS Task that runs in the background and triggers the motion updates for the stepper driver 37 | // 38 | ESPStepperMotorServer_MotionController::ESPStepperMotorServer_MotionController(ESPStepperMotorServer *serverRef) 39 | { 40 | this->serverRef = serverRef; 41 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 42 | ESPStepperMotorServer_Logger::logDebug("Motor Controller created"); 43 | #endif 44 | } 45 | 46 | void ESPStepperMotorServer_MotionController::start() 47 | { 48 | if (this->xHandle == NULL) //prevent multiple starts 49 | { 50 | disableCore0WDT(); 51 | xTaskCreate( 52 | ESPStepperMotorServer_MotionController::processMotionUpdates, /* Task function. */ 53 | "MotionControl", /* String with name of task. */ 54 | 10000, /* Stack size in bytes. */ 55 | this, /* Parameter passed as input of the task */ 56 | 2, /* Priority of the task. */ 57 | &this->xHandle); /* Task handle. */ 58 | //esp_task_wdt_delete(this->xHandle); 59 | ESPStepperMotorServer_Logger::logInfo("Motion Controller task started"); 60 | } 61 | } 62 | 63 | void ESPStepperMotorServer_MotionController::processMotionUpdates(void *parameter) 64 | { 65 | ESPStepperMotorServer_MotionController *ref = static_cast(parameter); 66 | ESPStepperMotorServer_Configuration *configuration = ref->serverRef->getCurrentServerConfiguration(); 67 | ESP_FlexyStepper **configuredFlexySteppers = configuration->getConfiguredFlexySteppers(); 68 | bool emergencySwitchFlag = false; 69 | bool allMovementsCompleted = true; 70 | #ifndef ESPStepperMotorServer_COMPILE_NO_WEB 71 | int updateCounter = 0; 72 | #endif 73 | while (true) 74 | { 75 | allMovementsCompleted = true; 76 | //update positions of all steppers / trigger stepping if needed 77 | for (byte i = 0; i < ESPServerMaxSteppers; i++) 78 | { 79 | if (configuredFlexySteppers[i]) 80 | { 81 | if (!configuredFlexySteppers[i]->processMovement()) 82 | { 83 | allMovementsCompleted = false; 84 | } 85 | } 86 | else 87 | { 88 | break; 89 | } 90 | } 91 | 92 | if (allMovementsCompleted && ref->serverRef->_isRebootScheduled) 93 | { 94 | //going for reboot since all motion is stopped and reboot has been requested 95 | Serial.println("Rebooting server now"); 96 | ESP.restart(); 97 | } 98 | 99 | //check for emergency switch 100 | if (ref->serverRef->emergencySwitchIsActive && !emergencySwitchFlag) 101 | { 102 | emergencySwitchFlag = true; 103 | ESPStepperMotorServer_Logger::logInfo("Emergency Switch triggered"); 104 | } 105 | else if (!ref->serverRef->emergencySwitchIsActive && emergencySwitchFlag) 106 | { 107 | emergencySwitchFlag = false; 108 | } 109 | 110 | #ifndef ESPStepperMotorServer_COMPILE_NO_WEB 111 | //check if we should send updated position information via websocket 112 | if (ref->serverRef->isWebserverEnabled) 113 | { 114 | updateCounter++; 115 | //we only send sproadically to reduce load and processing times 116 | if (updateCounter % 200000 == 0 && ref->serverRef->webSockerServer->count() > 0) 117 | { 118 | String positionsString = String("{"); 119 | char segmentBuffer[500]; 120 | bool isFirstSegment = true; 121 | for (byte n = 0; n < ESPServerMaxSteppers; n++) 122 | { 123 | if (configuredFlexySteppers[n]) 124 | { 125 | if (!isFirstSegment) 126 | { 127 | positionsString += ","; 128 | } 129 | sprintf(segmentBuffer, "\"s%ipos\":%ld, \"s%ivel\":%.3f", n, configuredFlexySteppers[n]->getCurrentPositionInSteps(), n, configuredFlexySteppers[n]->getCurrentVelocityInStepsPerSecond()); 130 | //maybe register as friendly class and access property directly and save some processing time 131 | positionsString += segmentBuffer; 132 | isFirstSegment = false; 133 | } 134 | } 135 | positionsString += "}"; 136 | 137 | ref->serverRef->sendSocketMessageToAllClients(positionsString.c_str(), positionsString.length()); 138 | updateCounter = 0; 139 | } 140 | } 141 | #endif 142 | } 143 | } 144 | 145 | void ESPStepperMotorServer_MotionController::stop() 146 | { 147 | vTaskDelete(this->xHandle); 148 | this->xHandle = NULL; 149 | ESPStepperMotorServer_Logger::logInfo("Motion Controller stopped"); 150 | } 151 | 152 | // -------------------------------------- End -------------------------------------- 153 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_MotionController.h: -------------------------------------------------------------------------------- 1 | // ****************************************************************** 2 | // * * 3 | // * Header file for ESPStepperMotorServer_MotionController.cpp * 4 | // * * 5 | // * Copyright (c) Paul Kerspe, 2019 * 6 | // * * 7 | // ****************************************************************** 8 | 9 | // MIT License 10 | // 11 | // Copyright (c) 2019 Paul Kerspe 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is furnished 18 | // to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | 31 | #ifndef ESPStepperMotorServer_MotionController_h 32 | #define ESPStepperMotorServer_MotionController_h 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | class ESPStepperMotorServer; 40 | 41 | class ESPStepperMotorServer_MotionController 42 | { 43 | public: 44 | ESPStepperMotorServer_MotionController(ESPStepperMotorServer *serverRef); 45 | static void processMotionUpdates(void *parameter); 46 | void start(); 47 | void stop(); 48 | 49 | private: 50 | TaskHandle_t xHandle = NULL; 51 | ESPStepperMotorServer *serverRef; 52 | }; 53 | 54 | #endif -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_PositionSwitch.cpp: -------------------------------------------------------------------------------- 1 | // ******************************************************************* 2 | // * * 3 | // * ESP8266 and ESP32 Stepper Motor Server - Position Switch class * 4 | // * Copyright (c) Paul Kerspe, 2019 * 5 | // * * 6 | // ******************************************************************* 7 | // 8 | // MIT License 9 | // 10 | // Copyright (c) 2019 Paul Kerspe 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is furnished 17 | // to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in all 20 | // copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | // SOFTWARE. 29 | // 30 | #include 31 | 32 | int _stepperIndex; // can be -1 for emergency stop switches only 33 | byte _ioPinNumber = 255; 34 | byte _switchType; //bit mask for active state and the general type of switch 35 | String _positionName = ""; 36 | long _switchPosition = -1; 37 | ESPStepperMotorServer_Logger _logger = ESPStepperMotorServer_Logger((String)"ESPStepperMotorServer_PositionSwitch"); 38 | 39 | ESPStepperMotorServer_PositionSwitch::ESPStepperMotorServer_PositionSwitch() 40 | { 41 | } 42 | 43 | ESPStepperMotorServer_PositionSwitch::~ESPStepperMotorServer_PositionSwitch() 44 | { 45 | this->clearMacroActions(); 46 | } 47 | 48 | ESPStepperMotorServer_PositionSwitch::ESPStepperMotorServer_PositionSwitch(byte ioPin, int stepperIndex, byte switchType, String name, long switchPosition) 49 | { 50 | this->_ioPinNumber = ioPin; 51 | this->_stepperIndex = stepperIndex; 52 | this->_switchType = switchType; //this is a bit mask representing the active state (bit 1 and 2) and the general type (homing/limit/position or emergency stop switch) in one byte 53 | this->_positionName = name; 54 | this->_switchPosition = switchPosition; 55 | } 56 | 57 | void ESPStepperMotorServer_PositionSwitch::setId(byte id) 58 | { 59 | this->_switchIndex = id; 60 | } 61 | 62 | /** 63 | * the unique ID of this switch 64 | * NOTE: This ID also matches the array index of the configuration in the allConfiguredSwitches array in the Configuration class 65 | */ 66 | byte ESPStepperMotorServer_PositionSwitch::getId() 67 | { 68 | return this->_switchIndex; 69 | } 70 | 71 | int ESPStepperMotorServer_PositionSwitch::getStepperIndex(void) 72 | { 73 | return this->_stepperIndex; 74 | } 75 | 76 | byte ESPStepperMotorServer_PositionSwitch::getIoPinNumber(void) 77 | { 78 | return this->_ioPinNumber; 79 | } 80 | 81 | byte ESPStepperMotorServer_PositionSwitch::getSwitchType(void) 82 | { 83 | return this->_switchType; 84 | } 85 | 86 | String ESPStepperMotorServer_PositionSwitch::getPositionName(void) 87 | { 88 | return this->_positionName; 89 | } 90 | void ESPStepperMotorServer_PositionSwitch::setPositionName(String name) 91 | { 92 | this->_positionName = name; 93 | } 94 | 95 | long ESPStepperMotorServer_PositionSwitch::getSwitchPosition(void) 96 | { 97 | return this->_switchPosition; 98 | } 99 | void ESPStepperMotorServer_PositionSwitch::setSwitchPosition(long position) 100 | { 101 | this->_switchPosition = position; 102 | } 103 | 104 | bool ESPStepperMotorServer_PositionSwitch::isActiveHigh() 105 | { 106 | return this->isTypeBitSet(SWITCHTYPE_STATE_ACTIVE_HIGH_BIT); 107 | } 108 | 109 | bool ESPStepperMotorServer_PositionSwitch::isEmergencySwitch() 110 | { 111 | return this->isTypeBitSet(SWITCHTYPE_EMERGENCY_STOP_SWITCH_BIT); 112 | } 113 | 114 | bool ESPStepperMotorServer_PositionSwitch::isLimitSwitch() 115 | { 116 | return (this->isTypeBitSet(SWITCHTYPE_LIMITSWITCH_POS_BEGIN_BIT) || this->isTypeBitSet(SWITCHTYPE_LIMITSWITCH_POS_END_BIT) || this->isTypeBitSet(SWITCHTYPE_LIMITSWITCH_COMBINED_BEGIN_END_BIT)); 117 | } 118 | 119 | bool ESPStepperMotorServer_PositionSwitch::isTypeBitSet(byte bitToCheck) 120 | { 121 | return this->_switchType & (1 << (bitToCheck - 1)); 122 | } 123 | 124 | void ESPStepperMotorServer_PositionSwitch::addMacroAction(ESPStepperMotorServer_MacroAction *macroAction) { 125 | this->_macroActions.push_back(macroAction); 126 | } 127 | 128 | std::vector ESPStepperMotorServer_PositionSwitch::getMacroActions() { 129 | return this->_macroActions; 130 | } 131 | 132 | void ESPStepperMotorServer_PositionSwitch::clearMacroActions() { 133 | for(ESPStepperMotorServer_MacroAction *macroAction : this->_macroActions) { 134 | delete(macroAction); 135 | } 136 | this->_macroActions.clear(); 137 | } 138 | 139 | bool ESPStepperMotorServer_PositionSwitch::hasMacroActions(){ 140 | return (this->_macroActions.size() > 0); 141 | } 142 | 143 | int ESPStepperMotorServer_PositionSwitch::serializeMacroActionsToJsonArray(JsonArray macroActionsJsonArray){ 144 | 145 | for(ESPStepperMotorServer_MacroAction *macroAction : this->_macroActions) { 146 | macroAction->addSerializedInstanceToJsonArray(macroActionsJsonArray); 147 | } 148 | return this->_macroActions.size(); 149 | } 150 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_PositionSwitch.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ESPStepperMotorServer_PositionSwitch_h 3 | #define ESPStepperMotorServer_PositionSwitch_h 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define SWITCHTYPE_STATE_ACTIVE_HIGH_BIT 1 12 | #define SWITCHTYPE_STATE_ACTIVE_LOW_BIT 2 13 | #define SWITCHTYPE_LIMITSWITCH_POS_BEGIN_BIT 3 14 | #define SWITCHTYPE_LIMITSWITCH_POS_END_BIT 4 15 | #define SWITCHTYPE_POSITION_SWITCH_BIT 5 16 | #define SWITCHTYPE_EMERGENCY_STOP_SWITCH_BIT 6 17 | #define SWITCHTYPE_LIMITSWITCH_COMBINED_BEGIN_END_BIT 7 18 | 19 | //size calculated using https://arduinojson.org/v6/assistant/ 20 | #define RESERVED_JSON_SIZE_ESPStepperMotorServer_PositionSwitch 170 21 | 22 | class ESPStepperMotorServer_MacroAction; 23 | 24 | class ESPStepperMotorServer_PositionSwitch 25 | { 26 | friend class ESPStepperMotorServer; 27 | public: 28 | ESPStepperMotorServer_PositionSwitch(); 29 | ~ESPStepperMotorServer_PositionSwitch(); 30 | ESPStepperMotorServer_PositionSwitch(byte ioPin, int stepperIndex, byte switchType, String name = "", long switchPosition = 0); 31 | 32 | /** 33 | * setter to set the id of this switch. 34 | * Only use this if you know what you are doing 35 | */ 36 | void setId(byte id); 37 | 38 | /** 39 | * get the id of the switch 40 | */ 41 | byte getId(); 42 | 43 | int getStepperIndex(void); 44 | 45 | byte getIoPinNumber(void); 46 | 47 | /** 48 | * return the type of this switch if set. 49 | * It indicates whether the switch as limit, position or emergency switch 50 | * See constants 51 | * SWITCHTYPE_LIMITSWITCH_POS_BEGIN_BIT 52 | * SWITCHTYPE_LIMITSWITCH_POS_END_BIT 53 | * SWITCHTYPE_LIMITSWITCH_COMBINED_BEGIN_END_BIT 54 | * SWITCHTYPE_POSITION_SWITCH_BIT 55 | * SWITCHTYPE_EMERGENCY_STOP_SWITCH_BIT 56 | */ 57 | byte getSwitchType(void); 58 | 59 | String getPositionName(void); 60 | void setPositionName(String name); 61 | 62 | bool isActiveHigh(); 63 | bool isEmergencySwitch(); 64 | bool isLimitSwitch(); 65 | bool isTypeBitSet(byte bitToCheck); 66 | 67 | long getSwitchPosition(void); 68 | void setSwitchPosition(long position); 69 | 70 | void addMacroAction(ESPStepperMotorServer_MacroAction *macroAction); 71 | std::vector getMacroActions(); 72 | bool hasMacroActions(void); 73 | void clearMacroActions(void); 74 | int serializeMacroActionsToJsonArray(JsonArray macroActionsJsonArray); 75 | 76 | private: 77 | byte _stepperIndex; 78 | byte _switchIndex; 79 | byte _ioPinNumber = 255; 80 | byte _switchType = 0; //this is a bit mask representing the active state (bit 1 and 2) and the general type (homing/limit/position or emergency stop switch) in one byte 81 | String _positionName; 82 | long _switchPosition; 83 | ESPStepperMotorServer_Logger _logger; 84 | std::vector _macroActions; 85 | }; 86 | #endif 87 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_RestAPI.h: -------------------------------------------------------------------------------- 1 | // ****************************************************************** 2 | // * * 3 | // * Header file for ESPStepperMotorServer_RestAPI.cpp * 4 | // * * 5 | // * Copyright (c) Paul Kerspe, 2019 * 6 | // * * 7 | // ****************************************************************** 8 | 9 | // MIT License 10 | // 11 | // Copyright (c) 2019 Paul Kerspe 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is furnished 18 | // to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | 31 | #ifndef ESPStepperMotorServer_RestAPI_h 32 | #define ESPStepperMotorServer_RestAPI_h 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | //just declare class here for compiler, since we have a circular dependency 44 | class ESPStepperMotorServer; 45 | 46 | class ESPStepperMotorServer_RestAPI 47 | { 48 | public: 49 | ESPStepperMotorServer_RestAPI(ESPStepperMotorServer *stepperMotorServer); 50 | /** 51 | * register all rest endpoint handlers with the given ESP AsyncWebServer instance reference 52 | */ 53 | void registerRestEndpoints(AsyncWebServer *server); 54 | 55 | private: 56 | String version; 57 | ESPStepperMotorServer_Logger *logger; 58 | ESPStepperMotorServer *_stepperMotorServer; 59 | void populateStepperDetailsToJsonObject(JsonObject &detailsObjecToPopulate, ESPStepperMotorServer_StepperConfiguration *stepper, int index); 60 | void populateSwitchDetailsToJsonObject(JsonObject &detailsObjecToPopulate, ESPStepperMotorServer_PositionSwitch *positionSwitch, int index); 61 | void populateRotaryEncoderDetailsToJsonObject(JsonObject &detailsObjecToPopulate, ESPStepperMotorServer_RotaryEncoder *rotaryEncoder, int index); 62 | 63 | void logDebugRequestUrl(AsyncWebServerRequest *request); 64 | 65 | //movement related endpoints 66 | void handleHomingRequest(AsyncWebServerRequest *request); 67 | //for other endpoints see ESPStepperMotorServer_RestAPI.cpp in function registerRestEndpoints 68 | 69 | // SWITCH CONFIGURATION ENDPOINT HANDLER 70 | void handlePostSwitchRequest(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total, int switchIndex = -1); 71 | int handleDeleteSwitchRequest(AsyncWebServerRequest *request, boolean sendReponse); 72 | // STEPPER CONFIGURATION ENDPOINT HANDLER 73 | void handlePostStepperRequest(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total, int switchIndex = -1); 74 | int handleDeleteStepperRequest(AsyncWebServerRequest *request, boolean sendReponse); 75 | // ROTARY ENCODER CONFIGURATION ENDPOINT HANDLER 76 | void handlePostRotaryEncoderRequest(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total, int encoderIndex = -1); 77 | int handleDeleteRotaryEncoderRequest(AsyncWebServerRequest *request, boolean sendReponse); 78 | }; 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_RotaryEncoder.cpp: -------------------------------------------------------------------------------- 1 | // ****************************************************************** 2 | // // * 3 | // // Header file for ESPStepperMotorServer_RotaryEncoder.cpp * 4 | // // * 5 | // // Copyright (c) Paul Kerspe, 2019 * 6 | // // * 7 | // ****************************************************************** 8 | // 9 | // This is a class to support rotary encoders a input controllers for the ESPStepperMotorServer 10 | // 11 | // It is based on the work Ben Buxton's Rotary Encodr Library, which was licensed under the following conditions: 12 | // Copyright 2011 Ben Buxton.Licenced under the GNU GPL Version 3. Contact : bb @cactii.net 13 | // Licenced under the GNU GPL Version 3. Contact: bb@cactii.net 14 | // https://github.com/buxtronix/arduino/tree/master/libraries/Rotary 15 | // 16 | // BEGIN OF BENS COMMENT 17 | // A typical mechanical rotary encoder emits a two bit gray code 18 | // on 3 output pins. Every step in the output (often accompanied 19 | // by a physical 'click') generates a specific sequence of output 20 | // codes on the pins. 21 | // There are 3 pins used for the rotary encoding - one common and 22 | // two 'bit' pins. 23 | // The following is the typical sequence of code on the output when 24 | // moving from one step to the next: 25 | // Position Bit1 Bit2 26 | // ---------------------- 27 | // Step1 0 0 28 | // 1/4 1 0 29 | // 1/2 1 1 30 | // 3/4 0 1 31 | // Step2 0 0 32 | // 33 | // From this table, we can see that when moving from one 'click' to 34 | // the next, there are 4 changes in the output code. 35 | // 36 | // - From an initial 0 - 0, Bit1 goes high, Bit0 stays low. 37 | // - Then both bits are high, halfway through the step. 38 | // - Then Bit1 goes low, but Bit2 stays high. 39 | // - Finally at the end of the step, both bits return to 0. 40 | // 41 | // Detecting the direction is easy - the table simply goes in the other 42 | // direction (read up instead of down). 43 | // 44 | // To decode this, we use a simple state machine. Every time the output 45 | // code changes, it follows state, until finally a full steps worth of 46 | // code is received (in the correct order). At the final 0-0, it returns 47 | // a value indicating a step in one direction or the other. 48 | // 49 | // If an invalid state happens (for example we go from '0-1' straight 50 | // to '1-0'), the state machine resets to the start until 0-0 and the 51 | // next valid codes occur. 52 | // 53 | // The biggest advantage of using a state machine over other algorithms 54 | // is that this has inherent debounce built in. Other algorithms emit spurious 55 | // output with switch bounce, but this one will simply flip between 56 | // sub-states until the bounce settles, then continue along the state 57 | // machine. 58 | // A side effect of debounce is that fast rotations can cause steps to 59 | // be skipped. By not requiring debounce, fast rotations can be accurately 60 | // measured. 61 | // Another advantage is the ability to properly handle bad state, such 62 | // as due to EMI, etc. 63 | // It is also a lot simpler than others - a static state table and less 64 | // than 10 lines of logic. 65 | // END OF BENS COMMENT 66 | // 67 | // I included the sources here to reduce the complexity of setting up the required libraries that need to be installed on top of the ESPSMS Library 68 | // by the user in the Arduino UI, since it can not currently deal with automatic dependency management like platformIO. 69 | // Ben's code is beautiful in its simplicy and therefore does not create a noticable overhead in the code size of the ESPStepperMotorServer. 70 | // 71 | // 72 | // ESPStepperMotorServer is licensed under the following conditions: 73 | // 74 | // MIT License 75 | // 76 | // Copyright (c) 2019 Paul Kerspe 77 | // 78 | // Permission is hereby granted, free of charge, to any person obtaining a copy 79 | // of this software and associated documentation files (the "Software"), to deal 80 | // in the Software without restriction, including without limitation the rights 81 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 82 | // copies of the Software, and to permit persons to whom the Software is furnished 83 | // to do so, subject to the following conditions: 84 | // 85 | // The above copyright notice and this permission notice shall be included in all 86 | // copies or substantial portions of the Software. 87 | // 88 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 89 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 90 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 91 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 92 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 93 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 94 | // SOFTWARE. 95 | 96 | #include "Arduino.h" 97 | #include "ESPStepperMotorServer_Logger.h" 98 | #include "ESPStepperMotorServer_RotaryEncoder.h" 99 | #include "ESPStepperMotorServer.h" 100 | 101 | // The below state table has, for each state (row), the new state 102 | // to set based on the next encoder output. From left to right in, 103 | // the table, the encoder outputs are 00, 01, 10, 11, and the value 104 | // in that position is the new state to set. 105 | #define R_START 0x0 106 | 107 | // NOTE regarind HALF STEP support in the original Rotray Encoder library by BenBuxton: 108 | // Half Step support has been removed to reduce complexity 109 | 110 | // Use the full-step state table (emits a code at 00 only) 111 | #define R_CW_FINAL 0x1 112 | #define R_CW_BEGIN 0x2 113 | #define R_CW_NEXT 0x3 114 | #define R_CCW_BEGIN 0x4 115 | #define R_CCW_FINAL 0x5 116 | #define R_CCW_NEXT 0x6 117 | 118 | const unsigned char ttable[7][4] = { 119 | // R_START 120 | {R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START}, 121 | // R_CW_FINAL 122 | {R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW}, 123 | // R_CW_BEGIN 124 | {R_CW_NEXT, R_CW_BEGIN, R_START, R_START}, 125 | // R_CW_NEXT 126 | {R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START}, 127 | // R_CCW_BEGIN 128 | {R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START}, 129 | // R_CCW_FINAL 130 | {R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW}, 131 | // R_CCW_NEXT 132 | {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START}, 133 | }; 134 | 135 | ESPStepperMotorServer_RotaryEncoder::ESPStepperMotorServer_RotaryEncoder(char pinA, char pinB, String displayName, int stepMultiplier, byte stepperIndex) 136 | { 137 | // Assign variables. 138 | this->_pinA = pinA; 139 | this->_pinB = pinB; 140 | this->_displayName = displayName; 141 | this->_stepMultiplier = stepMultiplier; 142 | this->_stepperIndex = stepperIndex; 143 | this->_encoderIndex = -1; 144 | // Initialise state. 145 | this->_state = R_START; 146 | } 147 | 148 | unsigned char ESPStepperMotorServer_RotaryEncoder::process() 149 | { 150 | // Grab state of input pins. 151 | unsigned char pinstate = (digitalRead(this->_pinB) << 1) | digitalRead(this->_pinA); 152 | // Determine new state from the pins and state table. 153 | this->_state = ttable[this->_state & 0xf][pinstate]; 154 | // Return emit bits, ie the generated event. 155 | return this->_state & 0x30; 156 | } 157 | 158 | void ESPStepperMotorServer_RotaryEncoder::setId(byte id) 159 | { 160 | this->_encoderIndex = id; 161 | } 162 | 163 | byte ESPStepperMotorServer_RotaryEncoder::getId() 164 | { 165 | return this->_encoderIndex; 166 | } 167 | 168 | unsigned char ESPStepperMotorServer_RotaryEncoder::getPinAIOPin() 169 | { 170 | return this->_pinA; 171 | } 172 | 173 | unsigned char ESPStepperMotorServer_RotaryEncoder::getPinBIOPin() 174 | { 175 | return this->_pinB; 176 | } 177 | 178 | void ESPStepperMotorServer_RotaryEncoder::setStepperIndex(byte stepperMotorIndex) 179 | { 180 | if (stepperMotorIndex > -1 && stepperMotorIndex <= ESPStepperHighestAllowedIoPin) 181 | { 182 | this->_stepperIndex = stepperMotorIndex; 183 | } 184 | else 185 | { 186 | ESPStepperMotorServer_Logger::logWarning("ESPStepperMotorServer_RotaryEncoder::setStepperIndex: Invalid stepper motor index value given, must be within he allowed range of 0 >= value <= ESPStepperHighestAllowedIoPin"); 187 | } 188 | } 189 | 190 | byte ESPStepperMotorServer_RotaryEncoder::getStepperIndex() 191 | { 192 | return this->_stepperIndex; 193 | } 194 | 195 | const String ESPStepperMotorServer_RotaryEncoder::getDisplayName() 196 | { 197 | return this->_displayName; 198 | } 199 | 200 | void ESPStepperMotorServer_RotaryEncoder::setStepMultiplier(unsigned int stepMultiplier) 201 | { 202 | this->_stepMultiplier = stepMultiplier; 203 | } 204 | 205 | unsigned int ESPStepperMotorServer_RotaryEncoder::getStepMultiplier() 206 | { 207 | return this->_stepMultiplier; 208 | } 209 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_RotaryEncoder.h: -------------------------------------------------------------------------------- 1 | // ****************************************************************** 2 | // * * 3 | // * Header file for ESPStepperMotorServer_RotaryEncoder.cpp * 4 | // * * 5 | // * Copyright (c) Paul Kerspe, 2019 * 6 | // * * 7 | // ****************************************************************** 8 | // 9 | // This is a class to support rotary encoders a input controllers for the ESPStepperMotorServer 10 | // 11 | // It is based on the work Ben Buxton's Rotary Encodr Library, which was licensed under the following conditions: 12 | // Copyright 2011 Ben Buxton.Licenced under the GNU GPL Version 3. Contact : bb @cactii.net 13 | // Licenced under the GNU GPL Version 3. Contact: bb@cactii.net 14 | // https://github.com/buxtronix/arduino/tree/master/libraries/Rotary 15 | // 16 | // I included the sources here to reduce the complexity of setting up the required libraries that need to be installed on top of the ESPSMS Library 17 | // by the user in the Arduino UI, since it can not currently deal with automatic dependency management like platformIO. 18 | // Ben's code is beautiful in its simplicy and therefore does not create a noticable overhead in the code size of the ESPStepperMotorServer. 19 | // 20 | // 21 | // ESPStepperMotorServer is licensed under the following conditions: 22 | // 23 | // MIT License 24 | // 25 | // Copyright (c) 2019 Paul Kerspe 26 | // 27 | // Permission is hereby granted, free of charge, to any person obtaining a copy 28 | // of this software and associated documentation files (the "Software"), to deal 29 | // in the Software without restriction, including without limitation the rights 30 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 31 | // copies of the Software, and to permit persons to whom the Software is furnished 32 | // to do so, subject to the following conditions: 33 | // 34 | // The above copyright notice and this permission notice shall be included in all 35 | // copies or substantial portions of the Software. 36 | // 37 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 43 | // SOFTWARE. 44 | 45 | #ifndef ESPStepperMotorServer_RotaryEncoder_h 46 | #define ESPStepperMotorServer_RotaryEncoder_h 47 | 48 | #include "Arduino.h" 49 | 50 | // Enable this to emit codes twice per step. 51 | //#define HALF_STEP 52 | 53 | // Enable weak pullups 54 | #define ENABLE_PULLUPS 55 | 56 | // Values returned by 'process' 57 | // No complete step yet. 58 | #define DIR_NONE 0x0 59 | // Clockwise step. 60 | #define DIR_CW 0x10 61 | // Anti-clockwise step. 62 | #define DIR_CCW 0x20 63 | 64 | //size calculated using https://arduinojson.org/v6/assistant/ 65 | #define RESERVED_JSON_SIZE_ESPStepperMotorServer_RotaryEncoder 170 66 | 67 | class ESPStepperMotorServer_RotaryEncoder 68 | { 69 | friend class ESPStepperMotorServer; 70 | 71 | public: 72 | /** 73 | * Constructor for the rotary encoder entity. 74 | * The arguments define the PGIO Pins to be used to connect the Rotary encoder to 75 | * The displayName defines the human readable name for thi encoder in the User Interface and logs 76 | */ 77 | ESPStepperMotorServer_RotaryEncoder(char pinA, char pinB, String displayName, int stepMultiplier, byte stepperIndex); 78 | 79 | /** 80 | * setter to set the id of this encoder. 81 | * Only use this if you know what you are doing 82 | */ 83 | void setId(byte id); 84 | /** 85 | * get the id of the rotary encoder 86 | */ 87 | byte getId(); 88 | /** 89 | * process the input states of the io pins to determine the current rotary encoder step status 90 | */ 91 | unsigned char process(); 92 | /** 93 | * return the configured GPIO pin number that is connected to pin A of the rotary encoder 94 | */ 95 | unsigned char getPinAIOPin(); 96 | /** 97 | * return the configured GPIO pin number that is connected to pin B of the rotary encoder 98 | */ 99 | unsigned char getPinBIOPin(); 100 | /** 101 | * get the configured display name of the rotary encoder 102 | */ 103 | const String getDisplayName(); 104 | /** 105 | * set the stepper motor id that should be linked to this rotary encoder 106 | */ 107 | void setStepperIndex(byte stepperMotorIndex); 108 | /** 109 | * get the configured id of the stepper motor that is linked to this rotary encoder 110 | */ 111 | byte getStepperIndex(); 112 | /** 113 | * set a multiplication factor used to calculate the ammount of pulses send to the stepper motor for one step of the rotary encoder. 114 | * Default is factor of 1, so if one step in the rotatry encoder will be convertd into on puls to the steppr motor driver. 115 | * If microstpping is configured in the stepper driver board, one pulse will be one microstep, so it might be needed to st this multiplier accordingly to the microstepp setting of the stepper drivr board. * e.g. if you configured 32 microsteps in your stepper driver board and you want the stepper motor to perform one full step per rotary encoder step, you need to set this mulitplier to 32 116 | */ 117 | void setStepMultiplier(unsigned int stepMultiplier); 118 | /** 119 | * get the configured step multiplier value for this rotary encoder 120 | */ 121 | unsigned int getStepMultiplier(void); 122 | 123 | private: 124 | unsigned char _state; 125 | unsigned char _pinA; 126 | unsigned char _pinB; 127 | unsigned char _encoderIndex; 128 | byte _stepperIndex; 129 | String _displayName; 130 | // step multiplier is used to define how many pulses should be sen to the stepper for one step from the rotary encoder 131 | unsigned int _stepMultiplier; 132 | }; 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_StepperConfiguration.cpp: -------------------------------------------------------------------------------- 1 | // ********************************************************************* 2 | // * * 3 | // * ESP8266 and ESP32 Stepper Motor Server - Stepper Config class * 4 | // * a wrapper class to decouple the FlexyStepper class a bit better * 5 | // * Copyright (c) Paul Kerspe, 2019 * 6 | // * * 7 | // ********************************************************************* 8 | // 9 | // MIT License 10 | // 11 | // Copyright (c) 2019 Paul Kerspe 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is furnished 18 | // to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | // 31 | 32 | #include "ESPStepperMotorServer_StepperConfiguration.h" 33 | 34 | ESPStepperMotorServer_StepperConfiguration::ESPStepperMotorServer_StepperConfiguration(const ESPStepperMotorServer_StepperConfiguration &espStepperConfiguration) 35 | { 36 | this->_flexyStepper = new ESP_FlexyStepper; 37 | *this->_flexyStepper = *espStepperConfiguration._flexyStepper; 38 | 39 | this->_stepIoPin = espStepperConfiguration._stepIoPin; 40 | this->_directionIoPin = espStepperConfiguration._directionIoPin; 41 | this->_stepsPerMM = espStepperConfiguration._stepsPerMM; 42 | this->_stepsPerRev = espStepperConfiguration._stepsPerRev; 43 | this->_microsteppingDivisor = espStepperConfiguration._microsteppingDivisor; 44 | this->_displayName = espStepperConfiguration._displayName; 45 | this->_rpmLimit = espStepperConfiguration._rpmLimit; 46 | 47 | this->_flexyStepper->connectToPins(this->_stepIoPin, this->_directionIoPin); 48 | } 49 | 50 | ESPStepperMotorServer_StepperConfiguration::~ESPStepperMotorServer_StepperConfiguration() 51 | { 52 | delete this->_flexyStepper; 53 | } 54 | 55 | // 56 | // constructor for the stepper wrapper class 57 | // 58 | ESPStepperMotorServer_StepperConfiguration::ESPStepperMotorServer_StepperConfiguration(byte stepIoPin, byte directionIoPin) 59 | { 60 | this->_stepIoPin = stepIoPin; 61 | this->_directionIoPin = directionIoPin; 62 | this->_flexyStepper = new ESP_FlexyStepper(); 63 | this->_flexyStepper->connectToPins(this->_stepIoPin, this->_directionIoPin); 64 | } 65 | 66 | ESPStepperMotorServer_StepperConfiguration::ESPStepperMotorServer_StepperConfiguration(byte stepIoPin, byte directionIoPin, String displayName, unsigned int stepsPerRev, unsigned int stepsPerMM, unsigned int microsteppingDivisor, unsigned int rpmLimit) 67 | { 68 | this->_stepIoPin = stepIoPin; 69 | this->_directionIoPin = directionIoPin; 70 | this->_microsteppingDivisor = microsteppingDivisor; 71 | this->_displayName = displayName; 72 | this->_rpmLimit = rpmLimit; 73 | 74 | this->_flexyStepper = new ESP_FlexyStepper(); 75 | this->_flexyStepper->connectToPins(this->_stepIoPin, this->_directionIoPin); 76 | 77 | //we store the value in flexistepper and locally, since flexystepper does not provider getters 78 | this->_flexyStepper->setStepsPerMillimeter(stepsPerMM * this->_microsteppingDivisor); 79 | this->_stepsPerMM = stepsPerMM; 80 | 81 | //we store the value in flexistepper and locally, since flexystepper does not provider getters 82 | this->_flexyStepper->setStepsPerRevolution(stepsPerRev * this->_microsteppingDivisor); 83 | this->_stepsPerRev = stepsPerRev; 84 | } 85 | 86 | // --------------------------------------------------------------------------------- 87 | // Getters / Setters 88 | // --------------------------------------------------------------------------------- 89 | ESP_FlexyStepper *ESPStepperMotorServer_StepperConfiguration::getFlexyStepper() 90 | { 91 | return this->_flexyStepper; 92 | } 93 | 94 | void ESPStepperMotorServer_StepperConfiguration::setId(byte id) 95 | { 96 | this->_stepperIndex = id; 97 | } 98 | 99 | byte ESPStepperMotorServer_StepperConfiguration::getId() 100 | { 101 | return this->_stepperIndex; 102 | } 103 | 104 | String ESPStepperMotorServer_StepperConfiguration::getDisplayName() 105 | { 106 | return this->_displayName; 107 | } 108 | void ESPStepperMotorServer_StepperConfiguration::setDisplayName(String displayName) 109 | { 110 | if (displayName.length() > ESPSMS_Stepper_DisplayName_MaxLength) 111 | { 112 | char logString[160]; 113 | sprintf(logString, "ESPStepperMotorServer_StepperConfiguration::setDisplayName: The display name for stepper with id %i is to long. Max length is %i characters. Name will be trimmed", this->getId(), ESPSMS_Stepper_DisplayName_MaxLength); 114 | ESPStepperMotorServer_Logger::logWarning(logString); 115 | this->_displayName = displayName.substring(0, ESPSMS_Stepper_DisplayName_MaxLength); 116 | } 117 | else 118 | { 119 | this->_displayName = displayName; 120 | } 121 | } 122 | 123 | byte ESPStepperMotorServer_StepperConfiguration::getStepIoPin() 124 | { 125 | return this->_stepIoPin; 126 | } 127 | 128 | byte ESPStepperMotorServer_StepperConfiguration::getDirectionIoPin() 129 | { 130 | return this->_directionIoPin; 131 | } 132 | 133 | // brake control settings 134 | 135 | byte ESPStepperMotorServer_StepperConfiguration::getBrakeIoPin() 136 | { 137 | return this->_brakeIoPin; 138 | } 139 | 140 | long ESPStepperMotorServer_StepperConfiguration::getBrakeEngageDelayMs() 141 | { 142 | return this->_brakeEngageDelayMs; 143 | } 144 | 145 | long ESPStepperMotorServer_StepperConfiguration::getBrakeReleaseDelayMs() 146 | { 147 | return this->_brakeReleaseDelayMs; 148 | } 149 | 150 | byte ESPStepperMotorServer_StepperConfiguration::getBrakePinActiveState() 151 | { 152 | return this->_brakePinActiveState; 153 | } 154 | 155 | void ESPStepperMotorServer_StepperConfiguration::setBrakeIoPin(byte brakeIoPin, byte brakePinActiveState) 156 | { 157 | this->_brakeIoPin = brakeIoPin; 158 | this->_brakePinActiveState = brakePinActiveState; 159 | this->_flexyStepper->setBrakePin(brakeIoPin, brakePinActiveState); 160 | } 161 | 162 | void ESPStepperMotorServer_StepperConfiguration::setBrakeEngageDelayMs(long delay) 163 | { 164 | this->_brakeEngageDelayMs = delay; 165 | this->_flexyStepper->setBrakeEngageDelayMs(delay); 166 | } 167 | 168 | void ESPStepperMotorServer_StepperConfiguration::setBrakeReleaseDelayMs(long delay) 169 | { 170 | this->_brakeReleaseDelayMs = delay; 171 | this->_flexyStepper->setBrakeReleaseDelayMs(delay); 172 | } 173 | 174 | void ESPStepperMotorServer_StepperConfiguration::setBrakePinActiveState(byte activeState) 175 | { 176 | this->_brakePinActiveState = activeState; 177 | this->_flexyStepper->setBrakePin(this->_brakeIoPin, this->_brakePinActiveState); 178 | } 179 | 180 | // motion configurateion settings 181 | void ESPStepperMotorServer_StepperConfiguration::setStepsPerRev(unsigned int stepsPerRev) 182 | { 183 | this->_flexyStepper->setStepsPerRevolution(stepsPerRev * this->_microsteppingDivisor); 184 | this->_stepsPerRev = stepsPerRev; 185 | } 186 | 187 | unsigned int ESPStepperMotorServer_StepperConfiguration::getStepsPerRev() 188 | { 189 | return this->_stepsPerRev; 190 | } 191 | 192 | void ESPStepperMotorServer_StepperConfiguration::setStepsPerMM(unsigned int stepsPerMM) 193 | { 194 | this->_flexyStepper->setStepsPerMillimeter(stepsPerMM * this->_microsteppingDivisor); 195 | this->_stepsPerMM = stepsPerMM; 196 | } 197 | 198 | unsigned int ESPStepperMotorServer_StepperConfiguration::getStepsPerMM() 199 | { 200 | return this->_stepsPerMM; 201 | } 202 | 203 | void ESPStepperMotorServer_StepperConfiguration::setMicrostepsPerStep(unsigned int microstepsPerStep) 204 | { 205 | //check for power of two value, since others are not allowed in micro step sizes 206 | if (microstepsPerStep && !(microstepsPerStep & (microstepsPerStep - 1))) 207 | { 208 | this->_microsteppingDivisor = microstepsPerStep; 209 | //update flexy stepper as well in regards to steps/rev and steps/mm 210 | this->_flexyStepper->setStepsPerMillimeter(this->_stepsPerMM * this->_microsteppingDivisor); 211 | this->_flexyStepper->setStepsPerRevolution(this->_stepsPerRev * this->_microsteppingDivisor); 212 | } 213 | else 214 | { 215 | ESPStepperMotorServer_Logger::logWarningf("Invalid microstepping value given: %i. Only values which are power of two are allowed", microstepsPerStep); 216 | } 217 | } 218 | 219 | unsigned int ESPStepperMotorServer_StepperConfiguration::getMicrostepsPerStep() 220 | { 221 | return this->_microsteppingDivisor; 222 | } 223 | 224 | void ESPStepperMotorServer_StepperConfiguration::setRpmLimit(unsigned int rpmLimit) 225 | { 226 | if (rpmLimit > ESPSMS_MAX_UPPER_RPM_LMIT) 227 | { 228 | char logString[170]; 229 | sprintf(logString, "ESPStepperMotorServer_StepperConfiguration::setRpmLimit: The given rpm limit value %u exceeds the allowed maximum rpm limit of %i, will set to %i", rpmLimit, ESPSMS_MAX_UPPER_RPM_LMIT, ESPSMS_MAX_UPPER_RPM_LMIT); 230 | ESPStepperMotorServer_Logger::logWarning(logString); 231 | this->_rpmLimit = ESPSMS_MAX_UPPER_RPM_LMIT; 232 | } 233 | else 234 | { 235 | this->_rpmLimit = rpmLimit; 236 | } 237 | } 238 | 239 | unsigned int ESPStepperMotorServer_StepperConfiguration::getRpmLimit() 240 | { 241 | return this->_rpmLimit; 242 | } 243 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_StepperConfiguration.h: -------------------------------------------------------------------------------- 1 | // ************************************************************************* 2 | // * * 3 | // * Header file for ESPStepperMotorServer_StepperConfiguration.cpp * 4 | // * * 5 | // * Copyright (c) Paul Kerspe, 2019 * 6 | // * * 7 | // ************************************************************************* 8 | 9 | // MIT License 10 | // 11 | // Copyright (c) 2019 Paul Kerspe 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is furnished 18 | // to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | 31 | #ifndef ESPStepperMotorServer_StepperConfiguration_h 32 | #define ESPStepperMotorServer_StepperConfiguration_h 33 | 34 | #include 35 | #include 36 | 37 | #define ESPSMS_MICROSTEPS_OFF 1 38 | #define ESPSMS_MICROSTEPS_2 2 39 | #define ESPSMS_MICROSTEPS_4 4 40 | #define ESPSMS_MICROSTEPS_8 8 41 | #define ESPSMS_MICROSTEPS_16 16 42 | #define ESPSMS_MICROSTEPS_32 32 43 | #define ESPSMS_MICROSTEPS_128 128 44 | #define ESPSMS_MICROSTEPS_256 256 45 | 46 | #define ESPSMS_MAX_UPPER_RPM_LMIT 3000 47 | 48 | #define ESPSMS_Stepper_DisplayName_MaxLength 20 49 | 50 | //size calculated using https://arduinojson.org/v6/assistant/ 51 | #define RESERVED_JSON_SIZE_ESPStepperMotorServer_StepperConfiguration 210 52 | 53 | class ESPStepperMotorServer_StepperConfiguration 54 | { 55 | friend class ESPStepperMotorServer; 56 | 57 | public: 58 | ESPStepperMotorServer_StepperConfiguration(const ESPStepperMotorServer_StepperConfiguration &espStepperConfiguration); 59 | ~ESPStepperMotorServer_StepperConfiguration(); 60 | ESPStepperMotorServer_StepperConfiguration(byte stepIoPin, byte directionIoPin); 61 | ESPStepperMotorServer_StepperConfiguration(byte stepIoPin, byte directionIoPin, String displayName, unsigned int stepsPerRev, unsigned int stepsPerMM, unsigned int microsteppingDivisor, unsigned int rpmLimit); 62 | 63 | ESP_FlexyStepper *getFlexyStepper(); 64 | 65 | /** 66 | * Internally used setter to set the id of this stepper motor. 67 | * Only use this if you know what you are doing 68 | */ 69 | void setId(byte id); 70 | /** 71 | * get the internal id of this stepper motor configuration within the stepper server 72 | */ 73 | byte getId(); 74 | 75 | /** 76 | * Set the display name of the stepper motor to be shown in the user intefaces 77 | */ 78 | void setDisplayName(String displayName); 79 | /** 80 | * Get the currently configured display name for the stepper motor 81 | */ 82 | String getDisplayName(); 83 | 84 | /** 85 | * Get the currently configured IO pin that is used to send step pulses to the stepper driver 86 | */ 87 | byte getStepIoPin(); 88 | 89 | /** 90 | * Get the currently configured IO pin that is used to send the direction signal to the stepper driver 91 | */ 92 | byte getDirectionIoPin(); 93 | 94 | /** 95 | * Get the currently configured IO pin that is used to engage an optional engine/motor brake. 96 | * Returns ESPServerStepperUnsetIoPinNumber (255) if none is defined 97 | */ 98 | byte getBrakeIoPin(); 99 | 100 | /** 101 | * Get the currently configured active state of the IO pin used to enabel the engine/motor brake. 102 | * Returns 1 for active high (pin goes high to activate the brake), 2 for active low (pin goes low to activate the brake) 103 | */ 104 | byte getBrakePinActiveState(); 105 | 106 | /** 107 | * Get the currently configured delay in ms between the motor comes to a stop and the engine brake is being engaged. Default is 0ms. 108 | */ 109 | long getBrakeEngageDelayMs(); 110 | 111 | /** 112 | * Get the currently confgured timeout of inactivity of the motor, before the motor brake is released. 113 | * Default is -1, meaning that the brake is never released as long as the engine is stopped. 114 | */ 115 | long getBrakeReleaseDelayMs(); 116 | 117 | void setBrakeIoPin(byte, byte); 118 | void setBrakePinActiveState(byte); 119 | void setBrakeEngageDelayMs(long); 120 | void setBrakeReleaseDelayMs(long); 121 | 122 | /** 123 | * Set the number of full steps the stepper motor itself needs to perform for a full revolution. 124 | * Most stepper motors perform 1.8 degree turn per step, thus resulting in 200 full steps per revolution. 125 | * Other somewhat common values are 3.6 degreee (100 steps/rev), 3.75 degree (96 steps/rev) and 7.5 degree (48 steps/rev) per full step. 126 | * Geared stepper motors may have much smaller values, resutling in a much higher steps/rev value. See the datasheet of your stepper motor for the correct value. 127 | * The default value is 200 steps/rev since this is the most common value. 128 | */ 129 | void setStepsPerRev(unsigned int); 130 | /** 131 | * Get the currently configured steps/rev value for this steppe motor 132 | * The default value is 200 steps/rev 133 | **/ 134 | unsigned int getStepsPerRev(); 135 | 136 | /** 137 | * Set the number of full steps (not microsteps!) required to move the axis by 1mm. 138 | * This depends on the lead screw pitch (if lead screws are used) or the gear ratio or whatever mechanism is used to transform revolutions of the stepper motor to linear motion 139 | * The default value is 100 steps/rev since this is the most common value for T8 leadscrews in regards to pitch. 140 | */ 141 | void setStepsPerMM(unsigned int); 142 | 143 | /** 144 | * Get the currently configured steps/mm value for this steppe motor 145 | * The default value is 100 steps/mm since standard T8 lead screws have a pitch of 2mm per rev, this, combined with the standard 200steps/rev of stepper motors, leads to 100 steps/mm 146 | **/ 147 | unsigned int getStepsPerMM(); 148 | 149 | /** 150 | * Set the number of microsteps you configured in the stepper driver (usually one with DIP switches on the driver board) for this stepper motor. 151 | * Common values are 1 (no micro stepping), 2 (half step), 4, 8, 16, 32, 64, 128 and sometimes 256 microsteps per step. 152 | * This setting is needed to calculate the proper amount of pulses that need to be send to the stepper driver. 153 | * If this value does not match the configured micro step setting on your driver board, 154 | * the number of pulses need to travel a certain distance in mm or to perform a certain amount of revolutions with the stepper motor, will not be correct. 155 | * The default value is 1 (ESPSMS_MICROSTEPS_OFF). 156 | * Allowed values: ESPSMS_MICROSTEPS_OFF (full stepping), ESPSMS_MICROSTEPS_2 (half stepping), ESPSMS_MICROSTEPS_4, ESPSMS_MICROSTEPS_8, ESPSMS_MICROSTEPS_16, ESPSMS_MICROSTEPS_32, ESPSMS_MICROSTEPS_64, ESPSMS_MICROSTEPS_128, ESPSMS_MICROSTEPS_256 157 | */ 158 | void setMicrostepsPerStep(unsigned int); 159 | /** 160 | * Get the currently configured number of microsteps per step for this stepper motor. 161 | * The default value is 1 (ESPSMS_MICROSTEPS_OFF) 162 | **/ 163 | unsigned int getMicrostepsPerStep(); 164 | 165 | /** 166 | * Set the maximum revolutions per minute for this stepper. 167 | * This limit will only be used to limit the allowed values in the rest api enpoints / user interfaces and to calculate the maximum step pulse frequncy needed. 168 | * If the step pulse frequnency is higher than the one your motor can handle, you might lose steps or the motor might stall. 169 | * See your stepper motors datasheet for the torque curve an chose the limit that fits your needs (some datasheets specify the PPS (=Pulses per second) rather then revs/minutem, so make sure you get the right unit when settings this value). 170 | * The default setting is 1200 revs/minute, which might be already to high for some steppers (especially geared ones or steppers driven with a low voltage) 171 | **/ 172 | void setRpmLimit(unsigned int); 173 | /** 174 | * Get the currently configured Revolutions per Minute limit for this stepper. 175 | * Default is 1200 revs/minute. 176 | */ 177 | unsigned int getRpmLimit(); 178 | 179 | const static byte ESPServerStepperUnsetIoPinNumber = 255; 180 | 181 | private: 182 | // 183 | // private member variables 184 | // 185 | ESP_FlexyStepper *_flexyStepper; 186 | String _displayName; 187 | byte _stepperIndex = 0; 188 | byte _stepIoPin = ESPServerStepperUnsetIoPinNumber; 189 | byte _directionIoPin = ESPServerStepperUnsetIoPinNumber; 190 | byte _brakeIoPin = ESPServerStepperUnsetIoPinNumber; 191 | byte _brakePinActiveState = 1; // 1 = active high, 2 = active low 192 | long _brakeEngageDelayMs = 0; 193 | long _brakeReleaseDelayMs = -1; 194 | unsigned int _stepsPerRev = 200; 195 | unsigned int _stepsPerMM = 100; 196 | unsigned int _microsteppingDivisor = ESPSMS_MICROSTEPS_OFF; 197 | unsigned int _rpmLimit = 1200; 198 | }; 199 | // ------------------------------------ End --------------------------------- 200 | #endif 201 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_WebInterface.cpp: -------------------------------------------------------------------------------- 1 | 2 | // ********************************************************* 3 | // * * 4 | // * ESP32 Stepper Motor Web Interface * 5 | // * * 6 | // * Copyright (c) Paul Kerspe, 2019 * 7 | // * * 8 | // ********************************************************** 9 | 10 | // MIT License 11 | // 12 | // Copyright (c) 2019 Paul Kerspe 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy 15 | // of this software and associated documentation files (the "Software"), to deal 16 | // in the Software without restriction, including without limitation the rights 17 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the Software is furnished 19 | // to do so, subject to the following conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all 22 | // copies or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | // SOFTWARE. 31 | 32 | #include 33 | 34 | HTTPClient http; 35 | // --------------------------------------------------------------------------------- 36 | // Setup functions 37 | // --------------------------------------------------------------------------------- 38 | 39 | // 40 | // constructor for the web user interface module 41 | // 42 | ESPStepperMotorServer_WebInterface::ESPStepperMotorServer_WebInterface(ESPStepperMotorServer *serverRef) 43 | { 44 | this->_serverRef = serverRef; 45 | this->_httpServer = NULL; 46 | } 47 | 48 | /** 49 | * check if the UI files exist in the SPIFFS and then register all endpoints for the web UI in the http server 50 | */ 51 | void ESPStepperMotorServer_WebInterface::registerWebInterfaceUrls(AsyncWebServer *httpServer) 52 | { 53 | this->_httpServer = httpServer; 54 | 55 | //OTA update form 56 | this->_httpServer->on("/update", HTTP_GET, [this](AsyncWebServerRequest *request) { 57 | if (SPIFFS.exists(this->webUiFirmwareUpdate)) 58 | { 59 | AsyncWebServerResponse *response = request->beginResponse(SPIFFS, this->webUiFirmwareUpdate, "text/html"); 60 | response->addHeader("Content-Encoding", "gzip"); 61 | request->send(response); 62 | } 63 | else 64 | { 65 | request->send(200, "text/html", "

Firmware update

Firmware File:

"); 66 | } 67 | }); 68 | 69 | //OTA Update handler 70 | this->_httpServer->on( 71 | "/update", HTTP_POST, [this](AsyncWebServerRequest *request) { 72 | // the request handler is triggered after the upload has finished... 73 | AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", (Update.hasError())?"UPDATE FAILED":"SUCCESS. Rebooting server now"); 74 | response->addHeader("Connection", "close"); 75 | response->addHeader("Access-Control-Allow-Origin", "*"); 76 | request->send(response); 77 | if (!Update.hasError()) { 78 | delay(100); 79 | this->_serverRef->requestReboot("Firmware update completed"); 80 | } }, 81 | 82 | //Upload handler to process chunks of data 83 | [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { 84 | if (!filename.endsWith(".bin")) 85 | { 86 | Serial.println("Invalid firmware file provided, must have .bin-extension"); 87 | request->send(400, "text/plain", "Invalid fimrware file given"); 88 | request->client()->close(); 89 | } 90 | else 91 | { 92 | if (!index) 93 | { // if index == 0 then this is the first frame of data 94 | Serial.printf("UploadStart: %s\n", filename.c_str()); 95 | Serial.setDebugOutput(true); 96 | 97 | // calculate sketch space required for the update 98 | uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; 99 | if (!Update.begin(maxSketchSpace)) 100 | { //start with max available size 101 | Update.printError(Serial); 102 | } 103 | } 104 | 105 | //Write chunked data to the free sketch space 106 | if (Update.write(data, len) != len) 107 | { 108 | Update.printError(Serial); 109 | } 110 | 111 | if (final) 112 | { // if the final flag is set then this is the last frame of data 113 | if (Update.end(true)) 114 | { //true to set the size to the current progress 115 | Serial.printf("Update Success: %u B\nRebooting...\n", index + len); 116 | } 117 | else 118 | { 119 | Update.printError(Serial); 120 | } 121 | Serial.setDebugOutput(false); 122 | } 123 | } 124 | }); 125 | 126 | if (this->_serverRef->isSPIFFSMounted() && checkIfGuiExistsInSpiffs()) 127 | { 128 | this->_httpServer->on("/", HTTP_GET, [this](AsyncWebServerRequest *request) { 129 | request->send(SPIFFS, this->webUiIndexFile); 130 | }); 131 | this->_httpServer->on(this->webUiIndexFile, HTTP_GET, [this](AsyncWebServerRequest *request) { 132 | request->send(SPIFFS, this->webUiIndexFile); 133 | }); 134 | 135 | this->_httpServer->on(this->webUiFaviconFile, HTTP_GET, [this](AsyncWebServerRequest *request) { 136 | AsyncWebServerResponse *response = request->beginResponse(SPIFFS, this->webUiFaviconFile, "image/x-icon"); 137 | //response->addHeader("Content-Encoding", "gzip"); 138 | request->send(response); 139 | }); 140 | /* REMOVED FOR NOW DUE TO SECURITY ISSUES. CAN STILL USE /api/config endpoint to download config (in memory config though!) 141 | this->_httpServer->on(this->_serverRef->defaultConfigurationFilename, HTTP_GET, [this](AsyncWebServerRequest *request) { 142 | //FIME: currently this streams the json config including the WiFi Credentials, which might be a security risk. 143 | //TODO: replace wifi passwords in config with placeholder (maybe add config flag to API to allowed disabling the security feature in the future) 144 | AsyncWebServerResponse *response = request->beginResponse(SPIFFS, this->_serverRef->defaultConfigurationFilename, "application/json", true); 145 | request->send(response); 146 | }); 147 | */ 148 | 149 | this->_httpServer->on("/js/app.js", HTTP_GET, [this](AsyncWebServerRequest *request) { 150 | request->redirect(this->webUiJsFile); 151 | }); 152 | this->_httpServer->on(this->webUiJsFile, HTTP_GET, [this](AsyncWebServerRequest *request) { 153 | AsyncWebServerResponse *response = request->beginResponse(SPIFFS, this->webUiJsFile, "text/javascript"); 154 | response->addHeader("Content-Encoding", "gzip"); 155 | request->send(response); 156 | }); 157 | 158 | //little test page to show contents of SPIFFS and check if it is initialized at all for trouble shooting 159 | this->_httpServer->on("/selftest", HTTP_GET, [this](AsyncWebServerRequest *request) { 160 | AsyncResponseStream *response = request->beginResponseStream("text/html"); 161 | response->print("ESP-StepperMotorServer Test Page"); 162 | response->print("

ESP-StepperMotorServer self test

Testing environment:

    "); 163 | response->printf("
  • Server Version: %s", this->_serverRef->version); 164 | response->printf("
  • SPIFFS Initialized: %s
  • ", (this->_serverRef->isSPIFFSMounted()) ? "true" : "false"); 165 | if (!this->_serverRef->isSPIFFSMounted()) 166 | { 167 | response->print("ERROR: SPIFFS not initialized & mounted, in case you are intending to use the WEB UI, you need to make sure the SPI Flash Filesystem has been properly initialized on your ESP32"); 168 | } 169 | else 170 | { 171 | response->printf("
  • WEB UI installed completely: %s
  • ", (this->checkIfGuiExistsInSpiffs()) ? "true" : "false"); 172 | 173 | File root = SPIFFS.open("/"); 174 | if (!root) 175 | { 176 | response->print("ERROR: Failed to open root folder on SPIFFS for reading"); 177 | } 178 | if (root.isDirectory()) 179 | { 180 | response->print("
  • Listing files in root folder of SPIFFS:
      "); 181 | File file = root.openNextFile(); 182 | while (file) 183 | { 184 | response->printf("
    • File: %s (%i) %ld
    • ", file.name(), file.size(), file.getLastWrite()); 185 | file = root.openNextFile(); 186 | } 187 | response->printf("
  • "); 188 | root.close(); 189 | } 190 | } 191 | response->printf("
"); 192 | String output = String(""); 193 | this->_serverRef->getServerStatusAsJsonString(output); //populate string with json 194 | response->print(output); 195 | response->print("
"); 196 | //send the response last 197 | request->send(response); 198 | }); 199 | 200 | // register image paths with caching header present 201 | this->_httpServer->on(this->webUiLogoFile, HTTP_GET, [this](AsyncWebServerRequest *request) { 202 | AsyncWebServerResponse *response = request->beginResponse(SPIFFS, this->webUiLogoFile, "image/svg+xml"); 203 | response->addHeader("Cache-Control", "max-age=36000, public"); 204 | request->send(response); 205 | }); 206 | this->_httpServer->on(this->webUiStepperGraphic, HTTP_GET, [this](AsyncWebServerRequest *request) { 207 | AsyncWebServerResponse *response = request->beginResponse(SPIFFS, this->webUiStepperGraphic, "image/svg+xml"); 208 | response->addHeader("Cache-Control", "max-age=36000, public"); 209 | request->send(response); 210 | }); 211 | this->_httpServer->on(this->webUiEncoderGraphic, HTTP_GET, [this](AsyncWebServerRequest *request) { 212 | AsyncWebServerResponse *response = request->beginResponse(SPIFFS, this->webUiEncoderGraphic, "image/svg+xml"); 213 | response->addHeader("Cache-Control", "max-age=36000, public"); 214 | request->send(response); 215 | }); 216 | this->_httpServer->on(this->webUiEmergencySwitchGraphic, HTTP_GET, [this](AsyncWebServerRequest *request) { 217 | AsyncWebServerResponse *response = request->beginResponse(SPIFFS, this->webUiEmergencySwitchGraphic, "image/svg+xml"); 218 | response->addHeader("Cache-Control", "max-age=36000, public"); 219 | request->send(response); 220 | }); 221 | this->_httpServer->on(this->webUiSwitchGraphic, HTTP_GET, [this](AsyncWebServerRequest *request) { 222 | AsyncWebServerResponse *response = request->beginResponse(SPIFFS, this->webUiSwitchGraphic, "image/svg+xml"); 223 | response->addHeader("Cache-Control", "max-age=36000, public"); 224 | request->send(response); 225 | }); 226 | this->_httpServer->onNotFound([this](AsyncWebServerRequest *request) { 227 | request->send(404, "text/html", "

ESP-StepperMotor-Server

The requested file could not be found"); 228 | }); 229 | } 230 | else 231 | { 232 | ESPStepperMotorServer_Logger::logInfo("No web UI could be registered"); 233 | } 234 | } 235 | 236 | /** 237 | * Checks if all required UI files exist in the SPIFFS. 238 | * Will try to download the current version of the files from the git hub repo if they could not be found 239 | */ 240 | bool ESPStepperMotorServer_WebInterface::checkIfGuiExistsInSpiffs() 241 | { 242 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 243 | ESPStepperMotorServer_Logger::logDebug("Checking if web UI is installed in SPIFFS"); 244 | #endif 245 | 246 | if (!this->_serverRef->isSPIFFSMounted()) 247 | { 248 | ESPStepperMotorServer_Logger::logWarning("SPIFFS is not mounted, UI files not found"); 249 | return false; 250 | } 251 | else 252 | { 253 | bool uiComplete = true; 254 | const char *notPresent = "The file %s could not be found on SPIFFS\n"; 255 | const char *files[] = { 256 | this->webUiIndexFile, 257 | this->webUiJsFile, 258 | this->webUiLogoFile, 259 | this->webUiFaviconFile, 260 | this->webUiEncoderGraphic, 261 | this->webUiEmergencySwitchGraphic, 262 | this->webUiStepperGraphic, 263 | this->webUiSwitchGraphic, 264 | this->webUiFirmwareUpdate}; 265 | 266 | for (int i = 0; i < 9; i++) //ALWAYS UPDATE THIS COUNTER IF NEW FILES ARE ADDED TO UI 267 | { 268 | if (!SPIFFS.exists(files[i])) 269 | { 270 | ESPStepperMotorServer_Logger::logInfof(notPresent, files[i]); 271 | if (this->_serverRef->getCurrentServerConfiguration()->wifiMode == ESPServerWifiModeClient && WiFi.isConnected()) 272 | { 273 | char downloadUrl[200]; 274 | strcpy(downloadUrl, webUiRepositoryBasePath); 275 | strcat(downloadUrl, files[i]); 276 | if (!this->downloadFileToSpiffs(downloadUrl, files[i])) 277 | { 278 | uiComplete = false; 279 | } 280 | } 281 | else 282 | { 283 | uiComplete = false; 284 | } 285 | } 286 | } 287 | 288 | if (uiComplete == false && this->_serverRef->getCurrentServerConfiguration()->wifiMode == ESPServerWifiModeAccessPoint) 289 | { 290 | ESPStepperMotorServer_Logger::logWarning("The UI does not seem to be installed completely on SPIFFS. Automatic download failed since the server is in Access Point mode and not connected to the internet"); 291 | ESPStepperMotorServer_Logger::logWarning("Start the server in wifi client (STA) mode to enable automatic download of the web interface files to SPIFFS"); 292 | } 293 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 294 | else if (ESPStepperMotorServer_Logger::isDebugEnabled()) 295 | { 296 | if (uiComplete == true) 297 | { 298 | ESPStepperMotorServer_Logger::logDebug("Check completed successfully"); 299 | } 300 | else 301 | { 302 | ESPStepperMotorServer_Logger::logDebug("Check failed, one or more UI files are missing and could not be downloaded automatically"); 303 | } 304 | } 305 | #endif 306 | return uiComplete; 307 | } 308 | } 309 | 310 | // Perform an HTTP GET request to a remote page to download a file to SPIFFS 311 | bool ESPStepperMotorServer_WebInterface::downloadFileToSpiffs(const char *url, const char *targetPath) 312 | { 313 | if (!this->_serverRef->isSPIFFSMounted()) 314 | { 315 | ESPStepperMotorServer_Logger::logWarningf("donwloading of %s was canceled since SPIFFS is not mounted\n"); 316 | return false; 317 | } 318 | else 319 | { 320 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 321 | ESPStepperMotorServer_Logger::logDebugf("downloading %s from %s\n", targetPath, url); 322 | #endif 323 | 324 | if (http.begin(url)) 325 | { 326 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 327 | int httpCode = http.GET(); 328 | ESPStepperMotorServer_Logger::logDebugf("server responded with %i\n", httpCode); 329 | #endif 330 | 331 | ////////////////// 332 | // get length of document (is -1 when Server sends no Content-Length header) 333 | int len = http.getSize(); 334 | uint8_t buff[128] = {0}; 335 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 336 | ESPStepperMotorServer_Logger::logDebugf("starting download stream for file size %i\n", len); 337 | #endif 338 | 339 | WiFiClient *stream = &http.getStream(); 340 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 341 | ESPStepperMotorServer_Logger::logDebug("opening file for writing"); 342 | #endif 343 | File f = SPIFFS.open(targetPath, "w+"); 344 | 345 | // read all data from server 346 | while (http.connected() && (len > 0 || len == -1)) 347 | { 348 | // get available data size 349 | size_t size = stream->available(); 350 | #ifndef ESPStepperMotorServer_COMPILE_NO_DEBUG 351 | ESPStepperMotorServer_Logger::logDebugf("%i bytes available to read from stream\n", size); 352 | #endif 353 | 354 | if (size) 355 | { 356 | // read up to 128 byte 357 | int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); 358 | // write it to file 359 | for (int i = 0; i < c; i++) 360 | { 361 | f.write(buff[i]); 362 | } 363 | if (len > 0) 364 | { 365 | len -= c; 366 | } 367 | } 368 | delay(1); 369 | } 370 | f.close(); 371 | ESPStepperMotorServer_Logger::logInfof("Download of %s completed\n", targetPath); 372 | http.end(); //Close connection 373 | } 374 | 375 | return SPIFFS.exists(targetPath); 376 | } 377 | } 378 | 379 | // -------------------------------------- End -------------------------------------- 380 | -------------------------------------------------------------------------------- /src/ESPStepperMotorServer_WebInterface.h: -------------------------------------------------------------------------------- 1 | // ****************************************************************** 2 | // * * 3 | // * Header file for ESPStepperMotorServer_WebInterface.cpp * 4 | // * * 5 | // * Copyright (c) Paul Kerspe, 2019 * 6 | // * * 7 | // ****************************************************************** 8 | 9 | // MIT License 10 | // 11 | // Copyright (c) 2019 Paul Kerspe 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is furnished 18 | // to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all 21 | // copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | // SOFTWARE. 30 | 31 | #ifndef ESPStepperMotorServer_WebInterface_h 32 | #define ESPStepperMotorServer_WebInterface_h 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | //need this forward declaration here due to circular dependency (use in constructor) 42 | class ESPStepperMotorServer; 43 | 44 | class ESPStepperMotorServer_WebInterface 45 | { 46 | public: 47 | ESPStepperMotorServer_WebInterface(ESPStepperMotorServer *serverRef); 48 | void registerWebInterfaceUrls(AsyncWebServer *httpServer); 49 | 50 | private: 51 | bool downloadFileToSpiffs(const char *url, const char *targetPath); 52 | bool checkIfGuiExistsInSpiffs(); 53 | 54 | const char *webUiFirmwareUpdate = "/upload.html.gz"; 55 | const char *webUiIndexFile = "/index.html"; 56 | const char *webUiJsFile = "/js/app.js.gz"; 57 | const char *webUiLogoFile = "/img/logo.svg"; 58 | const char *webUiEncoderGraphic = "/img/rotaryEncoderWheel.svg"; 59 | const char *webUiEmergencySwitchGraphic = "/img/emergencyStopSwitch.svg"; 60 | const char *webUiStepperGraphic = "/img/stepper.svg"; 61 | const char *webUiSwitchGraphic = "/img/switch.svg"; 62 | const char *webUiFaviconFile = "/favicon.ico"; 63 | const char *webUiRepositoryBasePath = "https://raw.githubusercontent.com/pkerspe/ESP-StepperMotor-Server-UI/master/data"; 64 | ESPStepperMotorServer *_serverRef; 65 | AsyncWebServer *_httpServer; 66 | }; 67 | 68 | #endif 69 | --------------------------------------------------------------------------------