├── .gitattributes ├── button.h ├── LICENSE.md ├── VersionHistory.txt ├── README.md ├── SerialCommand.h ├── .gitignore ├── EggDuino.ino ├── Helper_Functions.ino ├── SerialCommand.cpp ├── Functions.ino ├── AccelStepper.cpp └── AccelStepper.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /button.h: -------------------------------------------------------------------------------- 1 | /* 2 | * button.h 3 | * 4 | * Created: 04.05.2015 21:38:42 5 | * Author: Yogi 6 | */ 7 | 8 | 9 | #ifndef __BUTTON_H__ 10 | #define __BUTTON_H__ 11 | 12 | #include 13 | 14 | #define debounceDelay 50 15 | 16 | typedef void (*ActionCb)(void); 17 | 18 | class Button 19 | { 20 | 21 | private: 22 | long debounce; 23 | byte state:1; 24 | byte lastState:1; 25 | byte pin; 26 | ActionCb action; 27 | Button( const Button &c ); 28 | Button& operator=( const Button &c ); 29 | 30 | public: 31 | 32 | Button(byte p, ActionCb a): debounce(0), state(1), lastState(1), action(a), pin(p) { 33 | pinMode(pin, INPUT_PULLUP); 34 | } 35 | 36 | void check() { 37 | byte b = digitalRead(pin); 38 | long t = millis(); 39 | 40 | if (b != lastState) { 41 | debounce = t; 42 | } 43 | 44 | if ((t - debounce) > debounceDelay) { 45 | if (b != state) { 46 | state = b; 47 | 48 | if (!state) { 49 | (*action)(); 50 | } 51 | } 52 | } 53 | 54 | lastState = b; 55 | } 56 | }; //button 57 | 58 | #endif //__BUTTON_H__ 59 | 60 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) {{{year}}} {{{fullname}}} 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 | -------------------------------------------------------------------------------- /VersionHistory.txt: -------------------------------------------------------------------------------- 1 | 15.11.2015 v1.6a 2 | fixed bug in QP-Function 3 | 4 | 05.05.2015 v1.6 5 | imported some improvements from user "bartebor" 6 | fixed some timing issues 7 | added button-support via #ifdefine 8 | worked on penarm-shaking-bug 9 | restructured some codesegments 10 | 11 | 29.03.2015 v1.4 12 | added prototype for PRG-Button support. somebody has to test it. Connect pushbutton between Pin2 und GND. 13 | disabled ccordinate transform for 16-microstepping, because Inkscape sends in 16-microstepping, as well. 14 | removed commands for changing rotStepmodes 15 | fixed some minor bugs 16 | 17 | 26.03.2015 v1.3 18 | updated Accelstepper-Lib 19 | set Microstepping Factor 16 by default. 20 | fixed bug in Pen-position-transformation 21 | 22 | 23.04.2014 v1.2 23 | complete rework of Move-Algorithm, no position errors any more, complete in integer math //it was the hell to debug it... ;-) 24 | optimized timing of Ack-Answer 25 | added command QP "Query Pen", untested, answer might be wrong 0 <--> 1? 26 | optimized Init-String for EBBv13 27 | optimized some variable types to enhance memory performance 28 | changed enablePenMotor to 6 by default to match it to Spherebot Electronics 29 | changed default maxSpeeds from 1000 to 2000; 30 | 31 | 22.04.2014 V1.1 32 | bugfix Command SM - fixed rounding error in pen and rot-axis 33 | 34 | 17.04.2014 V1.0 35 | initial release 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Eggduino 2 | ==== 3 | 4 | Arduino Firmware for Eggbot / Spherebot with Inkscape-Integration 5 | 6 | Version 1.6a 7 | tested with Inkscape Portable 0.91, Eggbot Extension and patched eggbot.py 8 | 9 | Regards: Eggduino-Firmware by Joachim Cerny, 2015 10 | 11 | Thanks for the nice libs ACCELSTEPPER and SERIALCOMMAND, which made this project much easier. Thanks to the Eggbot-Team for such a funny and enjoyable concept! Thanks to my wife and my daughter for their patience. :-) 12 | 13 | Features: 14 | 15 | - Implemented Eggbot-Protocol-Version 2.1.0 16 | - Turn-on homing: switch-on position of pen will be taken as reference point. 17 | - No collision-detection!! 18 | - Supported Servos: At least one type ;-) I use Arduino Servo-Lib with TG9e- standard servo. 19 | - Full Arduino-Compatible. I used an Arduino Uno 20 | - Button-support (3 buttons) 21 | 22 | Tested and fully functional with Inkscape. 23 | 24 | Installation: 25 | 26 | - Upload Eggduino.ino with Arduino-IDE or similar tool to your Arudino (i.e. Uno) 27 | - Disable Autoreset on Arduinoboard (there are several ways to do this... Which one does not matter...) 28 | - Install Inkscape Tools with Eggbot extension. 29 | 30 | 1. Download and install the latest version of Inkscape (Version 0.91 or newer) 31 | 2. Download and install EggBot_250A.exe from: 32 | https://github.com/evil-mad/EggBot/releases/tag/v2.5.0 33 | 3. Download my patched ebb_serial.py from: 34 | 35 | https://github.com/plex3r/plotink/tree/master/libraries 36 | 37 | 4. Replace the existing ebb_serial.py in your inkscape extensions folder with the patched version. 38 | 39 | Ensure there are no other usb / com devices plugged in apart from your Eggbot. 40 | 41 | Start inskscape and it should now be able to connect to your eggbot. 42 | -------------------------------------------------------------------------------- /SerialCommand.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SerialCommand - A Wiring/Arduino library to tokenize and parse commands 3 | * received over a serial port. 4 | * 5 | * Copyright (C) 2012 Stefan Rado 6 | * Copyright (C) 2011 Steven Cogswell 7 | * http://husks.wordpress.com 8 | * 9 | * Version 20120522 10 | * 11 | * This library is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this library. If not, see . 23 | */ 24 | #ifndef SerialCommand_h 25 | #define SerialCommand_h 26 | 27 | #if defined(WIRING) && WIRING >= 100 28 | #include 29 | #elif defined(ARDUINO) && ARDUINO >= 100 30 | #include 31 | #else 32 | #include 33 | #endif 34 | #include 35 | 36 | // Size of the input buffer in bytes (maximum length of one command plus arguments) 37 | #define SERIALCOMMAND_BUFFER 63 38 | // Maximum length of a command excluding the terminating null 39 | #define SERIALCOMMAND_MAXCOMMANDLENGTH 20 40 | 41 | // Uncomment the next line to run the library in debug mode (verbose messages) 42 | //#define SERIALCOMMAND_DEBUG 43 | 44 | 45 | class SerialCommand { 46 | public: 47 | SerialCommand(); // Constructor 48 | void addCommand(const char *command, void(*function)()); // Add a command to the processing dictionary. 49 | void setDefaultHandler(void (*function)(const char *)); // A handler to call when no valid command received. 50 | 51 | void readSerial(); // Main entry point. 52 | void clearBuffer(); // Clears the input buffer. 53 | char *next(); // Returns pointer to next token found in command buffer (for getting arguments to commands). 54 | 55 | private: 56 | // Command/handler dictionary 57 | struct SerialCommandCallback { 58 | char command[SERIALCOMMAND_MAXCOMMANDLENGTH + 1]; 59 | void (*function)(); 60 | }; // Data structure to hold Command/Handler function key-value pairs 61 | SerialCommandCallback *commandList; // Actual definition for command/handler array 62 | byte commandCount; 63 | 64 | // Pointer to the default handler function 65 | void (*defaultHandler)(const char *); 66 | 67 | char delim[2]; // null-terminated list of character to be used as delimeters for tokenizing (default " ") 68 | char term; // Character that signals end of command (default '\n') 69 | 70 | char buffer[SERIALCOMMAND_BUFFER + 1]; // Buffer of stored characters while waiting for terminator character 71 | byte bufPos; // Current position in the buffer 72 | char *last; // State variable used by strtok_r during processing 73 | }; 74 | 75 | #endif //SerialCommand_h 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /EggDuino.ino: -------------------------------------------------------------------------------- 1 | /* Eggduino-Firmware by Joachim Cerny, 2014 2 | 3 | Thanks for the nice libs ACCELSTEPPER and SERIALCOMMAND, which made this project much easier. 4 | Thanks to the Eggbot-Team for such a funny and enjoable concept! 5 | Thanks to my wife and my daughter for their patience. :-) 6 | 7 | */ 8 | 9 | // implemented Eggbot-Protocol-Version v13 10 | // EBB-Command-Reference, I sourced from: http://www.schmalzhaus.com/EBB/EBBCommands.html 11 | // no homing sequence, switch-on position of pen will be taken as reference point. 12 | // No collision-detection!! 13 | // Supported Servos: I do not know, I use Arduino Servo Lib with TG9e- standard servo. 14 | // Note: Maximum-Speed in Inkscape is 1000 Steps/s. You could enter more, but then Pythonscript sends nonsense. 15 | // EBB-Coordinates are coming in for 16th-Microstepmode. The Coordinate-Transforms are done in weired integer-math. Be careful, when you diecide to modify settings. 16 | 17 | /* TODOs: 18 | 1 collision control via penMin/penMax 19 | 2 implement homing sequence via microswitch or optical device 20 | */ 21 | 22 | #include "AccelStepper.h" // nice lib from http://www.airspayce.com/mikem/arduino/AccelStepper/ 23 | #include 24 | #include "SerialCommand.h" //nice lib from Stefan Rado, https://github.com/kroimon/Arduino-SerialCommand 25 | #include 26 | #include "button.h" 27 | 28 | #define initSting "EBBv13_and_above Protocol emulated by Eggduino-Firmware V1.6a" 29 | //Rotational Stepper: 30 | #define step1 2 31 | #define dir1 5 32 | #define enableRotMotor 8 33 | #define rotMicrostep 16 //MicrostepMode, only 1,2,4,8,16 allowed, because of Integer-Math in this Sketch 34 | //Pen Stepper: 35 | #define step2 3 36 | #define dir2 6 37 | #define enablePenMotor 8 38 | #define penMicrostep 16 //MicrostepMode, only 1,2,4,8,16 allowed, because of Integer-Math in this Sketch 39 | 40 | #define servoPin 4 //Servo 41 | 42 | // EXTRAFEATURES - UNCOMMENT TO USE THEM ------------------------------------------------------------------- 43 | 44 | // #define prgButton 2 // PRG button 45 | // #define penToggleButton 12 // pen up/down button 46 | // #define motorsButton 4 // motors enable button 47 | 48 | //----------------------------------------------------------------------------------------------------------- 49 | 50 | #define penUpPosEEAddress ((uint16_t *)0) 51 | #define penDownPosEEAddress ((uint16_t *)2) 52 | 53 | //make Objects 54 | AccelStepper rotMotor(1, step1, dir1); 55 | AccelStepper penMotor(1, step2, dir2); 56 | Servo penServo; 57 | SerialCommand SCmd; 58 | //create Buttons 59 | #ifdef prgButton 60 | Button prgButtonToggle(prgButton, setprgButtonState); 61 | #endif 62 | #ifdef penToggleButton 63 | Button penToggle(penToggleButton, doTogglePen); 64 | #endif 65 | #ifdef motorsButton 66 | Button motorsToggle(motorsButton, toggleMotors); 67 | #endif 68 | // Variables... be careful, by messing around here, everything has a reason and crossrelations... 69 | int penMin=0; 70 | int penMax=0; 71 | int penUpPos=5; //can be overwritten from EBB-Command SC 72 | int penDownPos=20; //can be overwritten from EBB-Command SC 73 | int servoRateUp=0; //from EBB-Protocol not implemented on machine-side 74 | int servoRateDown=0; //from EBB-Protocol not implemented on machine-side 75 | long rotStepError=0; 76 | long penStepError=0; 77 | int penState=penUpPos; 78 | uint32_t nodeCount=0; 79 | unsigned int layer=0; 80 | boolean prgButtonState=0; 81 | uint8_t rotStepCorrection = 16/rotMicrostep ; //devide EBB-Coordinates by this factor to get EGGduino-Steps 82 | uint8_t penStepCorrection = 16/penMicrostep ; //devide EBB-Coordinates by this factor to get EGGduino-Steps 83 | float rotSpeed=0; 84 | float penSpeed=0; // these are local variables for Function SteppermotorMove-Command, but for performance-reasons it will be initialized here 85 | boolean motorsEnabled = 0; 86 | 87 | void setup() { 88 | Serial.begin(9600); 89 | makeComInterface(); 90 | initHardware(); 91 | } 92 | 93 | void loop() { 94 | moveOneStep(); 95 | 96 | SCmd.readSerial(); 97 | 98 | #ifdef penToggleButton 99 | penToggle.check(); 100 | #endif 101 | 102 | #ifdef motorsButton 103 | motorsToggle.check(); 104 | #endif 105 | 106 | #ifdef prgButton 107 | prgButtonToggle.check(); 108 | #endif 109 | } 110 | -------------------------------------------------------------------------------- /Helper_Functions.ino: -------------------------------------------------------------------------------- 1 | void initHardware(){ 2 | // enable eeprom wait in avr/eeprom.h functions 3 | SPMCSR &= ~SELFPRGEN; 4 | 5 | loadPenPosFromEE(); 6 | 7 | pinMode(enableRotMotor, OUTPUT); 8 | pinMode(enablePenMotor, OUTPUT); 9 | 10 | rotMotor.setMaxSpeed(2000.0); 11 | rotMotor.setAcceleration(10000.0); 12 | penMotor.setMaxSpeed(2000.0); 13 | penMotor.setAcceleration(10000.0); 14 | motorsOff(); 15 | penServo.attach(servoPin); 16 | penServo.write(penState); 17 | } 18 | 19 | inline void loadPenPosFromEE() { 20 | penUpPos = eeprom_read_word(penUpPosEEAddress); 21 | penDownPos = eeprom_read_word(penDownPosEEAddress); 22 | penState = penUpPos; 23 | } 24 | 25 | inline void storePenUpPosInEE() { 26 | eeprom_update_word(penUpPosEEAddress, penUpPos); 27 | } 28 | 29 | inline void storePenDownPosInEE() { 30 | eeprom_update_word(penDownPosEEAddress, penDownPos); 31 | } 32 | 33 | inline void sendAck(){ 34 | Serial.print("OK\r\n"); 35 | } 36 | 37 | inline void sendError(){ 38 | Serial.print("unknown CMD\r\n"); 39 | } 40 | 41 | void motorsOff() { 42 | digitalWrite(enableRotMotor, HIGH); 43 | digitalWrite(enablePenMotor, HIGH); 44 | motorsEnabled = 0; 45 | } 46 | 47 | void motorsOn() { 48 | digitalWrite(enableRotMotor, LOW) ; 49 | digitalWrite(enablePenMotor, LOW) ; 50 | motorsEnabled = 1; 51 | } 52 | 53 | void toggleMotors() { 54 | if (motorsEnabled) { 55 | motorsOff(); 56 | } else { 57 | motorsOn(); 58 | } 59 | } 60 | 61 | bool parseSMArgs(uint16_t *duration, int *penStepsEBB, int *rotStepsEBB) { 62 | char *arg1; 63 | char *arg2; 64 | char *arg3; 65 | arg1 = SCmd.next(); 66 | if (arg1 != NULL) { 67 | *duration = atoi(arg1); 68 | arg2 = SCmd.next(); 69 | } 70 | if (arg2 != NULL) { 71 | *penStepsEBB = atoi(arg2); 72 | arg3 = SCmd.next(); 73 | } 74 | if (arg3 != NULL) { 75 | *rotStepsEBB = atoi(arg3); 76 | 77 | return true; 78 | } 79 | 80 | return false; 81 | } 82 | 83 | void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB) { 84 | if (!motorsEnabled) { 85 | motorsOn(); 86 | } 87 | if( (1 == rotStepCorrection) && (1 == penStepCorrection) ){ // if coordinatessystems are identical 88 | //set Coordinates and Speed 89 | rotMotor.move(rotStepsEBB); 90 | rotMotor.setSpeed( abs( (float)rotStepsEBB * (float)1000 / (float)duration ) ); 91 | penMotor.move(penStepsEBB); 92 | penMotor.setSpeed( abs( (float)penStepsEBB * (float)1000 / (float)duration ) ); 93 | } else { 94 | //incoming EBB-Steps will be multiplied by 16, then Integer-maths is done, result will be divided by 16 95 | // This make thinks here really complicated, but floating point-math kills performance and memory, believe me... I tried... 96 | long rotSteps = ( (long)rotStepsEBB * 16 / rotStepCorrection) + (long)rotStepError; //correct incoming EBB-Steps to our microstep-Setting and multiply by 16 to avoid floatingpoint... 97 | long penSteps = ( (long)penStepsEBB * 16 / penStepCorrection) + (long)penStepError; 98 | 99 | int rotStepsToGo = (int) (rotSteps/16); //Calc Steps to go, which are possible on our machine 100 | int penStepsToGo = (int) (penSteps/16); 101 | 102 | rotStepError = (long)rotSteps - ((long) rotStepsToGo * (long)16); // calc Position-Error, if there is one 103 | penStepError = (long)penSteps - ((long) penStepsToGo * (long)16); 104 | 105 | long temp_rotSpeed = ((long)rotStepsToGo * (long)1000 / (long)duration ); // calc Speed in Integer Math 106 | long temp_penSpeed = ((long)penStepsToGo * (long)1000 / (long)duration ) ; 107 | 108 | float rotSpeed= (float) abs(temp_rotSpeed); // type cast 109 | float penSpeed= (float) abs(temp_penSpeed); 110 | 111 | //set Coordinates and Speed 112 | rotMotor.move(rotStepsToGo); // finally, let us set the target position... 113 | rotMotor.setSpeed(rotSpeed); // and the Speed! 114 | penMotor.move(penStepsToGo); 115 | penMotor.setSpeed( penSpeed ); 116 | } 117 | } 118 | 119 | void moveOneStep() { 120 | if ( penMotor.distanceToGo() || rotMotor.distanceToGo() ) { 121 | penMotor.runSpeedToPosition(); // Moving.... moving... moving.... 122 | rotMotor.runSpeedToPosition(); 123 | } 124 | } 125 | 126 | void moveToDestination() { 127 | while ( penMotor.distanceToGo() || rotMotor.distanceToGo() ) { 128 | penMotor.runSpeedToPosition(); // Moving.... moving... moving.... 129 | rotMotor.runSpeedToPosition(); 130 | } 131 | } 132 | 133 | void setprgButtonState(){ 134 | prgButtonState = 1; 135 | } 136 | -------------------------------------------------------------------------------- /SerialCommand.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * SerialCommand - A Wiring/Arduino library to tokenize and parse commands 3 | * received over a serial port. 4 | * 5 | * Copyright (C) 2012 Stefan Rado 6 | * Copyright (C) 2011 Steven Cogswell 7 | * http://husks.wordpress.com 8 | * 9 | * Version 20120522 10 | * 11 | * This library is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this library. If not, see . 23 | */ 24 | #include "SerialCommand.h" 25 | 26 | /** 27 | * Constructor makes sure some things are set. 28 | */ 29 | SerialCommand::SerialCommand() 30 | : commandList(NULL), 31 | commandCount(0), 32 | defaultHandler(NULL), 33 | term('\r'), // default terminator for commands, newline character 34 | last(NULL) 35 | { 36 | strcpy(delim, ","); // strtok_r needs a null-terminated string 37 | clearBuffer(); 38 | } 39 | 40 | /** 41 | * Adds a "command" and a handler function to the list of available commands. 42 | * This is used for matching a found token in the buffer, and gives the pointer 43 | * to the handler function to deal with it. 44 | */ 45 | void SerialCommand::addCommand(const char *command, void (*function)()) { 46 | #ifdef SERIALCOMMAND_DEBUG 47 | Serial.print("Adding command ("); 48 | Serial.print(commandCount); 49 | Serial.print("): "); 50 | Serial.println(command); 51 | #endif 52 | 53 | commandList = (SerialCommandCallback *) realloc(commandList, (commandCount + 1) * sizeof(SerialCommandCallback)); 54 | strncpy(commandList[commandCount].command, command, SERIALCOMMAND_MAXCOMMANDLENGTH); 55 | commandList[commandCount].function = function; 56 | commandCount++; 57 | } 58 | 59 | /** 60 | * This sets up a handler to be called in the event that the receveived command string 61 | * isn't in the list of commands. 62 | */ 63 | void SerialCommand::setDefaultHandler(void (*function)(const char *)) { 64 | defaultHandler = function; 65 | } 66 | 67 | 68 | /** 69 | * This checks the Serial stream for characters, and assembles them into a buffer. 70 | * When the terminator character (default '\n') is seen, it starts parsing the 71 | * buffer for a prefix command, and calls handlers setup by addCommand() member 72 | */ 73 | void SerialCommand::readSerial() { 74 | while (Serial.available() > 0) { 75 | char inChar = Serial.read(); // Read single available character, there may be more waiting 76 | #ifdef SERIALCOMMAND_DEBUG 77 | Serial.print(inChar); // Echo back to serial stream 78 | #endif 79 | 80 | if (inChar == term) { // Check for the terminator (default '\r') meaning end of command 81 | #ifdef SERIALCOMMAND_DEBUG 82 | Serial.print("Received: "); 83 | Serial.println(buffer); 84 | #endif 85 | 86 | char *command = strtok_r(buffer, delim, &last); // Search for command at start of buffer 87 | if (command != NULL) { 88 | boolean matched = false; 89 | for (int i = 0; i < commandCount; i++) { 90 | #ifdef SERIALCOMMAND_DEBUG 91 | Serial.print("Comparing ["); 92 | Serial.print(command); 93 | Serial.print("] to ["); 94 | Serial.print(commandList[i].command); 95 | Serial.println("]"); 96 | #endif 97 | 98 | // Compare the found command against the list of known commands for a match 99 | if (strncmp(command, commandList[i].command, SERIALCOMMAND_MAXCOMMANDLENGTH) == 0) { 100 | #ifdef SERIALCOMMAND_DEBUG 101 | Serial.print("Matched Command: "); 102 | Serial.println(command); 103 | #endif 104 | 105 | // Execute the stored handler function for the command 106 | (*commandList[i].function)(); 107 | matched = true; 108 | break; 109 | } 110 | } 111 | if (!matched && (defaultHandler != NULL)) { 112 | (*defaultHandler)(command); 113 | } 114 | } 115 | clearBuffer(); 116 | } 117 | else if (isprint(inChar)) { // Only printable characters into the buffer 118 | if (bufPos < SERIALCOMMAND_BUFFER) { 119 | buffer[bufPos++] = inChar; // Put character into buffer 120 | buffer[bufPos] = '\0'; // Null terminate 121 | } else { 122 | #ifdef SERIALCOMMAND_DEBUG 123 | Serial.println("Line buffer is full - increase SERIALCOMMAND_BUFFER"); 124 | #endif 125 | } 126 | } 127 | } 128 | } 129 | 130 | /* 131 | * Clear the input buffer. 132 | */ 133 | void SerialCommand::clearBuffer() { 134 | buffer[0] = '\0'; 135 | bufPos = 0; 136 | } 137 | 138 | /** 139 | * Retrieve the next token ("word" or "argument") from the command buffer. 140 | * Returns NULL if no more tokens exist. 141 | */ 142 | char *SerialCommand::next() { 143 | return strtok_r(NULL, delim, &last); 144 | } 145 | -------------------------------------------------------------------------------- /Functions.ino: -------------------------------------------------------------------------------- 1 | 2 | void makeComInterface(){ 3 | SCmd.addCommand("v",sendVersion); 4 | SCmd.addCommand("EM",enableMotors); 5 | SCmd.addCommand("SC",stepperModeConfigure); 6 | SCmd.addCommand("SP",setPen); 7 | SCmd.addCommand("SM",stepperMove); 8 | SCmd.addCommand("SE",ignore); 9 | SCmd.addCommand("TP",togglePen); 10 | SCmd.addCommand("PO",ignore); //Engraver command, not implemented, gives fake answer 11 | SCmd.addCommand("NI",nodeCountIncrement); 12 | SCmd.addCommand("ND",nodeCountDecrement); 13 | SCmd.addCommand("SN",setNodeCount); 14 | SCmd.addCommand("QN",queryNodeCount); 15 | SCmd.addCommand("SL",setLayer); 16 | SCmd.addCommand("QL",queryLayer); 17 | SCmd.addCommand("QP",queryPen); 18 | SCmd.addCommand("QB",queryButton); //"PRG" Button, 19 | SCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?") 20 | } 21 | 22 | void queryPen() { 23 | char state; 24 | if (penState==penUpPos) 25 | state='1'; 26 | else 27 | state='0'; 28 | Serial.print(String(state)+"\r\n"); 29 | sendAck(); 30 | } 31 | 32 | void queryButton() { 33 | Serial.print(String(prgButtonState) +"\r\n"); 34 | sendAck(); 35 | prgButtonState = 0; 36 | } 37 | 38 | void queryLayer() { 39 | Serial.print(String(layer) +"\r\n"); 40 | sendAck(); 41 | } 42 | 43 | void setLayer() { 44 | uint32_t value=0; 45 | char *arg1; 46 | arg1 = SCmd.next(); 47 | if (arg1 != NULL) { 48 | value = atoi(arg1); 49 | layer=value; 50 | sendAck(); 51 | } 52 | else 53 | sendError(); 54 | } 55 | 56 | void queryNodeCount() { 57 | Serial.print(String(nodeCount) +"\r\n"); 58 | sendAck(); 59 | 60 | } 61 | 62 | void setNodeCount() { 63 | uint32_t value=0; 64 | char *arg1; 65 | arg1 = SCmd.next(); 66 | if (arg1 != NULL) { 67 | value = atoi(arg1); 68 | nodeCount=value; 69 | sendAck(); 70 | } 71 | else 72 | sendError(); 73 | } 74 | 75 | void nodeCountIncrement() { 76 | nodeCount=nodeCount++; 77 | sendAck(); 78 | } 79 | 80 | void nodeCountDecrement() { 81 | nodeCount=nodeCount--; 82 | sendAck(); 83 | } 84 | 85 | void stepperMove() { 86 | uint16_t duration=0; //in ms 87 | int penStepsEBB=0; //Pen 88 | int rotStepsEBB=0; //Rot 89 | 90 | moveToDestination(); 91 | 92 | if (!parseSMArgs(&duration, &penStepsEBB, &rotStepsEBB)) { 93 | sendError(); 94 | return; 95 | } 96 | 97 | sendAck(); 98 | 99 | if ( (penStepsEBB==0) && (rotStepsEBB==0) ) { 100 | delay(duration); 101 | return; 102 | } 103 | 104 | prepareMove(duration, penStepsEBB, rotStepsEBB); 105 | } 106 | 107 | void setPen(){ 108 | int cmd; 109 | int value; 110 | char *arg; 111 | 112 | moveToDestination(); 113 | 114 | arg = SCmd.next(); 115 | if (arg != NULL) { 116 | cmd = atoi(arg); 117 | switch (cmd) { 118 | case 0: 119 | penServo.write(penUpPos); 120 | penState=penUpPos; 121 | break; 122 | 123 | case 1: 124 | penServo.write(penDownPos); 125 | penState=penDownPos; 126 | break; 127 | 128 | default: 129 | sendError(); 130 | } 131 | } 132 | char *val; 133 | val = SCmd.next(); 134 | if (val != NULL) { 135 | value = atoi(val); 136 | sendAck(); 137 | delay(value); 138 | } 139 | if (val==NULL && arg !=NULL) { 140 | sendAck(); 141 | delay(500); 142 | } 143 | // Serial.println("delay"); 144 | if (val==NULL && arg ==NULL) 145 | sendError(); 146 | } 147 | 148 | void togglePen(){ 149 | int value; 150 | char *arg; 151 | 152 | moveToDestination(); 153 | 154 | arg = SCmd.next(); 155 | if (arg != NULL) 156 | value = atoi(arg); 157 | else 158 | value = 500; 159 | 160 | doTogglePen(); 161 | sendAck(); 162 | delay(value); 163 | } 164 | 165 | void doTogglePen() { 166 | if (penState==penUpPos) { 167 | penServo.write(penDownPos); 168 | penState=penDownPos; 169 | } else { 170 | penServo.write(penUpPos); 171 | penState=penUpPos; 172 | } 173 | } 174 | 175 | void enableMotors(){ 176 | int cmd; 177 | int value; 178 | char *arg; 179 | char *val; 180 | arg = SCmd.next(); 181 | if (arg != NULL) 182 | cmd = atoi(arg); 183 | val = SCmd.next(); 184 | if (val != NULL) 185 | value = atoi(val); 186 | //values parsed 187 | if ((arg != NULL) && (val == NULL)){ 188 | switch (cmd) { 189 | case 0: motorsOff(); 190 | sendAck(); 191 | break; 192 | case 1: motorsOn(); 193 | sendAck(); 194 | break; 195 | default: 196 | sendError(); 197 | } 198 | } 199 | //the following implementaion is a little bit cheated, because i did not know, how to implement different values for first and second argument. 200 | if ((arg != NULL) && (val != NULL)){ 201 | switch (value) { 202 | case 0: motorsOff(); 203 | sendAck(); 204 | break; 205 | case 1: motorsOn(); 206 | sendAck(); 207 | break; 208 | default: 209 | sendError(); 210 | } 211 | } 212 | } 213 | 214 | void stepperModeConfigure(){ 215 | int cmd; 216 | int value; 217 | char *arg; 218 | arg = SCmd.next(); 219 | if (arg != NULL) 220 | cmd = atoi(arg); 221 | char *val; 222 | val = SCmd.next(); 223 | if (val != NULL) 224 | value = atoi(val); 225 | if ((arg != NULL) && (val != NULL)){ 226 | switch (cmd) { 227 | case 4: penDownPos= (int) ((float) (value-6000)/(float) 133.3); // transformation from EBB to PWM-Servo 228 | storePenDownPosInEE(); 229 | sendAck(); 230 | break; 231 | case 5: penUpPos= (int)((float) (value-6000)/(float) 133.3); // transformation from EBB to PWM-Servo 232 | storePenUpPosInEE(); 233 | sendAck(); 234 | break; 235 | case 6: //rotMin=value; ignored 236 | sendAck(); 237 | break; 238 | case 7: //rotMax=value; ignored 239 | sendAck(); 240 | break; 241 | case 11: servoRateUp=value; 242 | sendAck(); 243 | break; 244 | case 12: servoRateDown=value; 245 | sendAck(); 246 | break; 247 | default: 248 | sendError(); 249 | } 250 | } 251 | } 252 | 253 | void sendVersion(){ 254 | Serial.print(initSting); 255 | Serial.print("\r\n"); 256 | } 257 | 258 | void unrecognized(const char *command){ 259 | sendError(); 260 | } 261 | 262 | void ignore(){ 263 | sendAck(); 264 | } 265 | -------------------------------------------------------------------------------- /AccelStepper.cpp: -------------------------------------------------------------------------------- 1 | // AccelStepper.cpp 2 | // 3 | // Copyright (C) 2009-2013 Mike McCauley 4 | // $Id: AccelStepper.cpp,v 1.19 2014/10/31 06:05:27 mikem Exp mikem $ 5 | 6 | #include "AccelStepper.h" 7 | 8 | #if 0 9 | // Some debugging assistance 10 | void dump(uint8_t* p, int l) 11 | { 12 | int i; 13 | 14 | for (i = 0; i < l; i++) 15 | { 16 | Serial.print(p[i], HEX); 17 | Serial.print(" "); 18 | } 19 | Serial.println(""); 20 | } 21 | #endif 22 | 23 | void AccelStepper::moveTo(long absolute) 24 | { 25 | if (_targetPos != absolute) 26 | { 27 | _targetPos = absolute; 28 | computeNewSpeed(); 29 | // compute new n? 30 | } 31 | } 32 | 33 | void AccelStepper::move(long relative) 34 | { 35 | moveTo(_currentPos + relative); 36 | } 37 | 38 | // Implements steps according to the current step interval 39 | // You must call this at least once per step 40 | // returns true if a step occurred 41 | boolean AccelStepper::runSpeed() 42 | { 43 | // Dont do anything unless we actually have a step interval 44 | if (!_stepInterval) 45 | return false; 46 | 47 | unsigned long time = micros(); 48 | unsigned long nextStepTime = _lastStepTime + _stepInterval; 49 | // Gymnastics to detect wrapping of either the nextStepTime and/or the current time 50 | if ( ((nextStepTime >= _lastStepTime) && ((time >= nextStepTime) || (time < _lastStepTime))) 51 | || ((nextStepTime < _lastStepTime) && ((time >= nextStepTime) && (time < _lastStepTime)))) 52 | { 53 | if (_direction == DIRECTION_CW) 54 | { 55 | // Clockwise 56 | _currentPos += 1; 57 | } 58 | else 59 | { 60 | // Anticlockwise 61 | _currentPos -= 1; 62 | } 63 | step(_currentPos); 64 | 65 | _lastStepTime = time; 66 | return true; 67 | } 68 | else 69 | { 70 | return false; 71 | } 72 | } 73 | 74 | long AccelStepper::distanceToGo() 75 | { 76 | return _targetPos - _currentPos; 77 | } 78 | 79 | long AccelStepper::targetPosition() 80 | { 81 | return _targetPos; 82 | } 83 | 84 | long AccelStepper::currentPosition() 85 | { 86 | return _currentPos; 87 | } 88 | 89 | // Useful during initialisations or after initial positioning 90 | // Sets speed to 0 91 | void AccelStepper::setCurrentPosition(long position) 92 | { 93 | _targetPos = _currentPos = position; 94 | _n = 0; 95 | _stepInterval = 0; 96 | } 97 | 98 | void AccelStepper::computeNewSpeed() 99 | { 100 | long distanceTo = distanceToGo(); // +ve is clockwise from curent location 101 | 102 | long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16 103 | 104 | if (distanceTo == 0 && stepsToStop <= 1) 105 | { 106 | // We are at the target and its time to stop 107 | _stepInterval = 0; 108 | _speed = 0.0; 109 | _n = 0; 110 | return; 111 | } 112 | 113 | if (distanceTo > 0) 114 | { 115 | // We are anticlockwise from the target 116 | // Need to go clockwise from here, maybe decelerate now 117 | if (_n > 0) 118 | { 119 | // Currently accelerating, need to decel now? Or maybe going the wrong way? 120 | if ((stepsToStop >= distanceTo) || _direction == DIRECTION_CCW) 121 | _n = -stepsToStop; // Start deceleration 122 | } 123 | else if (_n < 0) 124 | { 125 | // Currently decelerating, need to accel again? 126 | if ((stepsToStop < distanceTo) && _direction == DIRECTION_CW) 127 | _n = -_n; // Start accceleration 128 | } 129 | } 130 | else if (distanceTo < 0) 131 | { 132 | // We are clockwise from the target 133 | // Need to go anticlockwise from here, maybe decelerate 134 | if (_n > 0) 135 | { 136 | // Currently accelerating, need to decel now? Or maybe going the wrong way? 137 | if ((stepsToStop >= -distanceTo) || _direction == DIRECTION_CW) 138 | _n = -stepsToStop; // Start deceleration 139 | } 140 | else if (_n < 0) 141 | { 142 | // Currently decelerating, need to accel again? 143 | if ((stepsToStop < -distanceTo) && _direction == DIRECTION_CCW) 144 | _n = -_n; // Start accceleration 145 | } 146 | } 147 | 148 | // Need to accelerate or decelerate 149 | if (_n == 0) 150 | { 151 | // First step from stopped 152 | _cn = _c0; 153 | _direction = (distanceTo > 0) ? DIRECTION_CW : DIRECTION_CCW; 154 | } 155 | else 156 | { 157 | // Subsequent step. Works for accel (n is +_ve) and decel (n is -ve). 158 | _cn = _cn - ((2.0 * _cn) / ((4.0 * _n) + 1)); // Equation 13 159 | _cn = max(_cn, _cmin); 160 | } 161 | _n++; 162 | _stepInterval = _cn; 163 | _speed = 1000000.0 / _cn; 164 | if (_direction == DIRECTION_CCW) 165 | _speed = -_speed; 166 | 167 | #if 0 168 | Serial.println(_speed); 169 | Serial.println(_acceleration); 170 | Serial.println(_cn); 171 | Serial.println(_c0); 172 | Serial.println(_n); 173 | Serial.println(_stepInterval); 174 | Serial.println(distanceTo); 175 | Serial.println(stepsToStop); 176 | Serial.println("-----"); 177 | #endif 178 | } 179 | 180 | // Run the motor to implement speed and acceleration in order to proceed to the target position 181 | // You must call this at least once per step, preferably in your main loop 182 | // If the motor is in the desired position, the cost is very small 183 | // returns true if the motor is still running to the target position. 184 | boolean AccelStepper::run() 185 | { 186 | if (runSpeed()) 187 | computeNewSpeed(); 188 | return _speed != 0.0 || distanceToGo() != 0; 189 | } 190 | 191 | AccelStepper::AccelStepper(uint8_t interface, uint8_t pin1, uint8_t pin2, uint8_t pin3, uint8_t pin4, bool enable) 192 | { 193 | _interface = interface; 194 | _currentPos = 0; 195 | _targetPos = 0; 196 | _speed = 0.0; 197 | _maxSpeed = 1.0; 198 | _acceleration = 0.0; 199 | _sqrt_twoa = 1.0; 200 | _stepInterval = 0; 201 | _minPulseWidth = 1; 202 | _enablePin = 0xff; 203 | _lastStepTime = 0; 204 | _pin[0] = pin1; 205 | _pin[1] = pin2; 206 | _pin[2] = pin3; 207 | _pin[3] = pin4; 208 | 209 | // NEW 210 | _n = 0; 211 | _c0 = 0.0; 212 | _cn = 0.0; 213 | _cmin = 1.0; 214 | _direction = DIRECTION_CCW; 215 | 216 | int i; 217 | for (i = 0; i < 4; i++) 218 | _pinInverted[i] = 0; 219 | if (enable) 220 | enableOutputs(); 221 | // Some reasonable default 222 | setAcceleration(1); 223 | } 224 | 225 | AccelStepper::AccelStepper(void (*forward)(), void (*backward)()) 226 | { 227 | _interface = 0; 228 | _currentPos = 0; 229 | _targetPos = 0; 230 | _speed = 0.0; 231 | _maxSpeed = 1.0; 232 | _acceleration = 0.0; 233 | _sqrt_twoa = 1.0; 234 | _stepInterval = 0; 235 | _minPulseWidth = 1; 236 | _enablePin = 0xff; 237 | _lastStepTime = 0; 238 | _pin[0] = 0; 239 | _pin[1] = 0; 240 | _pin[2] = 0; 241 | _pin[3] = 0; 242 | _forward = forward; 243 | _backward = backward; 244 | 245 | // NEW 246 | _n = 0; 247 | _c0 = 0.0; 248 | _cn = 0.0; 249 | _cmin = 1.0; 250 | _direction = DIRECTION_CCW; 251 | 252 | int i; 253 | for (i = 0; i < 4; i++) 254 | _pinInverted[i] = 0; 255 | // Some reasonable default 256 | setAcceleration(1); 257 | } 258 | 259 | void AccelStepper::setMaxSpeed(float speed) 260 | { 261 | if (_maxSpeed != speed) 262 | { 263 | _maxSpeed = speed; 264 | _cmin = 1000000.0 / speed; 265 | // Recompute _n from current speed and adjust speed if accelerating or cruising 266 | if (_n > 0) 267 | { 268 | _n = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16 269 | computeNewSpeed(); 270 | } 271 | } 272 | } 273 | 274 | void AccelStepper::setAcceleration(float acceleration) 275 | { 276 | if (acceleration == 0.0) 277 | return; 278 | if (_acceleration != acceleration) 279 | { 280 | // Recompute _n per Equation 17 281 | _n = _n * (_acceleration / acceleration); 282 | // New c0 per Equation 7, with correction per Equation 15 283 | _c0 = 0.676 * sqrt(2.0 / acceleration) * 1000000.0; // Equation 15 284 | _acceleration = acceleration; 285 | computeNewSpeed(); 286 | } 287 | } 288 | 289 | void AccelStepper::setSpeed(float speed) 290 | { 291 | if (speed == _speed) 292 | return; 293 | speed = constrain(speed, -_maxSpeed, _maxSpeed); 294 | if (speed == 0.0) 295 | _stepInterval = 0; 296 | else 297 | { 298 | _stepInterval = fabs(1000000.0 / speed); 299 | _direction = (speed > 0.0) ? DIRECTION_CW : DIRECTION_CCW; 300 | } 301 | _speed = speed; 302 | } 303 | 304 | float AccelStepper::speed() 305 | { 306 | return _speed; 307 | } 308 | 309 | // Subclasses can override 310 | void AccelStepper::step(long step) 311 | { 312 | switch (_interface) 313 | { 314 | case FUNCTION: 315 | step0(step); 316 | break; 317 | 318 | case DRIVER: 319 | step1(step); 320 | break; 321 | 322 | case FULL2WIRE: 323 | step2(step); 324 | break; 325 | 326 | case FULL3WIRE: 327 | step3(step); 328 | break; 329 | 330 | case FULL4WIRE: 331 | step4(step); 332 | break; 333 | 334 | case HALF3WIRE: 335 | step6(step); 336 | break; 337 | 338 | case HALF4WIRE: 339 | step8(step); 340 | break; 341 | } 342 | } 343 | 344 | // You might want to override this to implement eg serial output 345 | // bit 0 of the mask corresponds to _pin[0] 346 | // bit 1 of the mask corresponds to _pin[1] 347 | // .... 348 | void AccelStepper::setOutputPins(uint8_t mask) 349 | { 350 | uint8_t numpins = 2; 351 | if (_interface == FULL4WIRE || _interface == HALF4WIRE) 352 | numpins = 4; 353 | else if (_interface == FULL3WIRE || _interface == HALF3WIRE) 354 | numpins = 3; 355 | uint8_t i; 356 | for (i = 0; i < numpins; i++) 357 | digitalWrite(_pin[i], (mask & (1 << i)) ? (HIGH ^ _pinInverted[i]) : (LOW ^ _pinInverted[i])); 358 | } 359 | 360 | // 0 pin step function (ie for functional usage) 361 | void AccelStepper::step0(long step) 362 | { 363 | if (_speed > 0) 364 | _forward(); 365 | else 366 | _backward(); 367 | } 368 | 369 | // 1 pin step function (ie for stepper drivers) 370 | // This is passed the current step number (0 to 7) 371 | // Subclasses can override 372 | void AccelStepper::step1(long step) 373 | { 374 | // _pin[0] is step, _pin[1] is direction 375 | setOutputPins(_direction ? 0b10 : 0b00); // Set direction first else get rogue pulses 376 | setOutputPins(_direction ? 0b11 : 0b01); // step HIGH 377 | // Caution 200ns setup time 378 | // Delay the minimum allowed pulse width 379 | delayMicroseconds(_minPulseWidth); 380 | setOutputPins(_direction ? 0b10 : 0b00); // step LOW 381 | 382 | } 383 | 384 | 385 | // 2 pin step function 386 | // This is passed the current step number (0 to 7) 387 | // Subclasses can override 388 | void AccelStepper::step2(long step) 389 | { 390 | switch (step & 0x3) 391 | { 392 | case 0: /* 01 */ 393 | setOutputPins(0b10); 394 | break; 395 | 396 | case 1: /* 11 */ 397 | setOutputPins(0b11); 398 | break; 399 | 400 | case 2: /* 10 */ 401 | setOutputPins(0b01); 402 | break; 403 | 404 | case 3: /* 00 */ 405 | setOutputPins(0b00); 406 | break; 407 | } 408 | } 409 | // 3 pin step function 410 | // This is passed the current step number (0 to 7) 411 | // Subclasses can override 412 | void AccelStepper::step3(long step) 413 | { 414 | switch (step % 3) 415 | { 416 | case 0: // 100 417 | setOutputPins(0b100); 418 | break; 419 | 420 | case 1: // 001 421 | setOutputPins(0b001); 422 | break; 423 | 424 | case 2: //010 425 | setOutputPins(0b010); 426 | break; 427 | 428 | } 429 | } 430 | 431 | // 4 pin step function for half stepper 432 | // This is passed the current step number (0 to 7) 433 | // Subclasses can override 434 | void AccelStepper::step4(long step) 435 | { 436 | switch (step & 0x3) 437 | { 438 | case 0: // 1010 439 | setOutputPins(0b0101); 440 | break; 441 | 442 | case 1: // 0110 443 | setOutputPins(0b0110); 444 | break; 445 | 446 | case 2: //0101 447 | setOutputPins(0b1010); 448 | break; 449 | 450 | case 3: //1001 451 | setOutputPins(0b1001); 452 | break; 453 | } 454 | } 455 | 456 | // 3 pin half step function 457 | // This is passed the current step number (0 to 7) 458 | // Subclasses can override 459 | void AccelStepper::step6(long step) 460 | { 461 | switch (step % 6) 462 | { 463 | case 0: // 100 464 | setOutputPins(0b100); 465 | break; 466 | 467 | case 1: // 101 468 | setOutputPins(0b101); 469 | break; 470 | 471 | case 2: // 001 472 | setOutputPins(0b001); 473 | break; 474 | 475 | case 3: // 011 476 | setOutputPins(0b011); 477 | break; 478 | 479 | case 4: // 010 480 | setOutputPins(0b010); 481 | break; 482 | 483 | case 5: // 011 484 | setOutputPins(0b110); 485 | break; 486 | 487 | } 488 | } 489 | 490 | // 4 pin half step function 491 | // This is passed the current step number (0 to 7) 492 | // Subclasses can override 493 | void AccelStepper::step8(long step) 494 | { 495 | switch (step & 0x7) 496 | { 497 | case 0: // 1000 498 | setOutputPins(0b0001); 499 | break; 500 | 501 | case 1: // 1010 502 | setOutputPins(0b0101); 503 | break; 504 | 505 | case 2: // 0010 506 | setOutputPins(0b0100); 507 | break; 508 | 509 | case 3: // 0110 510 | setOutputPins(0b0110); 511 | break; 512 | 513 | case 4: // 0100 514 | setOutputPins(0b0010); 515 | break; 516 | 517 | case 5: //0101 518 | setOutputPins(0b1010); 519 | break; 520 | 521 | case 6: // 0001 522 | setOutputPins(0b1000); 523 | break; 524 | 525 | case 7: //1001 526 | setOutputPins(0b1001); 527 | break; 528 | } 529 | } 530 | 531 | // Prevents power consumption on the outputs 532 | void AccelStepper::disableOutputs() 533 | { 534 | if (! _interface) return; 535 | 536 | setOutputPins(0); // Handles inversion automatically 537 | if (_enablePin != 0xff) 538 | digitalWrite(_enablePin, LOW ^ _enableInverted); 539 | } 540 | 541 | void AccelStepper::enableOutputs() 542 | { 543 | if (! _interface) 544 | return; 545 | 546 | pinMode(_pin[0], OUTPUT); 547 | pinMode(_pin[1], OUTPUT); 548 | if (_interface == FULL4WIRE || _interface == HALF4WIRE) 549 | { 550 | pinMode(_pin[2], OUTPUT); 551 | pinMode(_pin[3], OUTPUT); 552 | } 553 | else if (_interface == FULL3WIRE || _interface == HALF3WIRE) 554 | { 555 | pinMode(_pin[2], OUTPUT); 556 | } 557 | 558 | if (_enablePin != 0xff) 559 | { 560 | pinMode(_enablePin, OUTPUT); 561 | digitalWrite(_enablePin, HIGH ^ _enableInverted); 562 | } 563 | } 564 | 565 | void AccelStepper::setMinPulseWidth(unsigned int minWidth) 566 | { 567 | _minPulseWidth = minWidth; 568 | } 569 | 570 | void AccelStepper::setEnablePin(uint8_t enablePin) 571 | { 572 | _enablePin = enablePin; 573 | 574 | // This happens after construction, so init pin now. 575 | if (_enablePin != 0xff) 576 | { 577 | pinMode(_enablePin, OUTPUT); 578 | digitalWrite(_enablePin, HIGH ^ _enableInverted); 579 | } 580 | } 581 | 582 | void AccelStepper::setPinsInverted(bool directionInvert, bool stepInvert, bool enableInvert) 583 | { 584 | _pinInverted[0] = stepInvert; 585 | _pinInverted[1] = directionInvert; 586 | _enableInverted = enableInvert; 587 | } 588 | 589 | void AccelStepper::setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert) 590 | { 591 | _pinInverted[0] = pin1Invert; 592 | _pinInverted[1] = pin2Invert; 593 | _pinInverted[2] = pin3Invert; 594 | _pinInverted[3] = pin4Invert; 595 | _enableInverted = enableInvert; 596 | } 597 | 598 | // Blocks until the target position is reached and stopped 599 | void AccelStepper::runToPosition() 600 | { 601 | while (run()) 602 | ; 603 | } 604 | 605 | boolean AccelStepper::runSpeedToPosition() 606 | { 607 | if (_targetPos == _currentPos) 608 | return false; 609 | if (_targetPos >_currentPos) 610 | _direction = DIRECTION_CW; 611 | else 612 | _direction = DIRECTION_CCW; 613 | return runSpeed(); 614 | } 615 | 616 | // Blocks until the new target position is reached 617 | void AccelStepper::runToNewPosition(long position) 618 | { 619 | moveTo(position); 620 | runToPosition(); 621 | } 622 | 623 | void AccelStepper::stop() 624 | { 625 | if (_speed != 0.0) 626 | { 627 | long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)) + 1; // Equation 16 (+integer rounding) 628 | if (_speed > 0) 629 | move(stepsToStop); 630 | else 631 | move(-stepsToStop); 632 | } 633 | } 634 | -------------------------------------------------------------------------------- /AccelStepper.h: -------------------------------------------------------------------------------- 1 | // AccelStepper.h 2 | // 3 | /// \mainpage AccelStepper library for Arduino 4 | /// 5 | /// This is the Arduino AccelStepper library. 6 | /// It provides an object-oriented interface for 2, 3 or 4 pin stepper motors. 7 | /// 8 | /// The standard Arduino IDE includes the Stepper library 9 | /// (http://arduino.cc/en/Reference/Stepper) for stepper motors. It is 10 | /// perfectly adequate for simple, single motor applications. 11 | /// 12 | /// AccelStepper significantly improves on the standard Arduino Stepper library in several ways: 13 | /// \li Supports acceleration and deceleration 14 | /// \li Supports multiple simultaneous steppers, with independent concurrent stepping on each stepper 15 | /// \li API functions never delay() or block 16 | /// \li Supports 2, 3 and 4 wire steppers, plus 3 and 4 wire half steppers. 17 | /// \li Supports alternate stepping functions to enable support of AFMotor (https://github.com/adafruit/Adafruit-Motor-Shield-library) 18 | /// \li Supports stepper drivers such as the Sparkfun EasyDriver (based on 3967 driver chip) 19 | /// \li Very slow speeds are supported 20 | /// \li Extensive API 21 | /// \li Subclass support 22 | /// 23 | /// The latest version of this documentation can be downloaded from 24 | /// http://www.airspayce.com/mikem/arduino/AccelStepper 25 | /// The version of the package that this documentation refers to can be downloaded 26 | /// from http://www.airspayce.com/mikem/arduino/AccelStepper/AccelStepper-1.47.zip 27 | /// 28 | /// Example Arduino programs are included to show the main modes of use. 29 | /// 30 | /// You can also find online help and discussion at http://groups.google.com/group/accelstepper 31 | /// Please use that group for all questions and discussions on this topic. 32 | /// Do not contact the author directly, unless it is to discuss commercial licensing. 33 | /// Before asking a question or reporting a bug, please read http://www.catb.org/esr/faqs/smart-questions.html 34 | /// 35 | /// Tested on Arduino Diecimila and Mega with arduino-0018 & arduino-0021 36 | /// on OpenSuSE 11.1 and avr-libc-1.6.1-1.15, 37 | /// cross-avr-binutils-2.19-9.1, cross-avr-gcc-4.1.3_20080612-26.5. 38 | /// Tested on Teensy http://www.pjrc.com/teensy including Teensy 3.1 built using Arduino IDE 1.0.5 with 39 | /// teensyduino addon 1.18 and later. 40 | /// 41 | /// \par Installation 42 | /// 43 | /// Install in the usual way: unzip the distribution zip file to the libraries 44 | /// sub-folder of your sketchbook. 45 | /// 46 | /// \par Theory 47 | /// 48 | /// This code uses speed calculations as described in 49 | /// "Generate stepper-motor speed profiles in real time" by David Austin 50 | /// http://fab.cba.mit.edu/classes/MIT/961.09/projects/i0/Stepper_Motor_Speed_Profile.pdf 51 | /// with the exception that AccelStepper uses steps per second rather than radians per second 52 | /// (because we dont know the step angle of the motor) 53 | /// An initial step interval is calculated for the first step, based on the desired acceleration 54 | /// On subsequent steps, shorter step intervals are calculated based 55 | /// on the previous step until max speed is achieved. 56 | /// 57 | /// \par Donations 58 | /// 59 | /// This library is offered under a free GPL license for those who want to use it that way. 60 | /// We try hard to keep it up to date, fix bugs 61 | /// and to provide free support. If this library has helped you save time or money, please consider donating at 62 | /// http://www.airspayce.com or here: 63 | /// 64 | /// \htmlonly
\endhtmlonly 65 | /// 66 | /// \par Trademarks 67 | /// 68 | /// AccelStepper is a trademark of AirSpayce Pty Ltd. The AccelStepper mark was first used on April 26 2010 for 69 | /// international trade, and is used only in relation to motor control hardware and software. 70 | /// It is not to be confused with any other similar marks covering other goods and services. 71 | /// 72 | /// \par Copyright 73 | /// 74 | /// This software is Copyright (C) 2010 Mike McCauley. Use is subject to license 75 | /// conditions. The main licensing options available are GPL V2 or Commercial: 76 | /// 77 | /// \par Open Source Licensing GPL V2 78 | /// This is the appropriate option if you want to share the source code of your 79 | /// application with everyone you distribute it to, and you also want to give them 80 | /// the right to share who uses it. If you wish to use this software under Open 81 | /// Source Licensing, you must contribute all your source code to the open source 82 | /// community in accordance with the GPL Version 2 when your application is 83 | /// distributed. See http://www.gnu.org/copyleft/gpl.html 84 | /// 85 | /// \par Commercial Licensing 86 | /// This is the appropriate option if you are creating proprietary applications 87 | /// and you are not prepared to distribute and share the source code of your 88 | /// application. Contact info@airspayce.com for details. 89 | /// 90 | /// \par Revision History 91 | /// \version 1.0 Initial release 92 | /// 93 | /// \version 1.1 Added speed() function to get the current speed. 94 | /// \version 1.2 Added runSpeedToPosition() submitted by Gunnar Arndt. 95 | /// \version 1.3 Added support for stepper drivers (ie with Step and Direction inputs) with _pins == 1 96 | /// \version 1.4 Added functional contructor to support AFMotor, contributed by Limor, with example sketches. 97 | /// \version 1.5 Improvements contributed by Peter Mousley: Use of microsecond steps and other speed improvements 98 | /// to increase max stepping speed to about 4kHz. New option for user to set the min allowed pulse width. 99 | /// Added checks for already running at max speed and skip further calcs if so. 100 | /// \version 1.6 Fixed a problem with wrapping of microsecond stepping that could cause stepping to hang. 101 | /// Reported by Sandy Noble. 102 | /// Removed redundant _lastRunTime member. 103 | /// \version 1.7 Fixed a bug where setCurrentPosition() did not always work as expected. 104 | /// Reported by Peter Linhart. 105 | /// \version 1.8 Added support for 4 pin half-steppers, requested by Harvey Moon 106 | /// \version 1.9 setCurrentPosition() now also sets motor speed to 0. 107 | /// \version 1.10 Builds on Arduino 1.0 108 | /// \version 1.11 Improvments from Michael Ellison: 109 | /// Added optional enable line support for stepper drivers 110 | /// Added inversion for step/direction/enable lines for stepper drivers 111 | /// \version 1.12 Announce Google Group 112 | /// \version 1.13 Improvements to speed calculation. Cost of calculation is now less in the worst case, 113 | /// and more or less constant in all cases. This should result in slightly beter high speed performance, and 114 | /// reduce anomalous speed glitches when other steppers are accelerating. 115 | /// However, its hard to see how to replace the sqrt() required at the very first step from 0 speed. 116 | /// \version 1.14 Fixed a problem with compiling under arduino 0021 reported by EmbeddedMan 117 | /// \version 1.15 Fixed a problem with runSpeedToPosition which did not correctly handle 118 | /// running backwards to a smaller target position. Added examples 119 | /// \version 1.16 Fixed some cases in the code where abs() was used instead of fabs(). 120 | /// \version 1.17 Added example ProportionalControl 121 | /// \version 1.18 Fixed a problem: If one calls the funcion runSpeed() when Speed is zero, it makes steps 122 | /// without counting. reported by Friedrich, Klappenbach. 123 | /// \version 1.19 Added MotorInterfaceType and symbolic names for the number of pins to use 124 | /// for the motor interface. Updated examples to suit. 125 | /// Replaced individual pin assignment variables _pin1, _pin2 etc with array _pin[4]. 126 | /// _pins member changed to _interface. 127 | /// Added _pinInverted array to simplify pin inversion operations. 128 | /// Added new function setOutputPins() which sets the motor output pins. 129 | /// It can be overridden in order to provide, say, serial output instead of parallel output 130 | /// Some refactoring and code size reduction. 131 | /// \version 1.20 Improved documentation and examples to show need for correctly 132 | /// specifying AccelStepper::FULL4WIRE and friends. 133 | /// \version 1.21 Fixed a problem where desiredSpeed could compute the wrong step acceleration 134 | /// when _speed was small but non-zero. Reported by Brian Schmalz. 135 | /// Precompute sqrt_twoa to improve performance and max possible stepping speed 136 | /// \version 1.22 Added Bounce.pde example 137 | /// Fixed a problem where calling moveTo(), setMaxSpeed(), setAcceleration() more 138 | /// frequently than the step time, even 139 | /// with the same values, would interfere with speed calcs. Now a new speed is computed 140 | /// only if there was a change in the set value. Reported by Brian Schmalz. 141 | /// \version 1.23 Rewrite of the speed algorithms in line with 142 | /// http://fab.cba.mit.edu/classes/MIT/961.09/projects/i0/Stepper_Motor_Speed_Profile.pdf 143 | /// Now expect smoother and more linear accelerations and decelerations. The desiredSpeed() 144 | /// function was removed. 145 | /// \version 1.24 Fixed a problem introduced in 1.23: with runToPosition, which did never returned 146 | /// \version 1.25 Now ignore attempts to set acceleration to 0.0 147 | /// \version 1.26 Fixed a problem where certina combinations of speed and accelration could cause 148 | /// oscillation about the target position. 149 | /// \version 1.27 Added stop() function to stop as fast as possible with current acceleration parameters. 150 | /// Also added new Quickstop example showing its use. 151 | /// \version 1.28 Fixed another problem where certain combinations of speed and accelration could cause 152 | /// oscillation about the target position. 153 | /// Added support for 3 wire full and half steppers such as Hard Disk Drive spindle. 154 | /// Contributed by Yuri Ivatchkovitch. 155 | /// \version 1.29 Fixed a problem that could cause a DRIVER stepper to continually step 156 | /// with some sketches. Reported by Vadim. 157 | /// \version 1.30 Fixed a problem that could cause stepper to back up a few steps at the end of 158 | /// accelerated travel with certain speeds. Reported and patched by jolo. 159 | /// \version 1.31 Updated author and distribution location details to airspayce.com 160 | /// \version 1.32 Fixed a problem with enableOutputs() and setEnablePin on Arduino Due that 161 | /// prevented the enable pin changing stae correctly. Reported by Duane Bishop. 162 | /// \version 1.33 Fixed an error in example AFMotor_ConstantSpeed.pde did not setMaxSpeed(); 163 | /// Fixed a problem that caused incorrect pin sequencing of FULL3WIRE and HALF3WIRE. 164 | /// Unfortunately this meant changing the signature for all step*() functions. 165 | /// Added example MotorShield, showing how to use AdaFruit Motor Shield to control 166 | /// a 3 phase motor such as a HDD spindle motor (and without using the AFMotor library. 167 | /// \version 1.34 Added setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert) 168 | /// to allow inversion of 2, 3 and 4 wire stepper pins. Requested by Oleg. 169 | /// \version 1.35 Removed default args from setPinsInverted(bool, bool, bool, bool, bool) to prevent ambiguity with 170 | /// setPinsInverted(bool, bool, bool). Reported by Mac Mac. 171 | /// \version 1.36 Changed enableOutputs() and disableOutputs() to be virtual so can be overridden. 172 | /// Added new optional argument 'enable' to constructor, which allows you toi disable the 173 | /// automatic enabling of outputs at construction time. Suggested by Guido. 174 | /// \version 1.37 Fixed a problem with step1 that could cause a rogue step in the 175 | /// wrong direction (or not, 176 | /// depending on the setup-time requirements of the connected hardware). 177 | /// Reported by Mark Tillotson. 178 | /// \version 1.38 run() function incorrectly always returned true. Updated function and doc so it returns true 179 | /// if the motor is still running to the target position. 180 | /// \version 1.39 Updated typos in keywords.txt, courtesey Jon Magill. 181 | /// \version 1.40 Updated documentation, including testing on Teensy 3.1 182 | /// \version 1.41 Fixed an error in the acceleration calculations, resulting in acceleration of haldf the intended value 183 | /// \version 1.42 Improved support for FULL3WIRE and HALF3WIRE output pins. These changes were in Yuri's original 184 | /// contribution but did not make it into production.
185 | /// \version 1.43 Added DualMotorShield example. Shows how to use AccelStepper to control 2 x 2 phase steppers using the 186 | /// Itead Studio Arduino Dual Stepper Motor Driver Shield model IM120417015.
187 | /// \version 1.44 examples/DualMotorShield/DualMotorShield.ino examples/DualMotorShield/DualMotorShield.pde 188 | /// was missing from the distribution.
189 | /// \version 1.45 Fixed a problem where if setAcceleration was not called, there was no default 190 | /// acceleration. Reported by Michael Newman.
191 | /// \version 1.45 Fixed inaccuracy in acceleration rate by using Equation 15, suggested by Sebastian Gracki.
192 | /// Performance improvements in runSpeed suggested by Jaakko Fagerlund.
193 | /// \version 1.46 Fixed error in documentation for runToPosition(). 194 | /// Reinstated time calculations in runSpeed() since new version is reported 195 | /// not to work correctly under some circumstances. Reported by Oleg V Gavva.
196 | 197 | /// 198 | /// \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS 199 | // Copyright (C) 2009-2013 Mike McCauley 200 | // $Id: AccelStepper.h,v 1.21 2014/10/31 06:05:30 mikem Exp mikem $ 201 | 202 | #ifndef AccelStepper_h 203 | #define AccelStepper_h 204 | 205 | #include 206 | #if ARDUINO >= 100 207 | #include 208 | #else 209 | #include 210 | #include 211 | #endif 212 | 213 | // These defs cause trouble on some versions of Arduino 214 | #undef round 215 | 216 | ///////////////////////////////////////////////////////////////////// 217 | /// \class AccelStepper AccelStepper.h 218 | /// \brief Support for stepper motors with acceleration etc. 219 | /// 220 | /// This defines a single 2 or 4 pin stepper motor, or stepper moter with fdriver chip, with optional 221 | /// acceleration, deceleration, absolute positioning commands etc. Multiple 222 | /// simultaneous steppers are supported, all moving 223 | /// at different speeds and accelerations. 224 | /// 225 | /// \par Operation 226 | /// This module operates by computing a step time in microseconds. The step 227 | /// time is recomputed after each step and after speed and acceleration 228 | /// parameters are changed by the caller. The time of each step is recorded in 229 | /// microseconds. The run() function steps the motor once if a new step is due. 230 | /// The run() function must be called frequently until the motor is in the 231 | /// desired position, after which time run() will do nothing. 232 | /// 233 | /// \par Positioning 234 | /// Positions are specified by a signed long integer. At 235 | /// construction time, the current position of the motor is consider to be 0. Positive 236 | /// positions are clockwise from the initial position; negative positions are 237 | /// anticlockwise. The current position can be altered for instance after 238 | /// initialization positioning. 239 | /// 240 | /// \par Caveats 241 | /// This is an open loop controller: If the motor stalls or is oversped, 242 | /// AccelStepper will not have a correct 243 | /// idea of where the motor really is (since there is no feedback of the motor's 244 | /// real position. We only know where we _think_ it is, relative to the 245 | /// initial starting point). 246 | /// 247 | /// \par Performance 248 | /// The fastest motor speed that can be reliably supported is about 4000 steps per 249 | /// second at a clock frequency of 16 MHz on Arduino such as Uno etc. 250 | /// Faster processors can support faster stepping speeds. 251 | /// However, any speed less than that 252 | /// down to very slow speeds (much less than one per second) are also supported, 253 | /// provided the run() function is called frequently enough to step the motor 254 | /// whenever required for the speed set. 255 | /// Calling setAcceleration() is expensive, 256 | /// since it requires a square root to be calculated. 257 | class AccelStepper 258 | { 259 | public: 260 | /// \brief Symbolic names for number of pins. 261 | /// Use this in the pins argument the AccelStepper constructor to 262 | /// provide a symbolic name for the number of pins 263 | /// to use. 264 | typedef enum 265 | { 266 | FUNCTION = 0, ///< Use the functional interface, implementing your own driver functions (internal use only) 267 | DRIVER = 1, ///< Stepper Driver, 2 driver pins required 268 | FULL2WIRE = 2, ///< 2 wire stepper, 2 motor pins required 269 | FULL3WIRE = 3, ///< 3 wire stepper, such as HDD spindle, 3 motor pins required 270 | FULL4WIRE = 4, ///< 4 wire full stepper, 4 motor pins required 271 | HALF3WIRE = 6, ///< 3 wire half stepper, such as HDD spindle, 3 motor pins required 272 | HALF4WIRE = 8 ///< 4 wire half stepper, 4 motor pins required 273 | } MotorInterfaceType; 274 | 275 | /// Constructor. You can have multiple simultaneous steppers, all moving 276 | /// at different speeds and accelerations, provided you call their run() 277 | /// functions at frequent enough intervals. Current Position is set to 0, target 278 | /// position is set to 0. MaxSpeed and Acceleration default to 1.0. 279 | /// The motor pins will be initialised to OUTPUT mode during the 280 | /// constructor by a call to enableOutputs(). 281 | /// \param[in] interface Number of pins to interface to. 1, 2, 4 or 8 are 282 | /// supported, but it is preferred to use the \ref MotorInterfaceType symbolic names. 283 | /// AccelStepper::DRIVER (1) means a stepper driver (with Step and Direction pins). 284 | /// If an enable line is also needed, call setEnablePin() after construction. 285 | /// You may also invert the pins using setPinsInverted(). 286 | /// AccelStepper::FULL2WIRE (2) means a 2 wire stepper (2 pins required). 287 | /// AccelStepper::FULL3WIRE (3) means a 3 wire stepper, such as HDD spindle (3 pins required). 288 | /// AccelStepper::FULL4WIRE (4) means a 4 wire stepper (4 pins required). 289 | /// AccelStepper::HALF3WIRE (6) means a 3 wire half stepper, such as HDD spindle (3 pins required) 290 | /// AccelStepper::HALF4WIRE (8) means a 4 wire half stepper (4 pins required) 291 | /// Defaults to AccelStepper::FULL4WIRE (4) pins. 292 | /// \param[in] pin1 Arduino digital pin number for motor pin 1. Defaults 293 | /// to pin 2. For a AccelStepper::DRIVER (interface==1), 294 | /// this is the Step input to the driver. Low to high transition means to step) 295 | /// \param[in] pin2 Arduino digital pin number for motor pin 2. Defaults 296 | /// to pin 3. For a AccelStepper::DRIVER (interface==1), 297 | /// this is the Direction input the driver. High means forward. 298 | /// \param[in] pin3 Arduino digital pin number for motor pin 3. Defaults 299 | /// to pin 4. 300 | /// \param[in] pin4 Arduino digital pin number for motor pin 4. Defaults 301 | /// to pin 5. 302 | /// \param[in] enable If this is true (the default), enableOutputs() will be called to enable 303 | /// the output pins at construction time. 304 | AccelStepper(uint8_t interface = AccelStepper::FULL4WIRE, uint8_t pin1 = 2, uint8_t pin2 = 3, uint8_t pin3 = 4, uint8_t pin4 = 5, bool enable = true); 305 | 306 | /// Alternate Constructor which will call your own functions for forward and backward steps. 307 | /// You can have multiple simultaneous steppers, all moving 308 | /// at different speeds and accelerations, provided you call their run() 309 | /// functions at frequent enough intervals. Current Position is set to 0, target 310 | /// position is set to 0. MaxSpeed and Acceleration default to 1.0. 311 | /// Any motor initialization should happen before hand, no pins are used or initialized. 312 | /// \param[in] forward void-returning procedure that will make a forward step 313 | /// \param[in] backward void-returning procedure that will make a backward step 314 | AccelStepper(void (*forward)(), void (*backward)()); 315 | 316 | /// Set the target position. The run() function will try to move the motor (at most one step per call) 317 | /// from the current position to the target position set by the most 318 | /// recent call to this function. Caution: moveTo() also recalculates the speed for the next step. 319 | /// If you are trying to use constant speed movements, you should call setSpeed() after calling moveTo(). 320 | /// \param[in] absolute The desired absolute position. Negative is 321 | /// anticlockwise from the 0 position. 322 | void moveTo(long absolute); 323 | 324 | /// Set the target position relative to the current position 325 | /// \param[in] relative The desired position relative to the current position. Negative is 326 | /// anticlockwise from the current position. 327 | void move(long relative); 328 | 329 | /// Poll the motor and step it if a step is due, implementing 330 | /// accelerations and decelerations to acheive the target position. You must call this as 331 | /// frequently as possible, but at least once per minimum step time interval, 332 | /// preferably in your main loop. Note that each call to run() will make at most one step, and then only when a step is due, 333 | /// based on the current speed and the time since the last step. 334 | /// \return true if the motor is still running to the target position. 335 | boolean run(); 336 | 337 | /// Poll the motor and step it if a step is due, implementing a constant 338 | /// speed as set by the most recent call to setSpeed(). You must call this as 339 | /// frequently as possible, but at least once per step interval, 340 | /// \return true if the motor was stepped. 341 | boolean runSpeed(); 342 | 343 | /// Sets the maximum permitted speed. The run() function will accelerate 344 | /// up to the speed set by this function. 345 | /// Caution: the maximum speed achievable depends on your processor and clock speed. 346 | /// \param[in] speed The desired maximum speed in steps per second. Must 347 | /// be > 0. Caution: Speeds that exceed the maximum speed supported by the processor may 348 | /// Result in non-linear accelerations and decelerations. 349 | void setMaxSpeed(float speed); 350 | 351 | /// Sets the acceleration/deceleration rate. 352 | /// \param[in] acceleration The desired acceleration in steps per second 353 | /// per second. Must be > 0.0. This is an expensive call since it requires a square 354 | /// root to be calculated. Dont call more ofthen than needed 355 | void setAcceleration(float acceleration); 356 | 357 | /// Sets the desired constant speed for use with runSpeed(). 358 | /// \param[in] speed The desired constant speed in steps per 359 | /// second. Positive is clockwise. Speeds of more than 1000 steps per 360 | /// second are unreliable. Very slow speeds may be set (eg 0.00027777 for 361 | /// once per hour, approximately. Speed accuracy depends on the Arduino 362 | /// crystal. Jitter depends on how frequently you call the runSpeed() function. 363 | void setSpeed(float speed); 364 | 365 | /// The most recently set speed 366 | /// \return the most recent speed in steps per second 367 | float speed(); 368 | 369 | /// The distance from the current position to the target position. 370 | /// \return the distance from the current position to the target position 371 | /// in steps. Positive is clockwise from the current position. 372 | long distanceToGo(); 373 | 374 | /// The most recently set target position. 375 | /// \return the target position 376 | /// in steps. Positive is clockwise from the 0 position. 377 | long targetPosition(); 378 | 379 | /// The currently motor position. 380 | /// \return the current motor position 381 | /// in steps. Positive is clockwise from the 0 position. 382 | long currentPosition(); 383 | 384 | /// Resets the current position of the motor, so that wherever the motor 385 | /// happens to be right now is considered to be the new 0 position. Useful 386 | /// for setting a zero position on a stepper after an initial hardware 387 | /// positioning move. 388 | /// Has the side effect of setting the current motor speed to 0. 389 | /// \param[in] position The position in steps of wherever the motor 390 | /// happens to be right now. 391 | void setCurrentPosition(long position); 392 | 393 | /// Moves the motor (with acceleration/deceleration) 394 | /// to the target position and blocks until it is at 395 | /// position. Dont use this in event loops, since it blocks. 396 | void runToPosition(); 397 | 398 | /// Runs at the currently selected speed until the target position is reached 399 | /// Does not implement accelerations. 400 | /// \return true if it stepped 401 | boolean runSpeedToPosition(); 402 | 403 | /// Moves the motor (with acceleration/deceleration) 404 | /// to the new target position and blocks until it is at 405 | /// position. Dont use this in event loops, since it blocks. 406 | /// \param[in] position The new target position. 407 | void runToNewPosition(long position); 408 | 409 | /// Sets a new target position that causes the stepper 410 | /// to stop as quickly as possible, using the current speed and acceleration parameters. 411 | void stop(); 412 | 413 | /// Disable motor pin outputs by setting them all LOW 414 | /// Depending on the design of your electronics this may turn off 415 | /// the power to the motor coils, saving power. 416 | /// This is useful to support Arduino low power modes: disable the outputs 417 | /// during sleep and then reenable with enableOutputs() before stepping 418 | /// again. 419 | virtual void disableOutputs(); 420 | 421 | /// Enable motor pin outputs by setting the motor pins to OUTPUT 422 | /// mode. Called automatically by the constructor. 423 | virtual void enableOutputs(); 424 | 425 | /// Sets the minimum pulse width allowed by the stepper driver. The minimum practical pulse width is 426 | /// approximately 20 microseconds. Times less than 20 microseconds 427 | /// will usually result in 20 microseconds or so. 428 | /// \param[in] minWidth The minimum pulse width in microseconds. 429 | void setMinPulseWidth(unsigned int minWidth); 430 | 431 | /// Sets the enable pin number for stepper drivers. 432 | /// 0xFF indicates unused (default). 433 | /// Otherwise, if a pin is set, the pin will be turned on when 434 | /// enableOutputs() is called and switched off when disableOutputs() 435 | /// is called. 436 | /// \param[in] enablePin Arduino digital pin number for motor enable 437 | /// \sa setPinsInverted 438 | void setEnablePin(uint8_t enablePin = 0xff); 439 | 440 | /// Sets the inversion for stepper driver pins 441 | /// \param[in] directionInvert True for inverted direction pin, false for non-inverted 442 | /// \param[in] stepInvert True for inverted step pin, false for non-inverted 443 | /// \param[in] enableInvert True for inverted enable pin, false (default) for non-inverted 444 | void setPinsInverted(bool directionInvert = false, bool stepInvert = false, bool enableInvert = false); 445 | 446 | /// Sets the inversion for 2, 3 and 4 wire stepper pins 447 | /// \param[in] pin1Invert True for inverted pin1, false for non-inverted 448 | /// \param[in] pin2Invert True for inverted pin2, false for non-inverted 449 | /// \param[in] pin3Invert True for inverted pin3, false for non-inverted 450 | /// \param[in] pin4Invert True for inverted pin4, false for non-inverted 451 | /// \param[in] enableInvert True for inverted enable pin, false (default) for non-inverted 452 | void setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert); 453 | 454 | protected: 455 | 456 | /// \brief Direction indicator 457 | /// Symbolic names for the direction the motor is turning 458 | typedef enum 459 | { 460 | DIRECTION_CCW = 0, ///< Clockwise 461 | DIRECTION_CW = 1 ///< Counter-Clockwise 462 | } Direction; 463 | 464 | /// Forces the library to compute a new instantaneous speed and set that as 465 | /// the current speed. It is called by 466 | /// the library: 467 | /// \li after each step 468 | /// \li after change to maxSpeed through setMaxSpeed() 469 | /// \li after change to acceleration through setAcceleration() 470 | /// \li after change to target position (relative or absolute) through 471 | /// move() or moveTo() 472 | void computeNewSpeed(); 473 | 474 | /// Low level function to set the motor output pins 475 | /// bit 0 of the mask corresponds to _pin[0] 476 | /// bit 1 of the mask corresponds to _pin[1] 477 | /// You can override this to impment, for example serial chip output insted of using the 478 | /// output pins directly 479 | virtual void setOutputPins(uint8_t mask); 480 | 481 | /// Called to execute a step. Only called when a new step is 482 | /// required. Subclasses may override to implement new stepping 483 | /// interfaces. The default calls step1(), step2(), step4() or step8() depending on the 484 | /// number of pins defined for the stepper. 485 | /// \param[in] step The current step phase number (0 to 7) 486 | virtual void step(long step); 487 | 488 | /// Called to execute a step using stepper functions (pins = 0) Only called when a new step is 489 | /// required. Calls _forward() or _backward() to perform the step 490 | /// \param[in] step The current step phase number (0 to 7) 491 | virtual void step0(long step); 492 | 493 | /// Called to execute a step on a stepper driver (ie where pins == 1). Only called when a new step is 494 | /// required. Subclasses may override to implement new stepping 495 | /// interfaces. The default sets or clears the outputs of Step pin1 to step, 496 | /// and sets the output of _pin2 to the desired direction. The Step pin (_pin1) is pulsed for 1 microsecond 497 | /// which is the minimum STEP pulse width for the 3967 driver. 498 | /// \param[in] step The current step phase number (0 to 7) 499 | virtual void step1(long step); 500 | 501 | /// Called to execute a step on a 2 pin motor. Only called when a new step is 502 | /// required. Subclasses may override to implement new stepping 503 | /// interfaces. The default sets or clears the outputs of pin1 and pin2 504 | /// \param[in] step The current step phase number (0 to 7) 505 | virtual void step2(long step); 506 | 507 | /// Called to execute a step on a 3 pin motor, such as HDD spindle. Only called when a new step is 508 | /// required. Subclasses may override to implement new stepping 509 | /// interfaces. The default sets or clears the outputs of pin1, pin2, 510 | /// pin3 511 | /// \param[in] step The current step phase number (0 to 7) 512 | virtual void step3(long step); 513 | 514 | /// Called to execute a step on a 4 pin motor. Only called when a new step is 515 | /// required. Subclasses may override to implement new stepping 516 | /// interfaces. The default sets or clears the outputs of pin1, pin2, 517 | /// pin3, pin4. 518 | /// \param[in] step The current step phase number (0 to 7) 519 | virtual void step4(long step); 520 | 521 | /// Called to execute a step on a 3 pin motor, such as HDD spindle. Only called when a new step is 522 | /// required. Subclasses may override to implement new stepping 523 | /// interfaces. The default sets or clears the outputs of pin1, pin2, 524 | /// pin3 525 | /// \param[in] step The current step phase number (0 to 7) 526 | virtual void step6(long step); 527 | 528 | /// Called to execute a step on a 4 pin half-steper motor. Only called when a new step is 529 | /// required. Subclasses may override to implement new stepping 530 | /// interfaces. The default sets or clears the outputs of pin1, pin2, 531 | /// pin3, pin4. 532 | /// \param[in] step The current step phase number (0 to 7) 533 | virtual void step8(long step); 534 | 535 | private: 536 | /// Number of pins on the stepper motor. Permits 2 or 4. 2 pins is a 537 | /// bipolar, and 4 pins is a unipolar. 538 | uint8_t _interface; // 0, 1, 2, 4, 8, See MotorInterfaceType 539 | 540 | /// Arduino pin number assignments for the 2 or 4 pins required to interface to the 541 | /// stepper motor or driver 542 | uint8_t _pin[4]; 543 | 544 | /// Whether the _pins is inverted or not 545 | uint8_t _pinInverted[4]; 546 | 547 | /// The current absolution position in steps. 548 | long _currentPos; // Steps 549 | 550 | /// The target position in steps. The AccelStepper library will move the 551 | /// motor from the _currentPos to the _targetPos, taking into account the 552 | /// max speed, acceleration and deceleration 553 | long _targetPos; // Steps 554 | 555 | /// The current motos speed in steps per second 556 | /// Positive is clockwise 557 | float _speed; // Steps per second 558 | 559 | /// The maximum permitted speed in steps per second. Must be > 0. 560 | float _maxSpeed; 561 | 562 | /// The acceleration to use to accelerate or decelerate the motor in steps 563 | /// per second per second. Must be > 0 564 | float _acceleration; 565 | float _sqrt_twoa; // Precomputed sqrt(2*_acceleration) 566 | 567 | /// The current interval between steps in microseconds. 568 | /// 0 means the motor is currently stopped with _speed == 0 569 | unsigned long _stepInterval; 570 | 571 | /// The last step time in microseconds 572 | unsigned long _lastStepTime; 573 | 574 | /// The minimum allowed pulse width in microseconds 575 | unsigned int _minPulseWidth; 576 | 577 | /// Is the direction pin inverted? 578 | ///bool _dirInverted; /// Moved to _pinInverted[1] 579 | 580 | /// Is the step pin inverted? 581 | ///bool _stepInverted; /// Moved to _pinInverted[0] 582 | 583 | /// Is the enable pin inverted? 584 | bool _enableInverted; 585 | 586 | /// Enable pin for stepper driver, or 0xFF if unused. 587 | uint8_t _enablePin; 588 | 589 | /// The pointer to a forward-step procedure 590 | void (*_forward)(); 591 | 592 | /// The pointer to a backward-step procedure 593 | void (*_backward)(); 594 | 595 | /// The step counter for speed calculations 596 | long _n; 597 | 598 | /// Initial step size in microseconds 599 | float _c0; 600 | 601 | /// Last step size in microseconds 602 | float _cn; 603 | 604 | /// Min step size in microseconds based on maxSpeed 605 | float _cmin; // at max speed 606 | 607 | /// Current direction motor is spinning in 608 | boolean _direction; // 1 == CW 609 | 610 | }; 611 | 612 | /// @example Random.pde 613 | /// Make a single stepper perform random changes in speed, position and acceleration 614 | 615 | /// @example Overshoot.pde 616 | /// Check overshoot handling 617 | /// which sets a new target position and then waits until the stepper has 618 | /// achieved it. This is used for testing the handling of overshoots 619 | 620 | /// @example MultiStepper.pde 621 | /// Shows how to multiple simultaneous steppers 622 | /// Runs one stepper forwards and backwards, accelerating and decelerating 623 | /// at the limits. Runs other steppers at the same time 624 | 625 | /// @example ConstantSpeed.pde 626 | /// Shows how to run AccelStepper in the simplest, 627 | /// fixed speed mode with no accelerations 628 | 629 | /// @example Blocking.pde 630 | /// Shows how to use the blocking call runToNewPosition 631 | /// Which sets a new target position and then waits until the stepper has 632 | /// achieved it. 633 | 634 | /// @example AFMotor_MultiStepper.pde 635 | /// Control both Stepper motors at the same time with different speeds 636 | /// and accelerations. 637 | 638 | /// @example AFMotor_ConstantSpeed.pde 639 | /// Shows how to run AccelStepper in the simplest, 640 | /// fixed speed mode with no accelerations 641 | 642 | /// @example ProportionalControl.pde 643 | /// Make a single stepper follow the analog value read from a pot or whatever 644 | /// The stepper will move at a constant speed to each newly set posiiton, 645 | /// depending on the value of the pot. 646 | 647 | /// @example Bounce.pde 648 | /// Make a single stepper bounce from one limit to another, observing 649 | /// accelrations at each end of travel 650 | 651 | /// @example Quickstop.pde 652 | /// Check stop handling. 653 | /// Calls stop() while the stepper is travelling at full speed, causing 654 | /// the stepper to stop as quickly as possible, within the constraints of the 655 | /// current acceleration. 656 | 657 | /// @example MotorShield.pde 658 | /// Shows how to use AccelStepper to control a 3-phase motor, such as a HDD spindle motor 659 | /// using the Adafruit Motor Shield http://www.ladyada.net/make/mshield/index.html. 660 | 661 | /// @example DualMotorShield.pde 662 | /// Shows how to use AccelStepper to control 2 x 2 phase steppers using the 663 | /// Itead Studio Arduino Dual Stepper Motor Driver Shield 664 | /// model IM120417015 665 | 666 | #endif 667 | --------------------------------------------------------------------------------