Compare commits
12 Commits
SlEggBot
...
SlEggBotE3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3059e5a493 | ||
|
|
40f1999b25 | ||
| a23ca75d92 | |||
| 556c7888de | |||
| c1bc52c5a3 | |||
| e4e3a22924 | |||
|
|
fc868377d2 | ||
|
|
076b5ba3e5 | ||
|
|
53e51dc50b | ||
|
|
42e9c4d97b | ||
|
|
098c577142 | ||
| 50430b050e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -215,3 +215,4 @@ pip-log.txt
|
|||||||
|
|
||||||
#Mr Developer
|
#Mr Developer
|
||||||
.mr.developer.cfg
|
.mr.developer.cfg
|
||||||
|
src/credentials.h
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -44,3 +44,15 @@ http://wiki.evilmadscientist.com/Installing_software
|
|||||||
# return serialPort
|
# return serialPort
|
||||||
- In my version lines 1355-1360
|
- In my version lines 1355-1360
|
||||||
|
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
Add credentials.h file with content like this:
|
||||||
|
|
||||||
|
const char *kWifiSsid = "MySSID";
|
||||||
|
const char *kWifiPassword = "MySecret";
|
||||||
|
|
||||||
|
To disable Wifi:
|
||||||
|
|
||||||
|
const char *kWifiSsid = 0;
|
||||||
|
const char *kWifiPassword = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -2,16 +2,41 @@
|
|||||||
#define EGGDUINO_H
|
#define EGGDUINO_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#ifdef ESP32
|
||||||
|
#include <ESP32Servo.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <WebServer.h>
|
||||||
|
#include <SPIFFS.h>
|
||||||
|
#else
|
||||||
#include <Servo.h>
|
#include <Servo.h>
|
||||||
#include <avr/eeprom.h>
|
#endif
|
||||||
|
|
||||||
#include "AccelStepper.h"
|
#include <FastAccelStepper.h>
|
||||||
#include "SerialCommand.h"
|
#include "SerialCommand.h"
|
||||||
#include "button.h"
|
#include "button.h"
|
||||||
|
|
||||||
// implemented Eggbot-Protocol-Version v13
|
// implemented Eggbot-Protocol-Version v13
|
||||||
#define initSting "EBBv13_and_above Protocol emulated by Eggduino-Firmware V1.6a"
|
#define initSting "EBBv13_and_above Protocol emulated by Eggduino-Firmware V1.6a"
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
|
|
||||||
|
// Rotational Stepper
|
||||||
|
#define dir1 16
|
||||||
|
#define enableRotMotor 12
|
||||||
|
#define step1 26
|
||||||
|
#define rotMicrostep 32
|
||||||
|
|
||||||
|
// Pen Stepper
|
||||||
|
#define step2 25
|
||||||
|
#define dir2 27
|
||||||
|
#define enablePenMotor 12
|
||||||
|
#define penMicrostep 32
|
||||||
|
|
||||||
|
#define servoPin 17
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
// Rotational Stepper
|
// Rotational Stepper
|
||||||
#define step1 2
|
#define step1 2
|
||||||
#define dir1 5
|
#define dir1 5
|
||||||
@@ -26,31 +51,39 @@
|
|||||||
|
|
||||||
#define servoPin 4
|
#define servoPin 4
|
||||||
|
|
||||||
#define penUpPosEEAddress ((uint16_t *)0)
|
|
||||||
#define penDownPosEEAddress ((uint16_t *)2)
|
|
||||||
|
|
||||||
extern AccelStepper rotMotor;
|
#endif
|
||||||
extern AccelStepper penMotor;
|
|
||||||
|
struct ConfigParameter {
|
||||||
|
const char *key;
|
||||||
|
int *value;
|
||||||
|
String description;
|
||||||
|
int defaultValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern FastAccelStepperEngine g_stepEngine;
|
||||||
|
extern FastAccelStepper *g_pStepperRotate;
|
||||||
|
extern FastAccelStepper *g_pStepperPen;
|
||||||
|
|
||||||
extern Servo penServo;
|
extern Servo penServo;
|
||||||
extern SerialCommand SCmd;
|
extern SerialCommand SCmd;
|
||||||
|
|
||||||
extern int penMin;
|
extern int g_iPenUpPos;
|
||||||
extern int penMax;
|
extern int g_iPenDownPos;
|
||||||
extern int penUpPos;
|
extern int g_iServoRateUp;
|
||||||
extern int penDownPos;
|
extern int g_iServoRateDown;
|
||||||
extern int servoRateUp;
|
extern long g_iRotStepError;
|
||||||
extern int servoRateDown;
|
extern long g_iPenStepError;
|
||||||
extern long rotStepError;
|
extern int g_iPenState;
|
||||||
extern long penStepError;
|
extern uint32_t g_uiNodeCount;
|
||||||
extern int penState;
|
extern unsigned int g_uiLayer;
|
||||||
extern uint32_t nodeCount;
|
extern boolean g_bPrgButtonState;
|
||||||
extern unsigned int layer;
|
extern float fROT_STEP_CORRECTION;
|
||||||
extern boolean prgButtonState;
|
extern float fPEN_STEP_CORRECTION;
|
||||||
extern uint8_t rotStepCorrection;
|
extern boolean g_bMotorsEnabled;
|
||||||
extern uint8_t penStepCorrection;
|
|
||||||
extern float rotSpeed;
|
extern ConfigParameter configParameters[];
|
||||||
extern float penSpeed;
|
extern const size_t configParameterCount;
|
||||||
extern boolean motorsEnabled;
|
|
||||||
|
|
||||||
void makeComInterface();
|
void makeComInterface();
|
||||||
void initHardware();
|
void initHardware();
|
||||||
@@ -68,4 +101,15 @@ void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB);
|
|||||||
void storePenUpPosInEE();
|
void storePenUpPosInEE();
|
||||||
void storePenDownPosInEE();
|
void storePenDownPosInEE();
|
||||||
|
|
||||||
|
bool initConfigStore();
|
||||||
|
bool loadConfigFromFile();
|
||||||
|
bool saveConfigToFile();
|
||||||
|
String buildConfigJson();
|
||||||
|
bool applyConfigJson(const String &payload, String &errorMessage);
|
||||||
|
void startWebInterface();
|
||||||
|
void handleWebInterface();
|
||||||
|
void Log(const String &message);
|
||||||
|
void Log(const char *message);
|
||||||
|
String buildLogsJson(uint32_t sinceSeq);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
BIN
inkscape/linux/AxiDraw_395_LinX86.zip
Normal file
BIN
inkscape/linux/AxiDraw_395_LinX86.zip
Normal file
Binary file not shown.
452
inkscape/linux/patch/ebb_serial.py
Normal file
452
inkscape/linux/patch/ebb_serial.py
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
'''
|
||||||
|
ebb_serial.py
|
||||||
|
Serial connection utilities for EiBotBoard
|
||||||
|
https://github.com/evil-mad/plotink
|
||||||
|
|
||||||
|
Intended to provide some common interfaces that can be used by
|
||||||
|
EggBot, WaterColorBot, AxiDraw, and similar machines.
|
||||||
|
|
||||||
|
See below for version information
|
||||||
|
|
||||||
|
Thanks to Shel Michaels for bug fixes and helpful suggestions.
|
||||||
|
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2022 Windell H. Oskay, Evil Mad Scientist Laboratories
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from packaging.version import parse
|
||||||
|
|
||||||
|
from .plot_utils_import import from_dependency_import
|
||||||
|
inkex = from_dependency_import('ink_extensions.inkex')
|
||||||
|
serial = from_dependency_import('serial')
|
||||||
|
from serial.tools.list_ports import comports \
|
||||||
|
#pylint: disable=wrong-import-position, wrong-import-order
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def version():
|
||||||
|
'''Version number for this document'''
|
||||||
|
return "0.19" # Dated 2022-10-05
|
||||||
|
|
||||||
|
|
||||||
|
def findPort():
|
||||||
|
'''
|
||||||
|
Find first available EiBotBoard by searching USB ports. Return serial port object.
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
com_ports_list = list(comports())
|
||||||
|
except TypeError:
|
||||||
|
return None
|
||||||
|
ebb_port = None
|
||||||
|
for port in com_ports_list:
|
||||||
|
if port[0].startswith("/dev/ttyUSB"):
|
||||||
|
ebb_port = port[0] # Success; EBB found by name match.
|
||||||
|
break # stop searching-- we are done.
|
||||||
|
for port in com_ports_list:
|
||||||
|
if port[1].startswith("EiBotBoard"):
|
||||||
|
ebb_port = port[0] # Success; EBB found by name match.
|
||||||
|
break # stop searching-- we are done.
|
||||||
|
if ebb_port is None:
|
||||||
|
for port in com_ports_list:
|
||||||
|
if port[2].startswith("USB VID:PID=04D8:FD92"):
|
||||||
|
ebb_port = port[0] # Success; EBB found by VID/PID match.
|
||||||
|
break # stop searching-- we are done.
|
||||||
|
return ebb_port
|
||||||
|
|
||||||
|
|
||||||
|
def find_named_ebb(port_name):
|
||||||
|
'''
|
||||||
|
Find a specific EiBotBoard identified by a string giving either:
|
||||||
|
The enumerated serial port, or
|
||||||
|
An EBB "Name tag"
|
||||||
|
Names should be 3-16 characters long. Comparisons are not case sensitive.
|
||||||
|
(Name tags may assigned with the ST command on firmware 2.5.5 and later.)
|
||||||
|
If found: Return serial port name (enumeration)
|
||||||
|
If not found, Return None
|
||||||
|
'''
|
||||||
|
if port_name is not None:
|
||||||
|
needle = 'SER=' + port_name # pyserial 3
|
||||||
|
needle2 = 'SNR=' + port_name # pyserial 2.7
|
||||||
|
needle3 = '(' + port_name + ')' # e.g., "(COM4)"
|
||||||
|
|
||||||
|
needle = needle.lower()
|
||||||
|
needle2 = needle2.lower()
|
||||||
|
needle3 = needle3.lower()
|
||||||
|
plower = port_name.lower()
|
||||||
|
|
||||||
|
try:
|
||||||
|
com_ports_list = list(comports())
|
||||||
|
except TypeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for port in com_ports_list:
|
||||||
|
p_0 = port[0].lower()
|
||||||
|
p_1 = port[1].lower()
|
||||||
|
p_2 = port[2].lower()
|
||||||
|
|
||||||
|
if needle in p_2:
|
||||||
|
return port[0] # Success; EBB found by name match.
|
||||||
|
if needle2 in p_2:
|
||||||
|
return port[0] # Success; EBB found by name match.
|
||||||
|
if needle3 in p_1:
|
||||||
|
return port[0] # Success; EBB found by port match.
|
||||||
|
|
||||||
|
p_1 = p_1[11:]
|
||||||
|
if p_1.startswith(plower):
|
||||||
|
return port[0] # Success; EBB found by name match.
|
||||||
|
if p_0.startswith(plower):
|
||||||
|
return port[0] # Success; EBB found by port match.
|
||||||
|
|
||||||
|
needle.replace(" ", "_") # SN on Windows has underscores, not spaces.
|
||||||
|
if needle in p_2:
|
||||||
|
return port[0] # Success; EBB found by port match.
|
||||||
|
|
||||||
|
needle2.replace(" ", "_") # SN on Windows has underscores, not spaces.
|
||||||
|
if needle2 in p_2:
|
||||||
|
return port[0] # Success; EBB found by port match.
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def query_nickname(port_name, verbose=True):
|
||||||
|
'''
|
||||||
|
Query the EBB nickname and report it.
|
||||||
|
If verbose is True or omitted, the result will be human readable.
|
||||||
|
A short version is returned if verbose is False.
|
||||||
|
Requires firmware version 2.5.5 or newer. http://evil-mad.github.io/EggBot/ebb.html#QT
|
||||||
|
'''
|
||||||
|
if port_name is not None:
|
||||||
|
version_status = min_version(port_name, "2.5.5")
|
||||||
|
|
||||||
|
if version_status:
|
||||||
|
raw_string = (query(port_name, 'QT\r'))
|
||||||
|
if raw_string.isspace():
|
||||||
|
if verbose:
|
||||||
|
return "This AxiDraw does not have a nickname assigned."
|
||||||
|
return None
|
||||||
|
if verbose:
|
||||||
|
return "AxiDraw nickname: " + raw_string
|
||||||
|
return str(raw_string).strip()
|
||||||
|
if version_status is False:
|
||||||
|
if verbose:
|
||||||
|
return "AxiDraw naming requires firmware version 2.5.5 or higher."
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def write_nickname(port_name, nickname):
|
||||||
|
'''
|
||||||
|
Write the EBB nickname.
|
||||||
|
Requires firmware version 2.5.5 or newer. http://evil-mad.github.io/EggBot/ebb.html#ST
|
||||||
|
'''
|
||||||
|
if port_name is not None:
|
||||||
|
version_status = min_version(port_name, "2.5.5")
|
||||||
|
|
||||||
|
if version_status:
|
||||||
|
try:
|
||||||
|
cmd = 'ST,' + nickname + '\r'
|
||||||
|
command(port_name,cmd)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def reboot(port_name):
|
||||||
|
'''
|
||||||
|
Reboot the EBB, as though it were just powered on.
|
||||||
|
Requires firmware version 2.5.5 or newer. http://evil-mad.github.io/EggBot/ebb.html#RB
|
||||||
|
'''
|
||||||
|
if port_name is not None:
|
||||||
|
version_status = min_version(port_name, "2.5.5")
|
||||||
|
if version_status:
|
||||||
|
try:
|
||||||
|
command(port_name,'RB\r')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def list_port_info():
|
||||||
|
'''Find and return a list of all USB devices and their information.'''
|
||||||
|
try:
|
||||||
|
com_ports_list = list(comports())
|
||||||
|
except TypeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
port_info_list = []
|
||||||
|
for port in com_ports_list:
|
||||||
|
port_info_list.append(port[0]) # port name
|
||||||
|
port_info_list.append(port[1]) # Identifier
|
||||||
|
port_info_list.append(port[2]) # VID/PID
|
||||||
|
if port_info_list:
|
||||||
|
return port_info_list
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def listEBBports():
|
||||||
|
'''Find and return a list of all EiBotBoard units connected via USB port.'''
|
||||||
|
|
||||||
|
try:
|
||||||
|
com_ports_list = list(comports())
|
||||||
|
except TypeError:
|
||||||
|
return None
|
||||||
|
ebb_ports_list = []
|
||||||
|
for port in com_ports_list:
|
||||||
|
port_has_ebb = False
|
||||||
|
if port[0].startswith("/dev/ttyUSB"):
|
||||||
|
port_has_ebb = True
|
||||||
|
elif port[1].startswith("EiBotBoard"):
|
||||||
|
port_has_ebb = True
|
||||||
|
elif port[2].startswith("USB VID:PID=04D8:FD92"):
|
||||||
|
port_has_ebb = True
|
||||||
|
if port_has_ebb:
|
||||||
|
ebb_ports_list.append(port)
|
||||||
|
if ebb_ports_list:
|
||||||
|
return ebb_ports_list
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def list_named_ebbs():
|
||||||
|
'''Return descriptive list of all EiBotBoard units'''
|
||||||
|
ebb_ports_list = listEBBports()
|
||||||
|
if not ebb_ports_list:
|
||||||
|
return None
|
||||||
|
ebb_names_list = []
|
||||||
|
for port in ebb_ports_list:
|
||||||
|
name_found = False
|
||||||
|
p_0 = port[0]
|
||||||
|
p_1 = port[1]
|
||||||
|
p_2 = port[2]
|
||||||
|
if p_1.startswith("EiBotBoard"):
|
||||||
|
temp_string = p_1[11:]
|
||||||
|
if temp_string:
|
||||||
|
if temp_string is not None:
|
||||||
|
ebb_names_list.append(temp_string)
|
||||||
|
name_found = True
|
||||||
|
if not name_found:
|
||||||
|
# Look for "SER=XXXX LOCAT" pattern,
|
||||||
|
# typical of Pyserial 3 on Windows.
|
||||||
|
if 'SER=' in p_2 and ' LOCAT' in p_2:
|
||||||
|
index1 = p_2.find('SER=') + len('SER=')
|
||||||
|
index2 = p_2.find(' LOCAT', index1)
|
||||||
|
temp_string = p_2[index1:index2]
|
||||||
|
if len(temp_string) < 3:
|
||||||
|
temp_string = None
|
||||||
|
if temp_string is not None:
|
||||||
|
ebb_names_list.append(temp_string)
|
||||||
|
name_found = True
|
||||||
|
if not name_found:
|
||||||
|
# Look for "...SNR=XXXX" pattern,
|
||||||
|
# typical of Pyserial 2.7 on Windows
|
||||||
|
if 'SNR=' in p_2:
|
||||||
|
index1 = p_2.find('SNR=') + len('SNR=')
|
||||||
|
index2 = len(p_2)
|
||||||
|
temp_string = p_2[index1:index2]
|
||||||
|
if len(temp_string) < 3:
|
||||||
|
temp_string = None
|
||||||
|
if temp_string is not None:
|
||||||
|
ebb_names_list.append(temp_string)
|
||||||
|
name_found = True
|
||||||
|
if not name_found:
|
||||||
|
ebb_names_list.append(p_0)
|
||||||
|
return ebb_names_list
|
||||||
|
|
||||||
|
|
||||||
|
def testPort(port_name):
|
||||||
|
"""
|
||||||
|
Open a given serial port, verify that it is an EiBotBoard,
|
||||||
|
and return a SerialPort object that we can reference later.
|
||||||
|
|
||||||
|
This routine only opens the port; it will need to be closed as well,
|
||||||
|
for example with closePort( port_name ).
|
||||||
|
You, who open the port, are responsible for closing it as well.
|
||||||
|
"""
|
||||||
|
if port_name is not None:
|
||||||
|
try:
|
||||||
|
serial_port = serial.Serial(port_name, timeout=1.0, baudrate=115200) # 1 second timeout!
|
||||||
|
|
||||||
|
serial_port.flushInput() # deprecated function name;
|
||||||
|
# use serial_port.reset_input_buffer()
|
||||||
|
# if we can be sure that we have pySerial 3+.
|
||||||
|
|
||||||
|
serial_port.write('v\r'.encode('ascii'))
|
||||||
|
str_version = serial_port.readline()
|
||||||
|
if str_version and str_version.startswith("EBB".encode('ascii')):
|
||||||
|
return serial_port
|
||||||
|
|
||||||
|
serial_port.write('v\r'.encode('ascii'))
|
||||||
|
str_version = serial_port.readline()
|
||||||
|
if str_version and str_version.startswith("EBB".encode('ascii')):
|
||||||
|
return serial_port
|
||||||
|
serial_port.close()
|
||||||
|
except serial.SerialException as err:
|
||||||
|
logger.error("Error testing serial port `{}` connection".format(port_name))
|
||||||
|
logger.info("Error context:", exc_info=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def openPort():
|
||||||
|
'''
|
||||||
|
Find and open a port to a single attached EiBotBoard.
|
||||||
|
The first port located will be used.
|
||||||
|
'''
|
||||||
|
found_port = findPort()
|
||||||
|
serial_port = testPort(found_port)
|
||||||
|
if serial_port:
|
||||||
|
return serial_port
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def open_named_port(port_name):
|
||||||
|
'''
|
||||||
|
Find and open a port to a single attached EiBotBoard, indicated by name.
|
||||||
|
The first port located will be used.
|
||||||
|
'''
|
||||||
|
found_port = find_named_ebb(port_name)
|
||||||
|
serial_port = testPort(found_port)
|
||||||
|
if serial_port:
|
||||||
|
return serial_port
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def closePort(port_name):
|
||||||
|
'''Close the given serial port.'''
|
||||||
|
if port_name is not None:
|
||||||
|
try:
|
||||||
|
port_name.close()
|
||||||
|
except serial.SerialException:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def query(port_name, cmd, verbose=True):
|
||||||
|
'''General command to send a query to the EiBotBoard'''
|
||||||
|
if port_name is not None and cmd is not None:
|
||||||
|
response = ''
|
||||||
|
try:
|
||||||
|
port_name.write(cmd.encode('ascii'))
|
||||||
|
response = port_name.readline().decode('ascii')
|
||||||
|
n_retry_count = 0
|
||||||
|
while len(response) == 0 and n_retry_count < 100:
|
||||||
|
# get new response to replace null response if necessary
|
||||||
|
response = port_name.readline()
|
||||||
|
n_retry_count += 1
|
||||||
|
if cmd.split(",")[0].strip().lower() not in ["a", "i", "mr", "pi", "qm", "qg", "v"]:
|
||||||
|
# Most queries return an "OK" after the data requested.
|
||||||
|
# We skip this for those few queries that do not return an extra line.
|
||||||
|
unused_response = port_name.readline() # read in extra blank/OK line
|
||||||
|
n_retry_count = 0
|
||||||
|
while len(unused_response) == 0 and n_retry_count < 100:
|
||||||
|
# get new response to replace null response if necessary
|
||||||
|
unused_response = port_name.readline()
|
||||||
|
n_retry_count += 1
|
||||||
|
except (serial.SerialException, IOError, RuntimeError, OSError) as err:
|
||||||
|
if verbose:
|
||||||
|
logger.error("Error reading serial data")
|
||||||
|
else:
|
||||||
|
logger.info("Error reading serial data")
|
||||||
|
logger.info("Error context:", exc_info=err)
|
||||||
|
|
||||||
|
if 'Err:' in response:
|
||||||
|
error_msg = '\n'.join(('Unexpected response from EBB.',
|
||||||
|
' Command: {0}'.format(cmd.strip()),
|
||||||
|
' Response: {0}'.format(response.strip())))
|
||||||
|
if verbose:
|
||||||
|
logger.error(error_msg)
|
||||||
|
else:
|
||||||
|
logger.info(error_msg)
|
||||||
|
return response
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def command(port_name, cmd, verbose=True):
|
||||||
|
'''General command to send a command to the EiBotBoard'''
|
||||||
|
if port_name is not None and cmd is not None:
|
||||||
|
try:
|
||||||
|
port_name.write(cmd.encode('ascii'))
|
||||||
|
response = port_name.readline().decode('ascii')
|
||||||
|
n_retry_count = 0
|
||||||
|
while len(response) == 0 and n_retry_count < 100:
|
||||||
|
# get new response to replace null response if necessary
|
||||||
|
response = port_name.readline().decode('ascii')
|
||||||
|
n_retry_count += 1
|
||||||
|
if response.strip().startswith("OK"):
|
||||||
|
# Debug option: indicate which command:
|
||||||
|
# inkex.errormsg( 'OK after command: ' + cmd )
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if response:
|
||||||
|
error_msg = '\n'.join(('Unexpected response from EBB.',
|
||||||
|
' Command: {0}'.format(cmd.strip()),
|
||||||
|
' Response: {0}'.format(response.strip())))
|
||||||
|
else:
|
||||||
|
error_msg = 'EBB Serial Timeout after command: {0}'.format(cmd)
|
||||||
|
if verbose:
|
||||||
|
logger.error(error_msg)
|
||||||
|
else:
|
||||||
|
logger.info(error_msg)
|
||||||
|
except (serial.SerialException, IOError, RuntimeError, OSError) as err:
|
||||||
|
if cmd.strip().lower() not in ["rb"]: # Ignore error on reboot (RB) command
|
||||||
|
if verbose:
|
||||||
|
logger.error('Failed after command: {0}'.format(cmd))
|
||||||
|
else:
|
||||||
|
logger.info('Failed after command: {0}'.format(cmd))
|
||||||
|
logger.info("Error context:", exc_info=err)
|
||||||
|
|
||||||
|
|
||||||
|
def bootload(port_name):
|
||||||
|
'''Enter bootloader mode. Do not try to read back data.'''
|
||||||
|
if port_name is not None:
|
||||||
|
try:
|
||||||
|
port_name.write('BL\r'.encode('ascii'))
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def min_version(port_name, version_string):
|
||||||
|
'''
|
||||||
|
Query the EBB firmware version for the EBB located at port_name.
|
||||||
|
Return True if the EBB firmware version is at least version_string.
|
||||||
|
Return False if the EBB firmware version is below version_string.
|
||||||
|
Return None if we are unable to determine True or False.
|
||||||
|
'''
|
||||||
|
if port_name is not None:
|
||||||
|
ebb_version_string = queryVersion(port_name) # Full string, human readable
|
||||||
|
ebb_version_string = ebb_version_string.split("Firmware Version ", 1)
|
||||||
|
|
||||||
|
if len(ebb_version_string) > 1:
|
||||||
|
ebb_version_string = ebb_version_string[1]
|
||||||
|
else:
|
||||||
|
return None # We haven't received a reasonable version number response.
|
||||||
|
|
||||||
|
ebb_version_string = ebb_version_string.strip() # Stripped copy, for number comparisons
|
||||||
|
if parse(ebb_version_string) >= parse(version_string):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def queryVersion(port_name):
|
||||||
|
'''Query EBB Version String'''
|
||||||
|
return query(port_name, 'V\r', True)
|
||||||
6
inkscape/linux/readme.md
Normal file
6
inkscape/linux/readme.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Inkscape Plugin
|
||||||
|
This directory contains the plugin that fits to the EggBot firmware.
|
||||||
|
Use the ebb_serial.py file to patch the plugin so that all /dev/ttyUSBx devices can be used.
|
||||||
|
Currently only the first found tty USB device will be used.
|
||||||
|
|
||||||
|
If plugin doesn't work because of missing python dependencies, just install them with apt or similar system tool.
|
||||||
@@ -1,633 +0,0 @@
|
|||||||
// AccelStepper.cpp
|
|
||||||
//
|
|
||||||
// Copyright (C) 2009-2013 Mike McCauley
|
|
||||||
// $Id: AccelStepper.cpp,v 1.19 2014/10/31 06:05:27 mikem Exp mikem $
|
|
||||||
|
|
||||||
#include "AccelStepper.h"
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// Some debugging assistance
|
|
||||||
void dump(uint8_t* p, int l)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < l; i++)
|
|
||||||
{
|
|
||||||
Serial.print(p[i], HEX);
|
|
||||||
Serial.print(" ");
|
|
||||||
}
|
|
||||||
Serial.println("");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void AccelStepper::moveTo(long absolute)
|
|
||||||
{
|
|
||||||
if (_targetPos != absolute)
|
|
||||||
{
|
|
||||||
_targetPos = absolute;
|
|
||||||
computeNewSpeed();
|
|
||||||
// compute new n?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::move(long relative)
|
|
||||||
{
|
|
||||||
moveTo(_currentPos + relative);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements steps according to the current step interval
|
|
||||||
// You must call this at least once per step
|
|
||||||
// returns true if a step occurred
|
|
||||||
boolean AccelStepper::runSpeed()
|
|
||||||
{
|
|
||||||
// Dont do anything unless we actually have a step interval
|
|
||||||
if (!_stepInterval)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
unsigned long time = micros();
|
|
||||||
unsigned long nextStepTime = _lastStepTime + _stepInterval;
|
|
||||||
// Gymnastics to detect wrapping of either the nextStepTime and/or the current time
|
|
||||||
if ( ((nextStepTime >= _lastStepTime) && ((time >= nextStepTime) || (time < _lastStepTime)))
|
|
||||||
|| ((nextStepTime < _lastStepTime) && ((time >= nextStepTime) && (time < _lastStepTime))))
|
|
||||||
{
|
|
||||||
if (_direction == DIRECTION_CW)
|
|
||||||
{
|
|
||||||
// Clockwise
|
|
||||||
_currentPos += 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Anticlockwise
|
|
||||||
_currentPos -= 1;
|
|
||||||
}
|
|
||||||
step(_currentPos);
|
|
||||||
|
|
||||||
_lastStepTime = time;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long AccelStepper::distanceToGo()
|
|
||||||
{
|
|
||||||
return _targetPos - _currentPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
long AccelStepper::targetPosition()
|
|
||||||
{
|
|
||||||
return _targetPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
long AccelStepper::currentPosition()
|
|
||||||
{
|
|
||||||
return _currentPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Useful during initialisations or after initial positioning
|
|
||||||
// Sets speed to 0
|
|
||||||
void AccelStepper::setCurrentPosition(long position)
|
|
||||||
{
|
|
||||||
_targetPos = _currentPos = position;
|
|
||||||
_n = 0;
|
|
||||||
_stepInterval = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::computeNewSpeed()
|
|
||||||
{
|
|
||||||
long distanceTo = distanceToGo(); // +ve is clockwise from curent location
|
|
||||||
|
|
||||||
long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16
|
|
||||||
|
|
||||||
if (distanceTo == 0 && stepsToStop <= 1)
|
|
||||||
{
|
|
||||||
// We are at the target and its time to stop
|
|
||||||
_stepInterval = 0;
|
|
||||||
_speed = 0.0;
|
|
||||||
_n = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (distanceTo > 0)
|
|
||||||
{
|
|
||||||
// We are anticlockwise from the target
|
|
||||||
// Need to go clockwise from here, maybe decelerate now
|
|
||||||
if (_n > 0)
|
|
||||||
{
|
|
||||||
// Currently accelerating, need to decel now? Or maybe going the wrong way?
|
|
||||||
if ((stepsToStop >= distanceTo) || _direction == DIRECTION_CCW)
|
|
||||||
_n = -stepsToStop; // Start deceleration
|
|
||||||
}
|
|
||||||
else if (_n < 0)
|
|
||||||
{
|
|
||||||
// Currently decelerating, need to accel again?
|
|
||||||
if ((stepsToStop < distanceTo) && _direction == DIRECTION_CW)
|
|
||||||
_n = -_n; // Start accceleration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (distanceTo < 0)
|
|
||||||
{
|
|
||||||
// We are clockwise from the target
|
|
||||||
// Need to go anticlockwise from here, maybe decelerate
|
|
||||||
if (_n > 0)
|
|
||||||
{
|
|
||||||
// Currently accelerating, need to decel now? Or maybe going the wrong way?
|
|
||||||
if ((stepsToStop >= -distanceTo) || _direction == DIRECTION_CW)
|
|
||||||
_n = -stepsToStop; // Start deceleration
|
|
||||||
}
|
|
||||||
else if (_n < 0)
|
|
||||||
{
|
|
||||||
// Currently decelerating, need to accel again?
|
|
||||||
if ((stepsToStop < -distanceTo) && _direction == DIRECTION_CCW)
|
|
||||||
_n = -_n; // Start accceleration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to accelerate or decelerate
|
|
||||||
if (_n == 0)
|
|
||||||
{
|
|
||||||
// First step from stopped
|
|
||||||
_cn = _c0;
|
|
||||||
_direction = (distanceTo > 0) ? DIRECTION_CW : DIRECTION_CCW;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Subsequent step. Works for accel (n is +_ve) and decel (n is -ve).
|
|
||||||
_cn = _cn - ((2.0 * _cn) / ((4.0 * _n) + 1)); // Equation 13
|
|
||||||
_cn = max(_cn, _cmin);
|
|
||||||
}
|
|
||||||
_n++;
|
|
||||||
_stepInterval = _cn;
|
|
||||||
_speed = 1000000.0 / _cn;
|
|
||||||
if (_direction == DIRECTION_CCW)
|
|
||||||
_speed = -_speed;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
Serial.println(_speed);
|
|
||||||
Serial.println(_acceleration);
|
|
||||||
Serial.println(_cn);
|
|
||||||
Serial.println(_c0);
|
|
||||||
Serial.println(_n);
|
|
||||||
Serial.println(_stepInterval);
|
|
||||||
Serial.println(distanceTo);
|
|
||||||
Serial.println(stepsToStop);
|
|
||||||
Serial.println("-----");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the motor to implement speed and acceleration in order to proceed to the target position
|
|
||||||
// You must call this at least once per step, preferably in your main loop
|
|
||||||
// If the motor is in the desired position, the cost is very small
|
|
||||||
// returns true if the motor is still running to the target position.
|
|
||||||
boolean AccelStepper::run()
|
|
||||||
{
|
|
||||||
if (runSpeed())
|
|
||||||
computeNewSpeed();
|
|
||||||
return _speed != 0.0 || distanceToGo() != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
AccelStepper::AccelStepper(uint8_t interface, uint8_t pin1, uint8_t pin2, uint8_t pin3, uint8_t pin4, bool enable)
|
|
||||||
{
|
|
||||||
_interface = interface;
|
|
||||||
_currentPos = 0;
|
|
||||||
_targetPos = 0;
|
|
||||||
_speed = 0.0;
|
|
||||||
_maxSpeed = 1.0;
|
|
||||||
_acceleration = 0.0;
|
|
||||||
_sqrt_twoa = 1.0;
|
|
||||||
_stepInterval = 0;
|
|
||||||
_minPulseWidth = 1;
|
|
||||||
_enablePin = 0xff;
|
|
||||||
_lastStepTime = 0;
|
|
||||||
_pin[0] = pin1;
|
|
||||||
_pin[1] = pin2;
|
|
||||||
_pin[2] = pin3;
|
|
||||||
_pin[3] = pin4;
|
|
||||||
|
|
||||||
// NEW
|
|
||||||
_n = 0;
|
|
||||||
_c0 = 0.0;
|
|
||||||
_cn = 0.0;
|
|
||||||
_cmin = 1.0;
|
|
||||||
_direction = DIRECTION_CCW;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
_pinInverted[i] = 0;
|
|
||||||
if (enable)
|
|
||||||
enableOutputs();
|
|
||||||
// Some reasonable default
|
|
||||||
setAcceleration(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
AccelStepper::AccelStepper(void (*forward)(), void (*backward)())
|
|
||||||
{
|
|
||||||
_interface = 0;
|
|
||||||
_currentPos = 0;
|
|
||||||
_targetPos = 0;
|
|
||||||
_speed = 0.0;
|
|
||||||
_maxSpeed = 1.0;
|
|
||||||
_acceleration = 0.0;
|
|
||||||
_sqrt_twoa = 1.0;
|
|
||||||
_stepInterval = 0;
|
|
||||||
_minPulseWidth = 1;
|
|
||||||
_enablePin = 0xff;
|
|
||||||
_lastStepTime = 0;
|
|
||||||
_pin[0] = 0;
|
|
||||||
_pin[1] = 0;
|
|
||||||
_pin[2] = 0;
|
|
||||||
_pin[3] = 0;
|
|
||||||
_forward = forward;
|
|
||||||
_backward = backward;
|
|
||||||
|
|
||||||
// NEW
|
|
||||||
_n = 0;
|
|
||||||
_c0 = 0.0;
|
|
||||||
_cn = 0.0;
|
|
||||||
_cmin = 1.0;
|
|
||||||
_direction = DIRECTION_CCW;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
_pinInverted[i] = 0;
|
|
||||||
// Some reasonable default
|
|
||||||
setAcceleration(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::setMaxSpeed(float speed)
|
|
||||||
{
|
|
||||||
if (_maxSpeed != speed)
|
|
||||||
{
|
|
||||||
_maxSpeed = speed;
|
|
||||||
_cmin = 1000000.0 / speed;
|
|
||||||
// Recompute _n from current speed and adjust speed if accelerating or cruising
|
|
||||||
if (_n > 0)
|
|
||||||
{
|
|
||||||
_n = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16
|
|
||||||
computeNewSpeed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::setAcceleration(float acceleration)
|
|
||||||
{
|
|
||||||
if (acceleration == 0.0)
|
|
||||||
return;
|
|
||||||
if (_acceleration != acceleration)
|
|
||||||
{
|
|
||||||
// Recompute _n per Equation 17
|
|
||||||
_n = _n * (_acceleration / acceleration);
|
|
||||||
// New c0 per Equation 7, with correction per Equation 15
|
|
||||||
_c0 = 0.676 * sqrt(2.0 / acceleration) * 1000000.0; // Equation 15
|
|
||||||
_acceleration = acceleration;
|
|
||||||
computeNewSpeed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::setSpeed(float speed)
|
|
||||||
{
|
|
||||||
if (speed == _speed)
|
|
||||||
return;
|
|
||||||
speed = constrain(speed, -_maxSpeed, _maxSpeed);
|
|
||||||
if (speed == 0.0)
|
|
||||||
_stepInterval = 0;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_stepInterval = fabs(1000000.0 / speed);
|
|
||||||
_direction = (speed > 0.0) ? DIRECTION_CW : DIRECTION_CCW;
|
|
||||||
}
|
|
||||||
_speed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
float AccelStepper::speed()
|
|
||||||
{
|
|
||||||
return _speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subclasses can override
|
|
||||||
void AccelStepper::step(long step)
|
|
||||||
{
|
|
||||||
switch (_interface)
|
|
||||||
{
|
|
||||||
case FUNCTION:
|
|
||||||
step0(step);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DRIVER:
|
|
||||||
step1(step);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FULL2WIRE:
|
|
||||||
step2(step);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FULL3WIRE:
|
|
||||||
step3(step);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FULL4WIRE:
|
|
||||||
step4(step);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HALF3WIRE:
|
|
||||||
step6(step);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HALF4WIRE:
|
|
||||||
step8(step);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// You might want to override this to implement eg serial output
|
|
||||||
// bit 0 of the mask corresponds to _pin[0]
|
|
||||||
// bit 1 of the mask corresponds to _pin[1]
|
|
||||||
// ....
|
|
||||||
void AccelStepper::setOutputPins(uint8_t mask)
|
|
||||||
{
|
|
||||||
uint8_t numpins = 2;
|
|
||||||
if (_interface == FULL4WIRE || _interface == HALF4WIRE)
|
|
||||||
numpins = 4;
|
|
||||||
else if (_interface == FULL3WIRE || _interface == HALF3WIRE)
|
|
||||||
numpins = 3;
|
|
||||||
uint8_t i;
|
|
||||||
for (i = 0; i < numpins; i++)
|
|
||||||
digitalWrite(_pin[i], (mask & (1 << i)) ? (HIGH ^ _pinInverted[i]) : (LOW ^ _pinInverted[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0 pin step function (ie for functional usage)
|
|
||||||
void AccelStepper::step0(long step)
|
|
||||||
{
|
|
||||||
if (_speed > 0)
|
|
||||||
_forward();
|
|
||||||
else
|
|
||||||
_backward();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 pin step function (ie for stepper drivers)
|
|
||||||
// This is passed the current step number (0 to 7)
|
|
||||||
// Subclasses can override
|
|
||||||
void AccelStepper::step1(long step)
|
|
||||||
{
|
|
||||||
// _pin[0] is step, _pin[1] is direction
|
|
||||||
setOutputPins(_direction ? 0b10 : 0b00); // Set direction first else get rogue pulses
|
|
||||||
setOutputPins(_direction ? 0b11 : 0b01); // step HIGH
|
|
||||||
// Caution 200ns setup time
|
|
||||||
// Delay the minimum allowed pulse width
|
|
||||||
delayMicroseconds(_minPulseWidth);
|
|
||||||
setOutputPins(_direction ? 0b10 : 0b00); // step LOW
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 2 pin step function
|
|
||||||
// This is passed the current step number (0 to 7)
|
|
||||||
// Subclasses can override
|
|
||||||
void AccelStepper::step2(long step)
|
|
||||||
{
|
|
||||||
switch (step & 0x3)
|
|
||||||
{
|
|
||||||
case 0: /* 01 */
|
|
||||||
setOutputPins(0b10);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: /* 11 */
|
|
||||||
setOutputPins(0b11);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: /* 10 */
|
|
||||||
setOutputPins(0b01);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: /* 00 */
|
|
||||||
setOutputPins(0b00);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 3 pin step function
|
|
||||||
// This is passed the current step number (0 to 7)
|
|
||||||
// Subclasses can override
|
|
||||||
void AccelStepper::step3(long step)
|
|
||||||
{
|
|
||||||
switch (step % 3)
|
|
||||||
{
|
|
||||||
case 0: // 100
|
|
||||||
setOutputPins(0b100);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // 001
|
|
||||||
setOutputPins(0b001);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: //010
|
|
||||||
setOutputPins(0b010);
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4 pin step function for half stepper
|
|
||||||
// This is passed the current step number (0 to 7)
|
|
||||||
// Subclasses can override
|
|
||||||
void AccelStepper::step4(long step)
|
|
||||||
{
|
|
||||||
switch (step & 0x3)
|
|
||||||
{
|
|
||||||
case 0: // 1010
|
|
||||||
setOutputPins(0b0101);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // 0110
|
|
||||||
setOutputPins(0b0110);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: //0101
|
|
||||||
setOutputPins(0b1010);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: //1001
|
|
||||||
setOutputPins(0b1001);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3 pin half step function
|
|
||||||
// This is passed the current step number (0 to 7)
|
|
||||||
// Subclasses can override
|
|
||||||
void AccelStepper::step6(long step)
|
|
||||||
{
|
|
||||||
switch (step % 6)
|
|
||||||
{
|
|
||||||
case 0: // 100
|
|
||||||
setOutputPins(0b100);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // 101
|
|
||||||
setOutputPins(0b101);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // 001
|
|
||||||
setOutputPins(0b001);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // 011
|
|
||||||
setOutputPins(0b011);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4: // 010
|
|
||||||
setOutputPins(0b010);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5: // 011
|
|
||||||
setOutputPins(0b110);
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4 pin half step function
|
|
||||||
// This is passed the current step number (0 to 7)
|
|
||||||
// Subclasses can override
|
|
||||||
void AccelStepper::step8(long step)
|
|
||||||
{
|
|
||||||
switch (step & 0x7)
|
|
||||||
{
|
|
||||||
case 0: // 1000
|
|
||||||
setOutputPins(0b0001);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // 1010
|
|
||||||
setOutputPins(0b0101);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // 0010
|
|
||||||
setOutputPins(0b0100);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // 0110
|
|
||||||
setOutputPins(0b0110);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4: // 0100
|
|
||||||
setOutputPins(0b0010);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5: //0101
|
|
||||||
setOutputPins(0b1010);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6: // 0001
|
|
||||||
setOutputPins(0b1000);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7: //1001
|
|
||||||
setOutputPins(0b1001);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevents power consumption on the outputs
|
|
||||||
void AccelStepper::disableOutputs()
|
|
||||||
{
|
|
||||||
if (! _interface) return;
|
|
||||||
|
|
||||||
setOutputPins(0); // Handles inversion automatically
|
|
||||||
if (_enablePin != 0xff)
|
|
||||||
digitalWrite(_enablePin, LOW ^ _enableInverted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::enableOutputs()
|
|
||||||
{
|
|
||||||
if (! _interface)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pinMode(_pin[0], OUTPUT);
|
|
||||||
pinMode(_pin[1], OUTPUT);
|
|
||||||
if (_interface == FULL4WIRE || _interface == HALF4WIRE)
|
|
||||||
{
|
|
||||||
pinMode(_pin[2], OUTPUT);
|
|
||||||
pinMode(_pin[3], OUTPUT);
|
|
||||||
}
|
|
||||||
else if (_interface == FULL3WIRE || _interface == HALF3WIRE)
|
|
||||||
{
|
|
||||||
pinMode(_pin[2], OUTPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_enablePin != 0xff)
|
|
||||||
{
|
|
||||||
pinMode(_enablePin, OUTPUT);
|
|
||||||
digitalWrite(_enablePin, HIGH ^ _enableInverted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::setMinPulseWidth(unsigned int minWidth)
|
|
||||||
{
|
|
||||||
_minPulseWidth = minWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::setEnablePin(uint8_t enablePin)
|
|
||||||
{
|
|
||||||
_enablePin = enablePin;
|
|
||||||
|
|
||||||
// This happens after construction, so init pin now.
|
|
||||||
if (_enablePin != 0xff)
|
|
||||||
{
|
|
||||||
pinMode(_enablePin, OUTPUT);
|
|
||||||
digitalWrite(_enablePin, HIGH ^ _enableInverted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::setPinsInverted(bool directionInvert, bool stepInvert, bool enableInvert)
|
|
||||||
{
|
|
||||||
_pinInverted[0] = stepInvert;
|
|
||||||
_pinInverted[1] = directionInvert;
|
|
||||||
_enableInverted = enableInvert;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert)
|
|
||||||
{
|
|
||||||
_pinInverted[0] = pin1Invert;
|
|
||||||
_pinInverted[1] = pin2Invert;
|
|
||||||
_pinInverted[2] = pin3Invert;
|
|
||||||
_pinInverted[3] = pin4Invert;
|
|
||||||
_enableInverted = enableInvert;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocks until the target position is reached and stopped
|
|
||||||
void AccelStepper::runToPosition()
|
|
||||||
{
|
|
||||||
while (run())
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean AccelStepper::runSpeedToPosition()
|
|
||||||
{
|
|
||||||
if (_targetPos == _currentPos)
|
|
||||||
return false;
|
|
||||||
if (_targetPos >_currentPos)
|
|
||||||
_direction = DIRECTION_CW;
|
|
||||||
else
|
|
||||||
_direction = DIRECTION_CCW;
|
|
||||||
return runSpeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocks until the new target position is reached
|
|
||||||
void AccelStepper::runToNewPosition(long position)
|
|
||||||
{
|
|
||||||
moveTo(position);
|
|
||||||
runToPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AccelStepper::stop()
|
|
||||||
{
|
|
||||||
if (_speed != 0.0)
|
|
||||||
{
|
|
||||||
long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)) + 1; // Equation 16 (+integer rounding)
|
|
||||||
if (_speed > 0)
|
|
||||||
move(stepsToStop);
|
|
||||||
else
|
|
||||||
move(-stepsToStop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,666 +0,0 @@
|
|||||||
// AccelStepper.h
|
|
||||||
//
|
|
||||||
/// \mainpage AccelStepper library for Arduino
|
|
||||||
///
|
|
||||||
/// This is the Arduino AccelStepper library.
|
|
||||||
/// It provides an object-oriented interface for 2, 3 or 4 pin stepper motors.
|
|
||||||
///
|
|
||||||
/// The standard Arduino IDE includes the Stepper library
|
|
||||||
/// (http://arduino.cc/en/Reference/Stepper) for stepper motors. It is
|
|
||||||
/// perfectly adequate for simple, single motor applications.
|
|
||||||
///
|
|
||||||
/// AccelStepper significantly improves on the standard Arduino Stepper library in several ways:
|
|
||||||
/// \li Supports acceleration and deceleration
|
|
||||||
/// \li Supports multiple simultaneous steppers, with independent concurrent stepping on each stepper
|
|
||||||
/// \li API functions never delay() or block
|
|
||||||
/// \li Supports 2, 3 and 4 wire steppers, plus 3 and 4 wire half steppers.
|
|
||||||
/// \li Supports alternate stepping functions to enable support of AFMotor (https://github.com/adafruit/Adafruit-Motor-Shield-library)
|
|
||||||
/// \li Supports stepper drivers such as the Sparkfun EasyDriver (based on 3967 driver chip)
|
|
||||||
/// \li Very slow speeds are supported
|
|
||||||
/// \li Extensive API
|
|
||||||
/// \li Subclass support
|
|
||||||
///
|
|
||||||
/// The latest version of this documentation can be downloaded from
|
|
||||||
/// http://www.airspayce.com/mikem/arduino/AccelStepper
|
|
||||||
/// The version of the package that this documentation refers to can be downloaded
|
|
||||||
/// from http://www.airspayce.com/mikem/arduino/AccelStepper/AccelStepper-1.47.zip
|
|
||||||
///
|
|
||||||
/// Example Arduino programs are included to show the main modes of use.
|
|
||||||
///
|
|
||||||
/// You can also find online help and discussion at http://groups.google.com/group/accelstepper
|
|
||||||
/// Please use that group for all questions and discussions on this topic.
|
|
||||||
/// Do not contact the author directly, unless it is to discuss commercial licensing.
|
|
||||||
/// Before asking a question or reporting a bug, please read http://www.catb.org/esr/faqs/smart-questions.html
|
|
||||||
///
|
|
||||||
/// Tested on Arduino Diecimila and Mega with arduino-0018 & arduino-0021
|
|
||||||
/// on OpenSuSE 11.1 and avr-libc-1.6.1-1.15,
|
|
||||||
/// cross-avr-binutils-2.19-9.1, cross-avr-gcc-4.1.3_20080612-26.5.
|
|
||||||
/// Tested on Teensy http://www.pjrc.com/teensy including Teensy 3.1 built using Arduino IDE 1.0.5 with
|
|
||||||
/// teensyduino addon 1.18 and later.
|
|
||||||
///
|
|
||||||
/// \par Installation
|
|
||||||
///
|
|
||||||
/// Install in the usual way: unzip the distribution zip file to the libraries
|
|
||||||
/// sub-folder of your sketchbook.
|
|
||||||
///
|
|
||||||
/// \par Theory
|
|
||||||
///
|
|
||||||
/// This code uses speed calculations as described in
|
|
||||||
/// "Generate stepper-motor speed profiles in real time" by David Austin
|
|
||||||
/// http://fab.cba.mit.edu/classes/MIT/961.09/projects/i0/Stepper_Motor_Speed_Profile.pdf
|
|
||||||
/// with the exception that AccelStepper uses steps per second rather than radians per second
|
|
||||||
/// (because we dont know the step angle of the motor)
|
|
||||||
/// An initial step interval is calculated for the first step, based on the desired acceleration
|
|
||||||
/// On subsequent steps, shorter step intervals are calculated based
|
|
||||||
/// on the previous step until max speed is achieved.
|
|
||||||
///
|
|
||||||
/// \par Donations
|
|
||||||
///
|
|
||||||
/// This library is offered under a free GPL license for those who want to use it that way.
|
|
||||||
/// We try hard to keep it up to date, fix bugs
|
|
||||||
/// and to provide free support. If this library has helped you save time or money, please consider donating at
|
|
||||||
/// http://www.airspayce.com or here:
|
|
||||||
///
|
|
||||||
/// \htmlonly <form action="https://www.paypal.com/cgi-bin/webscr" method="post"><input type="hidden" name="cmd" value="_donations" /> <input type="hidden" name="business" value="mikem@airspayce.com" /> <input type="hidden" name="lc" value="AU" /> <input type="hidden" name="item_name" value="Airspayce" /> <input type="hidden" name="item_number" value="AccelStepper" /> <input type="hidden" name="currency_code" value="USD" /> <input type="hidden" name="bn" value="PP-DonationsBF:btn_donateCC_LG.gif:NonHosted" /> <input type="image" alt="PayPal — The safer, easier way to pay online." name="submit" src="https://www.paypalobjects.com/en_AU/i/btn/btn_donateCC_LG.gif" /> <img alt="" src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif" width="1" height="1" border="0" /></form> \endhtmlonly
|
|
||||||
///
|
|
||||||
/// \par Trademarks
|
|
||||||
///
|
|
||||||
/// AccelStepper is a trademark of AirSpayce Pty Ltd. The AccelStepper mark was first used on April 26 2010 for
|
|
||||||
/// international trade, and is used only in relation to motor control hardware and software.
|
|
||||||
/// It is not to be confused with any other similar marks covering other goods and services.
|
|
||||||
///
|
|
||||||
/// \par Copyright
|
|
||||||
///
|
|
||||||
/// This software is Copyright (C) 2010 Mike McCauley. Use is subject to license
|
|
||||||
/// conditions. The main licensing options available are GPL V2 or Commercial:
|
|
||||||
///
|
|
||||||
/// \par Open Source Licensing GPL V2
|
|
||||||
/// This is the appropriate option if you want to share the source code of your
|
|
||||||
/// application with everyone you distribute it to, and you also want to give them
|
|
||||||
/// the right to share who uses it. If you wish to use this software under Open
|
|
||||||
/// Source Licensing, you must contribute all your source code to the open source
|
|
||||||
/// community in accordance with the GPL Version 2 when your application is
|
|
||||||
/// distributed. See http://www.gnu.org/copyleft/gpl.html
|
|
||||||
///
|
|
||||||
/// \par Commercial Licensing
|
|
||||||
/// This is the appropriate option if you are creating proprietary applications
|
|
||||||
/// and you are not prepared to distribute and share the source code of your
|
|
||||||
/// application. Contact info@airspayce.com for details.
|
|
||||||
///
|
|
||||||
/// \par Revision History
|
|
||||||
/// \version 1.0 Initial release
|
|
||||||
///
|
|
||||||
/// \version 1.1 Added speed() function to get the current speed.
|
|
||||||
/// \version 1.2 Added runSpeedToPosition() submitted by Gunnar Arndt.
|
|
||||||
/// \version 1.3 Added support for stepper drivers (ie with Step and Direction inputs) with _pins == 1
|
|
||||||
/// \version 1.4 Added functional contructor to support AFMotor, contributed by Limor, with example sketches.
|
|
||||||
/// \version 1.5 Improvements contributed by Peter Mousley: Use of microsecond steps and other speed improvements
|
|
||||||
/// to increase max stepping speed to about 4kHz. New option for user to set the min allowed pulse width.
|
|
||||||
/// Added checks for already running at max speed and skip further calcs if so.
|
|
||||||
/// \version 1.6 Fixed a problem with wrapping of microsecond stepping that could cause stepping to hang.
|
|
||||||
/// Reported by Sandy Noble.
|
|
||||||
/// Removed redundant _lastRunTime member.
|
|
||||||
/// \version 1.7 Fixed a bug where setCurrentPosition() did not always work as expected.
|
|
||||||
/// Reported by Peter Linhart.
|
|
||||||
/// \version 1.8 Added support for 4 pin half-steppers, requested by Harvey Moon
|
|
||||||
/// \version 1.9 setCurrentPosition() now also sets motor speed to 0.
|
|
||||||
/// \version 1.10 Builds on Arduino 1.0
|
|
||||||
/// \version 1.11 Improvments from Michael Ellison:
|
|
||||||
/// Added optional enable line support for stepper drivers
|
|
||||||
/// Added inversion for step/direction/enable lines for stepper drivers
|
|
||||||
/// \version 1.12 Announce Google Group
|
|
||||||
/// \version 1.13 Improvements to speed calculation. Cost of calculation is now less in the worst case,
|
|
||||||
/// and more or less constant in all cases. This should result in slightly beter high speed performance, and
|
|
||||||
/// reduce anomalous speed glitches when other steppers are accelerating.
|
|
||||||
/// However, its hard to see how to replace the sqrt() required at the very first step from 0 speed.
|
|
||||||
/// \version 1.14 Fixed a problem with compiling under arduino 0021 reported by EmbeddedMan
|
|
||||||
/// \version 1.15 Fixed a problem with runSpeedToPosition which did not correctly handle
|
|
||||||
/// running backwards to a smaller target position. Added examples
|
|
||||||
/// \version 1.16 Fixed some cases in the code where abs() was used instead of fabs().
|
|
||||||
/// \version 1.17 Added example ProportionalControl
|
|
||||||
/// \version 1.18 Fixed a problem: If one calls the funcion runSpeed() when Speed is zero, it makes steps
|
|
||||||
/// without counting. reported by Friedrich, Klappenbach.
|
|
||||||
/// \version 1.19 Added MotorInterfaceType and symbolic names for the number of pins to use
|
|
||||||
/// for the motor interface. Updated examples to suit.
|
|
||||||
/// Replaced individual pin assignment variables _pin1, _pin2 etc with array _pin[4].
|
|
||||||
/// _pins member changed to _interface.
|
|
||||||
/// Added _pinInverted array to simplify pin inversion operations.
|
|
||||||
/// Added new function setOutputPins() which sets the motor output pins.
|
|
||||||
/// It can be overridden in order to provide, say, serial output instead of parallel output
|
|
||||||
/// Some refactoring and code size reduction.
|
|
||||||
/// \version 1.20 Improved documentation and examples to show need for correctly
|
|
||||||
/// specifying AccelStepper::FULL4WIRE and friends.
|
|
||||||
/// \version 1.21 Fixed a problem where desiredSpeed could compute the wrong step acceleration
|
|
||||||
/// when _speed was small but non-zero. Reported by Brian Schmalz.
|
|
||||||
/// Precompute sqrt_twoa to improve performance and max possible stepping speed
|
|
||||||
/// \version 1.22 Added Bounce.pde example
|
|
||||||
/// Fixed a problem where calling moveTo(), setMaxSpeed(), setAcceleration() more
|
|
||||||
/// frequently than the step time, even
|
|
||||||
/// with the same values, would interfere with speed calcs. Now a new speed is computed
|
|
||||||
/// only if there was a change in the set value. Reported by Brian Schmalz.
|
|
||||||
/// \version 1.23 Rewrite of the speed algorithms in line with
|
|
||||||
/// http://fab.cba.mit.edu/classes/MIT/961.09/projects/i0/Stepper_Motor_Speed_Profile.pdf
|
|
||||||
/// Now expect smoother and more linear accelerations and decelerations. The desiredSpeed()
|
|
||||||
/// function was removed.
|
|
||||||
/// \version 1.24 Fixed a problem introduced in 1.23: with runToPosition, which did never returned
|
|
||||||
/// \version 1.25 Now ignore attempts to set acceleration to 0.0
|
|
||||||
/// \version 1.26 Fixed a problem where certina combinations of speed and accelration could cause
|
|
||||||
/// oscillation about the target position.
|
|
||||||
/// \version 1.27 Added stop() function to stop as fast as possible with current acceleration parameters.
|
|
||||||
/// Also added new Quickstop example showing its use.
|
|
||||||
/// \version 1.28 Fixed another problem where certain combinations of speed and accelration could cause
|
|
||||||
/// oscillation about the target position.
|
|
||||||
/// Added support for 3 wire full and half steppers such as Hard Disk Drive spindle.
|
|
||||||
/// Contributed by Yuri Ivatchkovitch.
|
|
||||||
/// \version 1.29 Fixed a problem that could cause a DRIVER stepper to continually step
|
|
||||||
/// with some sketches. Reported by Vadim.
|
|
||||||
/// \version 1.30 Fixed a problem that could cause stepper to back up a few steps at the end of
|
|
||||||
/// accelerated travel with certain speeds. Reported and patched by jolo.
|
|
||||||
/// \version 1.31 Updated author and distribution location details to airspayce.com
|
|
||||||
/// \version 1.32 Fixed a problem with enableOutputs() and setEnablePin on Arduino Due that
|
|
||||||
/// prevented the enable pin changing stae correctly. Reported by Duane Bishop.
|
|
||||||
/// \version 1.33 Fixed an error in example AFMotor_ConstantSpeed.pde did not setMaxSpeed();
|
|
||||||
/// Fixed a problem that caused incorrect pin sequencing of FULL3WIRE and HALF3WIRE.
|
|
||||||
/// Unfortunately this meant changing the signature for all step*() functions.
|
|
||||||
/// Added example MotorShield, showing how to use AdaFruit Motor Shield to control
|
|
||||||
/// a 3 phase motor such as a HDD spindle motor (and without using the AFMotor library.
|
|
||||||
/// \version 1.34 Added setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert)
|
|
||||||
/// to allow inversion of 2, 3 and 4 wire stepper pins. Requested by Oleg.
|
|
||||||
/// \version 1.35 Removed default args from setPinsInverted(bool, bool, bool, bool, bool) to prevent ambiguity with
|
|
||||||
/// setPinsInverted(bool, bool, bool). Reported by Mac Mac.
|
|
||||||
/// \version 1.36 Changed enableOutputs() and disableOutputs() to be virtual so can be overridden.
|
|
||||||
/// Added new optional argument 'enable' to constructor, which allows you toi disable the
|
|
||||||
/// automatic enabling of outputs at construction time. Suggested by Guido.
|
|
||||||
/// \version 1.37 Fixed a problem with step1 that could cause a rogue step in the
|
|
||||||
/// wrong direction (or not,
|
|
||||||
/// depending on the setup-time requirements of the connected hardware).
|
|
||||||
/// Reported by Mark Tillotson.
|
|
||||||
/// \version 1.38 run() function incorrectly always returned true. Updated function and doc so it returns true
|
|
||||||
/// if the motor is still running to the target position.
|
|
||||||
/// \version 1.39 Updated typos in keywords.txt, courtesey Jon Magill.
|
|
||||||
/// \version 1.40 Updated documentation, including testing on Teensy 3.1
|
|
||||||
/// \version 1.41 Fixed an error in the acceleration calculations, resulting in acceleration of haldf the intended value
|
|
||||||
/// \version 1.42 Improved support for FULL3WIRE and HALF3WIRE output pins. These changes were in Yuri's original
|
|
||||||
/// contribution but did not make it into production.<br>
|
|
||||||
/// \version 1.43 Added DualMotorShield example. Shows how to use AccelStepper to control 2 x 2 phase steppers using the
|
|
||||||
/// Itead Studio Arduino Dual Stepper Motor Driver Shield model IM120417015.<br>
|
|
||||||
/// \version 1.44 examples/DualMotorShield/DualMotorShield.ino examples/DualMotorShield/DualMotorShield.pde
|
|
||||||
/// was missing from the distribution.<br>
|
|
||||||
/// \version 1.45 Fixed a problem where if setAcceleration was not called, there was no default
|
|
||||||
/// acceleration. Reported by Michael Newman.<br>
|
|
||||||
/// \version 1.45 Fixed inaccuracy in acceleration rate by using Equation 15, suggested by Sebastian Gracki.<br>
|
|
||||||
/// Performance improvements in runSpeed suggested by Jaakko Fagerlund.<br>
|
|
||||||
/// \version 1.46 Fixed error in documentation for runToPosition().
|
|
||||||
/// Reinstated time calculations in runSpeed() since new version is reported
|
|
||||||
/// not to work correctly under some circumstances. Reported by Oleg V Gavva.<br>
|
|
||||||
|
|
||||||
///
|
|
||||||
/// \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS
|
|
||||||
// Copyright (C) 2009-2013 Mike McCauley
|
|
||||||
// $Id: AccelStepper.h,v 1.21 2014/10/31 06:05:30 mikem Exp mikem $
|
|
||||||
|
|
||||||
#ifndef AccelStepper_h
|
|
||||||
#define AccelStepper_h
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#if ARDUINO >= 100
|
|
||||||
#include <Arduino.h>
|
|
||||||
#else
|
|
||||||
#include <WProgram.h>
|
|
||||||
#include <wiring.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// These defs cause trouble on some versions of Arduino
|
|
||||||
#undef round
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
/// \class AccelStepper AccelStepper.h <AccelStepper.h>
|
|
||||||
/// \brief Support for stepper motors with acceleration etc.
|
|
||||||
///
|
|
||||||
/// This defines a single 2 or 4 pin stepper motor, or stepper moter with fdriver chip, with optional
|
|
||||||
/// acceleration, deceleration, absolute positioning commands etc. Multiple
|
|
||||||
/// simultaneous steppers are supported, all moving
|
|
||||||
/// at different speeds and accelerations.
|
|
||||||
///
|
|
||||||
/// \par Operation
|
|
||||||
/// This module operates by computing a step time in microseconds. The step
|
|
||||||
/// time is recomputed after each step and after speed and acceleration
|
|
||||||
/// parameters are changed by the caller. The time of each step is recorded in
|
|
||||||
/// microseconds. The run() function steps the motor once if a new step is due.
|
|
||||||
/// The run() function must be called frequently until the motor is in the
|
|
||||||
/// desired position, after which time run() will do nothing.
|
|
||||||
///
|
|
||||||
/// \par Positioning
|
|
||||||
/// Positions are specified by a signed long integer. At
|
|
||||||
/// construction time, the current position of the motor is consider to be 0. Positive
|
|
||||||
/// positions are clockwise from the initial position; negative positions are
|
|
||||||
/// anticlockwise. The current position can be altered for instance after
|
|
||||||
/// initialization positioning.
|
|
||||||
///
|
|
||||||
/// \par Caveats
|
|
||||||
/// This is an open loop controller: If the motor stalls or is oversped,
|
|
||||||
/// AccelStepper will not have a correct
|
|
||||||
/// idea of where the motor really is (since there is no feedback of the motor's
|
|
||||||
/// real position. We only know where we _think_ it is, relative to the
|
|
||||||
/// initial starting point).
|
|
||||||
///
|
|
||||||
/// \par Performance
|
|
||||||
/// The fastest motor speed that can be reliably supported is about 4000 steps per
|
|
||||||
/// second at a clock frequency of 16 MHz on Arduino such as Uno etc.
|
|
||||||
/// Faster processors can support faster stepping speeds.
|
|
||||||
/// However, any speed less than that
|
|
||||||
/// down to very slow speeds (much less than one per second) are also supported,
|
|
||||||
/// provided the run() function is called frequently enough to step the motor
|
|
||||||
/// whenever required for the speed set.
|
|
||||||
/// Calling setAcceleration() is expensive,
|
|
||||||
/// since it requires a square root to be calculated.
|
|
||||||
class AccelStepper
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// \brief Symbolic names for number of pins.
|
|
||||||
/// Use this in the pins argument the AccelStepper constructor to
|
|
||||||
/// provide a symbolic name for the number of pins
|
|
||||||
/// to use.
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
FUNCTION = 0, ///< Use the functional interface, implementing your own driver functions (internal use only)
|
|
||||||
DRIVER = 1, ///< Stepper Driver, 2 driver pins required
|
|
||||||
FULL2WIRE = 2, ///< 2 wire stepper, 2 motor pins required
|
|
||||||
FULL3WIRE = 3, ///< 3 wire stepper, such as HDD spindle, 3 motor pins required
|
|
||||||
FULL4WIRE = 4, ///< 4 wire full stepper, 4 motor pins required
|
|
||||||
HALF3WIRE = 6, ///< 3 wire half stepper, such as HDD spindle, 3 motor pins required
|
|
||||||
HALF4WIRE = 8 ///< 4 wire half stepper, 4 motor pins required
|
|
||||||
} MotorInterfaceType;
|
|
||||||
|
|
||||||
/// Constructor. You can have multiple simultaneous steppers, all moving
|
|
||||||
/// at different speeds and accelerations, provided you call their run()
|
|
||||||
/// functions at frequent enough intervals. Current Position is set to 0, target
|
|
||||||
/// position is set to 0. MaxSpeed and Acceleration default to 1.0.
|
|
||||||
/// The motor pins will be initialised to OUTPUT mode during the
|
|
||||||
/// constructor by a call to enableOutputs().
|
|
||||||
/// \param[in] interface Number of pins to interface to. 1, 2, 4 or 8 are
|
|
||||||
/// supported, but it is preferred to use the \ref MotorInterfaceType symbolic names.
|
|
||||||
/// AccelStepper::DRIVER (1) means a stepper driver (with Step and Direction pins).
|
|
||||||
/// If an enable line is also needed, call setEnablePin() after construction.
|
|
||||||
/// You may also invert the pins using setPinsInverted().
|
|
||||||
/// AccelStepper::FULL2WIRE (2) means a 2 wire stepper (2 pins required).
|
|
||||||
/// AccelStepper::FULL3WIRE (3) means a 3 wire stepper, such as HDD spindle (3 pins required).
|
|
||||||
/// AccelStepper::FULL4WIRE (4) means a 4 wire stepper (4 pins required).
|
|
||||||
/// AccelStepper::HALF3WIRE (6) means a 3 wire half stepper, such as HDD spindle (3 pins required)
|
|
||||||
/// AccelStepper::HALF4WIRE (8) means a 4 wire half stepper (4 pins required)
|
|
||||||
/// Defaults to AccelStepper::FULL4WIRE (4) pins.
|
|
||||||
/// \param[in] pin1 Arduino digital pin number for motor pin 1. Defaults
|
|
||||||
/// to pin 2. For a AccelStepper::DRIVER (interface==1),
|
|
||||||
/// this is the Step input to the driver. Low to high transition means to step)
|
|
||||||
/// \param[in] pin2 Arduino digital pin number for motor pin 2. Defaults
|
|
||||||
/// to pin 3. For a AccelStepper::DRIVER (interface==1),
|
|
||||||
/// this is the Direction input the driver. High means forward.
|
|
||||||
/// \param[in] pin3 Arduino digital pin number for motor pin 3. Defaults
|
|
||||||
/// to pin 4.
|
|
||||||
/// \param[in] pin4 Arduino digital pin number for motor pin 4. Defaults
|
|
||||||
/// to pin 5.
|
|
||||||
/// \param[in] enable If this is true (the default), enableOutputs() will be called to enable
|
|
||||||
/// the output pins at construction time.
|
|
||||||
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);
|
|
||||||
|
|
||||||
/// Alternate Constructor which will call your own functions for forward and backward steps.
|
|
||||||
/// You can have multiple simultaneous steppers, all moving
|
|
||||||
/// at different speeds and accelerations, provided you call their run()
|
|
||||||
/// functions at frequent enough intervals. Current Position is set to 0, target
|
|
||||||
/// position is set to 0. MaxSpeed and Acceleration default to 1.0.
|
|
||||||
/// Any motor initialization should happen before hand, no pins are used or initialized.
|
|
||||||
/// \param[in] forward void-returning procedure that will make a forward step
|
|
||||||
/// \param[in] backward void-returning procedure that will make a backward step
|
|
||||||
AccelStepper(void (*forward)(), void (*backward)());
|
|
||||||
|
|
||||||
/// Set the target position. The run() function will try to move the motor (at most one step per call)
|
|
||||||
/// from the current position to the target position set by the most
|
|
||||||
/// recent call to this function. Caution: moveTo() also recalculates the speed for the next step.
|
|
||||||
/// If you are trying to use constant speed movements, you should call setSpeed() after calling moveTo().
|
|
||||||
/// \param[in] absolute The desired absolute position. Negative is
|
|
||||||
/// anticlockwise from the 0 position.
|
|
||||||
void moveTo(long absolute);
|
|
||||||
|
|
||||||
/// Set the target position relative to the current position
|
|
||||||
/// \param[in] relative The desired position relative to the current position. Negative is
|
|
||||||
/// anticlockwise from the current position.
|
|
||||||
void move(long relative);
|
|
||||||
|
|
||||||
/// Poll the motor and step it if a step is due, implementing
|
|
||||||
/// accelerations and decelerations to acheive the target position. You must call this as
|
|
||||||
/// frequently as possible, but at least once per minimum step time interval,
|
|
||||||
/// 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,
|
|
||||||
/// based on the current speed and the time since the last step.
|
|
||||||
/// \return true if the motor is still running to the target position.
|
|
||||||
boolean run();
|
|
||||||
|
|
||||||
/// Poll the motor and step it if a step is due, implementing a constant
|
|
||||||
/// speed as set by the most recent call to setSpeed(). You must call this as
|
|
||||||
/// frequently as possible, but at least once per step interval,
|
|
||||||
/// \return true if the motor was stepped.
|
|
||||||
boolean runSpeed();
|
|
||||||
|
|
||||||
/// Sets the maximum permitted speed. The run() function will accelerate
|
|
||||||
/// up to the speed set by this function.
|
|
||||||
/// Caution: the maximum speed achievable depends on your processor and clock speed.
|
|
||||||
/// \param[in] speed The desired maximum speed in steps per second. Must
|
|
||||||
/// be > 0. Caution: Speeds that exceed the maximum speed supported by the processor may
|
|
||||||
/// Result in non-linear accelerations and decelerations.
|
|
||||||
void setMaxSpeed(float speed);
|
|
||||||
|
|
||||||
/// Sets the acceleration/deceleration rate.
|
|
||||||
/// \param[in] acceleration The desired acceleration in steps per second
|
|
||||||
/// per second. Must be > 0.0. This is an expensive call since it requires a square
|
|
||||||
/// root to be calculated. Dont call more ofthen than needed
|
|
||||||
void setAcceleration(float acceleration);
|
|
||||||
|
|
||||||
/// Sets the desired constant speed for use with runSpeed().
|
|
||||||
/// \param[in] speed The desired constant speed in steps per
|
|
||||||
/// second. Positive is clockwise. Speeds of more than 1000 steps per
|
|
||||||
/// second are unreliable. Very slow speeds may be set (eg 0.00027777 for
|
|
||||||
/// once per hour, approximately. Speed accuracy depends on the Arduino
|
|
||||||
/// crystal. Jitter depends on how frequently you call the runSpeed() function.
|
|
||||||
void setSpeed(float speed);
|
|
||||||
|
|
||||||
/// The most recently set speed
|
|
||||||
/// \return the most recent speed in steps per second
|
|
||||||
float speed();
|
|
||||||
|
|
||||||
/// The distance from the current position to the target position.
|
|
||||||
/// \return the distance from the current position to the target position
|
|
||||||
/// in steps. Positive is clockwise from the current position.
|
|
||||||
long distanceToGo();
|
|
||||||
|
|
||||||
/// The most recently set target position.
|
|
||||||
/// \return the target position
|
|
||||||
/// in steps. Positive is clockwise from the 0 position.
|
|
||||||
long targetPosition();
|
|
||||||
|
|
||||||
/// The currently motor position.
|
|
||||||
/// \return the current motor position
|
|
||||||
/// in steps. Positive is clockwise from the 0 position.
|
|
||||||
long currentPosition();
|
|
||||||
|
|
||||||
/// Resets the current position of the motor, so that wherever the motor
|
|
||||||
/// happens to be right now is considered to be the new 0 position. Useful
|
|
||||||
/// for setting a zero position on a stepper after an initial hardware
|
|
||||||
/// positioning move.
|
|
||||||
/// Has the side effect of setting the current motor speed to 0.
|
|
||||||
/// \param[in] position The position in steps of wherever the motor
|
|
||||||
/// happens to be right now.
|
|
||||||
void setCurrentPosition(long position);
|
|
||||||
|
|
||||||
/// Moves the motor (with acceleration/deceleration)
|
|
||||||
/// to the target position and blocks until it is at
|
|
||||||
/// position. Dont use this in event loops, since it blocks.
|
|
||||||
void runToPosition();
|
|
||||||
|
|
||||||
/// Runs at the currently selected speed until the target position is reached
|
|
||||||
/// Does not implement accelerations.
|
|
||||||
/// \return true if it stepped
|
|
||||||
boolean runSpeedToPosition();
|
|
||||||
|
|
||||||
/// Moves the motor (with acceleration/deceleration)
|
|
||||||
/// to the new target position and blocks until it is at
|
|
||||||
/// position. Dont use this in event loops, since it blocks.
|
|
||||||
/// \param[in] position The new target position.
|
|
||||||
void runToNewPosition(long position);
|
|
||||||
|
|
||||||
/// Sets a new target position that causes the stepper
|
|
||||||
/// to stop as quickly as possible, using the current speed and acceleration parameters.
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
/// Disable motor pin outputs by setting them all LOW
|
|
||||||
/// Depending on the design of your electronics this may turn off
|
|
||||||
/// the power to the motor coils, saving power.
|
|
||||||
/// This is useful to support Arduino low power modes: disable the outputs
|
|
||||||
/// during sleep and then reenable with enableOutputs() before stepping
|
|
||||||
/// again.
|
|
||||||
virtual void disableOutputs();
|
|
||||||
|
|
||||||
/// Enable motor pin outputs by setting the motor pins to OUTPUT
|
|
||||||
/// mode. Called automatically by the constructor.
|
|
||||||
virtual void enableOutputs();
|
|
||||||
|
|
||||||
/// Sets the minimum pulse width allowed by the stepper driver. The minimum practical pulse width is
|
|
||||||
/// approximately 20 microseconds. Times less than 20 microseconds
|
|
||||||
/// will usually result in 20 microseconds or so.
|
|
||||||
/// \param[in] minWidth The minimum pulse width in microseconds.
|
|
||||||
void setMinPulseWidth(unsigned int minWidth);
|
|
||||||
|
|
||||||
/// Sets the enable pin number for stepper drivers.
|
|
||||||
/// 0xFF indicates unused (default).
|
|
||||||
/// Otherwise, if a pin is set, the pin will be turned on when
|
|
||||||
/// enableOutputs() is called and switched off when disableOutputs()
|
|
||||||
/// is called.
|
|
||||||
/// \param[in] enablePin Arduino digital pin number for motor enable
|
|
||||||
/// \sa setPinsInverted
|
|
||||||
void setEnablePin(uint8_t enablePin = 0xff);
|
|
||||||
|
|
||||||
/// Sets the inversion for stepper driver pins
|
|
||||||
/// \param[in] directionInvert True for inverted direction pin, false for non-inverted
|
|
||||||
/// \param[in] stepInvert True for inverted step pin, false for non-inverted
|
|
||||||
/// \param[in] enableInvert True for inverted enable pin, false (default) for non-inverted
|
|
||||||
void setPinsInverted(bool directionInvert = false, bool stepInvert = false, bool enableInvert = false);
|
|
||||||
|
|
||||||
/// Sets the inversion for 2, 3 and 4 wire stepper pins
|
|
||||||
/// \param[in] pin1Invert True for inverted pin1, false for non-inverted
|
|
||||||
/// \param[in] pin2Invert True for inverted pin2, false for non-inverted
|
|
||||||
/// \param[in] pin3Invert True for inverted pin3, false for non-inverted
|
|
||||||
/// \param[in] pin4Invert True for inverted pin4, false for non-inverted
|
|
||||||
/// \param[in] enableInvert True for inverted enable pin, false (default) for non-inverted
|
|
||||||
void setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/// \brief Direction indicator
|
|
||||||
/// Symbolic names for the direction the motor is turning
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
DIRECTION_CCW = 0, ///< Clockwise
|
|
||||||
DIRECTION_CW = 1 ///< Counter-Clockwise
|
|
||||||
} Direction;
|
|
||||||
|
|
||||||
/// Forces the library to compute a new instantaneous speed and set that as
|
|
||||||
/// the current speed. It is called by
|
|
||||||
/// the library:
|
|
||||||
/// \li after each step
|
|
||||||
/// \li after change to maxSpeed through setMaxSpeed()
|
|
||||||
/// \li after change to acceleration through setAcceleration()
|
|
||||||
/// \li after change to target position (relative or absolute) through
|
|
||||||
/// move() or moveTo()
|
|
||||||
void computeNewSpeed();
|
|
||||||
|
|
||||||
/// Low level function to set the motor output pins
|
|
||||||
/// bit 0 of the mask corresponds to _pin[0]
|
|
||||||
/// bit 1 of the mask corresponds to _pin[1]
|
|
||||||
/// You can override this to impment, for example serial chip output insted of using the
|
|
||||||
/// output pins directly
|
|
||||||
virtual void setOutputPins(uint8_t mask);
|
|
||||||
|
|
||||||
/// Called to execute a step. Only called when a new step is
|
|
||||||
/// required. Subclasses may override to implement new stepping
|
|
||||||
/// interfaces. The default calls step1(), step2(), step4() or step8() depending on the
|
|
||||||
/// number of pins defined for the stepper.
|
|
||||||
/// \param[in] step The current step phase number (0 to 7)
|
|
||||||
virtual void step(long step);
|
|
||||||
|
|
||||||
/// Called to execute a step using stepper functions (pins = 0) Only called when a new step is
|
|
||||||
/// required. Calls _forward() or _backward() to perform the step
|
|
||||||
/// \param[in] step The current step phase number (0 to 7)
|
|
||||||
virtual void step0(long step);
|
|
||||||
|
|
||||||
/// Called to execute a step on a stepper driver (ie where pins == 1). Only called when a new step is
|
|
||||||
/// required. Subclasses may override to implement new stepping
|
|
||||||
/// interfaces. The default sets or clears the outputs of Step pin1 to step,
|
|
||||||
/// and sets the output of _pin2 to the desired direction. The Step pin (_pin1) is pulsed for 1 microsecond
|
|
||||||
/// which is the minimum STEP pulse width for the 3967 driver.
|
|
||||||
/// \param[in] step The current step phase number (0 to 7)
|
|
||||||
virtual void step1(long step);
|
|
||||||
|
|
||||||
/// Called to execute a step on a 2 pin motor. Only called when a new step is
|
|
||||||
/// required. Subclasses may override to implement new stepping
|
|
||||||
/// interfaces. The default sets or clears the outputs of pin1 and pin2
|
|
||||||
/// \param[in] step The current step phase number (0 to 7)
|
|
||||||
virtual void step2(long step);
|
|
||||||
|
|
||||||
/// Called to execute a step on a 3 pin motor, such as HDD spindle. Only called when a new step is
|
|
||||||
/// required. Subclasses may override to implement new stepping
|
|
||||||
/// interfaces. The default sets or clears the outputs of pin1, pin2,
|
|
||||||
/// pin3
|
|
||||||
/// \param[in] step The current step phase number (0 to 7)
|
|
||||||
virtual void step3(long step);
|
|
||||||
|
|
||||||
/// Called to execute a step on a 4 pin motor. Only called when a new step is
|
|
||||||
/// required. Subclasses may override to implement new stepping
|
|
||||||
/// interfaces. The default sets or clears the outputs of pin1, pin2,
|
|
||||||
/// pin3, pin4.
|
|
||||||
/// \param[in] step The current step phase number (0 to 7)
|
|
||||||
virtual void step4(long step);
|
|
||||||
|
|
||||||
/// Called to execute a step on a 3 pin motor, such as HDD spindle. Only called when a new step is
|
|
||||||
/// required. Subclasses may override to implement new stepping
|
|
||||||
/// interfaces. The default sets or clears the outputs of pin1, pin2,
|
|
||||||
/// pin3
|
|
||||||
/// \param[in] step The current step phase number (0 to 7)
|
|
||||||
virtual void step6(long step);
|
|
||||||
|
|
||||||
/// Called to execute a step on a 4 pin half-steper motor. Only called when a new step is
|
|
||||||
/// required. Subclasses may override to implement new stepping
|
|
||||||
/// interfaces. The default sets or clears the outputs of pin1, pin2,
|
|
||||||
/// pin3, pin4.
|
|
||||||
/// \param[in] step The current step phase number (0 to 7)
|
|
||||||
virtual void step8(long step);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Number of pins on the stepper motor. Permits 2 or 4. 2 pins is a
|
|
||||||
/// bipolar, and 4 pins is a unipolar.
|
|
||||||
uint8_t _interface; // 0, 1, 2, 4, 8, See MotorInterfaceType
|
|
||||||
|
|
||||||
/// Arduino pin number assignments for the 2 or 4 pins required to interface to the
|
|
||||||
/// stepper motor or driver
|
|
||||||
uint8_t _pin[4];
|
|
||||||
|
|
||||||
/// Whether the _pins is inverted or not
|
|
||||||
uint8_t _pinInverted[4];
|
|
||||||
|
|
||||||
/// The current absolution position in steps.
|
|
||||||
long _currentPos; // Steps
|
|
||||||
|
|
||||||
/// The target position in steps. The AccelStepper library will move the
|
|
||||||
/// motor from the _currentPos to the _targetPos, taking into account the
|
|
||||||
/// max speed, acceleration and deceleration
|
|
||||||
long _targetPos; // Steps
|
|
||||||
|
|
||||||
/// The current motos speed in steps per second
|
|
||||||
/// Positive is clockwise
|
|
||||||
float _speed; // Steps per second
|
|
||||||
|
|
||||||
/// The maximum permitted speed in steps per second. Must be > 0.
|
|
||||||
float _maxSpeed;
|
|
||||||
|
|
||||||
/// The acceleration to use to accelerate or decelerate the motor in steps
|
|
||||||
/// per second per second. Must be > 0
|
|
||||||
float _acceleration;
|
|
||||||
float _sqrt_twoa; // Precomputed sqrt(2*_acceleration)
|
|
||||||
|
|
||||||
/// The current interval between steps in microseconds.
|
|
||||||
/// 0 means the motor is currently stopped with _speed == 0
|
|
||||||
unsigned long _stepInterval;
|
|
||||||
|
|
||||||
/// The last step time in microseconds
|
|
||||||
unsigned long _lastStepTime;
|
|
||||||
|
|
||||||
/// The minimum allowed pulse width in microseconds
|
|
||||||
unsigned int _minPulseWidth;
|
|
||||||
|
|
||||||
/// Is the direction pin inverted?
|
|
||||||
///bool _dirInverted; /// Moved to _pinInverted[1]
|
|
||||||
|
|
||||||
/// Is the step pin inverted?
|
|
||||||
///bool _stepInverted; /// Moved to _pinInverted[0]
|
|
||||||
|
|
||||||
/// Is the enable pin inverted?
|
|
||||||
bool _enableInverted;
|
|
||||||
|
|
||||||
/// Enable pin for stepper driver, or 0xFF if unused.
|
|
||||||
uint8_t _enablePin;
|
|
||||||
|
|
||||||
/// The pointer to a forward-step procedure
|
|
||||||
void (*_forward)();
|
|
||||||
|
|
||||||
/// The pointer to a backward-step procedure
|
|
||||||
void (*_backward)();
|
|
||||||
|
|
||||||
/// The step counter for speed calculations
|
|
||||||
long _n;
|
|
||||||
|
|
||||||
/// Initial step size in microseconds
|
|
||||||
float _c0;
|
|
||||||
|
|
||||||
/// Last step size in microseconds
|
|
||||||
float _cn;
|
|
||||||
|
|
||||||
/// Min step size in microseconds based on maxSpeed
|
|
||||||
float _cmin; // at max speed
|
|
||||||
|
|
||||||
/// Current direction motor is spinning in
|
|
||||||
boolean _direction; // 1 == CW
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @example Random.pde
|
|
||||||
/// Make a single stepper perform random changes in speed, position and acceleration
|
|
||||||
|
|
||||||
/// @example Overshoot.pde
|
|
||||||
/// Check overshoot handling
|
|
||||||
/// which sets a new target position and then waits until the stepper has
|
|
||||||
/// achieved it. This is used for testing the handling of overshoots
|
|
||||||
|
|
||||||
/// @example MultiStepper.pde
|
|
||||||
/// Shows how to multiple simultaneous steppers
|
|
||||||
/// Runs one stepper forwards and backwards, accelerating and decelerating
|
|
||||||
/// at the limits. Runs other steppers at the same time
|
|
||||||
|
|
||||||
/// @example ConstantSpeed.pde
|
|
||||||
/// Shows how to run AccelStepper in the simplest,
|
|
||||||
/// fixed speed mode with no accelerations
|
|
||||||
|
|
||||||
/// @example Blocking.pde
|
|
||||||
/// Shows how to use the blocking call runToNewPosition
|
|
||||||
/// Which sets a new target position and then waits until the stepper has
|
|
||||||
/// achieved it.
|
|
||||||
|
|
||||||
/// @example AFMotor_MultiStepper.pde
|
|
||||||
/// Control both Stepper motors at the same time with different speeds
|
|
||||||
/// and accelerations.
|
|
||||||
|
|
||||||
/// @example AFMotor_ConstantSpeed.pde
|
|
||||||
/// Shows how to run AccelStepper in the simplest,
|
|
||||||
/// fixed speed mode with no accelerations
|
|
||||||
|
|
||||||
/// @example ProportionalControl.pde
|
|
||||||
/// Make a single stepper follow the analog value read from a pot or whatever
|
|
||||||
/// The stepper will move at a constant speed to each newly set posiiton,
|
|
||||||
/// depending on the value of the pot.
|
|
||||||
|
|
||||||
/// @example Bounce.pde
|
|
||||||
/// Make a single stepper bounce from one limit to another, observing
|
|
||||||
/// accelrations at each end of travel
|
|
||||||
|
|
||||||
/// @example Quickstop.pde
|
|
||||||
/// Check stop handling.
|
|
||||||
/// Calls stop() while the stepper is travelling at full speed, causing
|
|
||||||
/// the stepper to stop as quickly as possible, within the constraints of the
|
|
||||||
/// current acceleration.
|
|
||||||
|
|
||||||
/// @example MotorShield.pde
|
|
||||||
/// Shows how to use AccelStepper to control a 3-phase motor, such as a HDD spindle motor
|
|
||||||
/// using the Adafruit Motor Shield http://www.ladyada.net/make/mshield/index.html.
|
|
||||||
|
|
||||||
/// @example DualMotorShield.pde
|
|
||||||
/// Shows how to use AccelStepper to control 2 x 2 phase steppers using the
|
|
||||||
/// Itead Studio Arduino Dual Stepper Motor Driver Shield
|
|
||||||
/// model IM120417015
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -9,10 +9,13 @@
|
|||||||
; https://docs.platformio.org/page/projectconf.html
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
[env:uno]
|
[env:uno]
|
||||||
platform = atmelavr
|
platform = platformio/espressif32
|
||||||
board = uno
|
board = esp32dev
|
||||||
framework = arduino
|
framework = arduino
|
||||||
monitor_speed = 9600
|
monitor_speed = 115200
|
||||||
upload_speed = 115200
|
upload_speed = 576000
|
||||||
upload_port= COM3
|
upload_port = /dev/ttyUSB*
|
||||||
lib_deps = arduino-libraries/Servo@^1.3.0
|
lib_deps =
|
||||||
|
madhephaestus/ESP32Servo@^3.0.6
|
||||||
|
bblanchon/ArduinoJson@^6.21.5
|
||||||
|
gin66/FastAccelStepper@^0.33.13
|
||||||
|
|||||||
413
src/Config_Web.cpp
Normal file
413
src/Config_Web.cpp
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
#include "EggDuino.h"
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include "credentials.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const char *kConfigPath = "/config.json";
|
||||||
|
|
||||||
|
WebServer server(80);
|
||||||
|
bool configStoreReady = false;
|
||||||
|
|
||||||
|
ConfigParameter *findParameter(const String &key)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < configParameterCount; ++i)
|
||||||
|
{
|
||||||
|
if (key.equals(configParameters[i].key))
|
||||||
|
{
|
||||||
|
return &configParameters[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyDefaults()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < configParameterCount; ++i)
|
||||||
|
{
|
||||||
|
*configParameters[i].value = configParameters[i].defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleRoot()
|
||||||
|
{
|
||||||
|
static const char kPage[] PROGMEM = R"HTML(
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>EggDuino Konfiguration</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: "Segoe UI", sans-serif; margin: 20px; background: #f3f6fb; color: #1a1a1a; }
|
||||||
|
main { max-width: 760px; margin: 0 auto; background: #fff; border-radius: 12px; padding: 20px; box-shadow: 0 8px 24px rgba(0,0,0,0.08); }
|
||||||
|
h1 { margin-top: 0; font-size: 1.35rem; }
|
||||||
|
label { display: block; margin: 14px 0 6px; font-weight: 600; }
|
||||||
|
input[type='number'] { width: 100%; padding: 10px; border: 1px solid #c7d2e5; border-radius: 8px; box-sizing: border-box; }
|
||||||
|
button { margin-top: 18px; border: 0; background: #0b5ed7; color: white; padding: 10px 14px; border-radius: 8px; cursor: pointer; }
|
||||||
|
#status { margin-top: 12px; min-height: 1.2em; }
|
||||||
|
#log { margin-top: 20px; border: 1px solid #d6dfef; border-radius: 8px; background: #0f172a; color: #d2e3ff; padding: 10px; height: 220px; overflow-y: auto; white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.9rem; }
|
||||||
|
#logTitle { margin-top: 24px; margin-bottom: 8px; font-weight: 700; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<h1>EggDuino Parameter</h1>
|
||||||
|
<form id="cfgForm"></form>
|
||||||
|
<button id="saveBtn" type="button">Speichern</button>
|
||||||
|
<div id="status"></div>
|
||||||
|
<div id="logTitle">Logs</div>
|
||||||
|
<div id="log"></div>
|
||||||
|
</main>
|
||||||
|
<script>
|
||||||
|
let lastSeq = 0;
|
||||||
|
|
||||||
|
async function loadConfig() {
|
||||||
|
const resp = await fetch('/api/config', { cache: 'no-store' });
|
||||||
|
if (!resp.ok) throw new Error('Konfiguration konnte nicht geladen werden');
|
||||||
|
return resp.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderForm(config) {
|
||||||
|
const form = document.getElementById('cfgForm');
|
||||||
|
form.innerHTML = '';
|
||||||
|
(config.parameters || []).forEach(p => {
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.textContent = p.description || p.key;
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'number';
|
||||||
|
input.value = p.value;
|
||||||
|
input.dataset.key = p.key;
|
||||||
|
form.appendChild(label);
|
||||||
|
form.appendChild(input);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveConfig() {
|
||||||
|
const status = document.getElementById('status');
|
||||||
|
const inputs = [...document.querySelectorAll('input[data-key]')];
|
||||||
|
const payload = {
|
||||||
|
parameters: inputs.map(i => ({ key: i.dataset.key, value: Number(i.value) }))
|
||||||
|
};
|
||||||
|
const resp = await fetch('/api/config', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
if (!resp.ok) {
|
||||||
|
const text = await resp.text();
|
||||||
|
throw new Error(text || 'Speichern fehlgeschlagen');
|
||||||
|
}
|
||||||
|
const saved = await resp.json();
|
||||||
|
renderForm(saved);
|
||||||
|
status.textContent = 'Gespeichert';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollLogs() {
|
||||||
|
const box = document.getElementById('log');
|
||||||
|
const resp = await fetch('/api/logs?since=' + lastSeq);
|
||||||
|
if (!resp.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const payload = await resp.json();
|
||||||
|
(payload.logs || []).forEach(entry => {
|
||||||
|
box.textContent += entry.text + '\n';
|
||||||
|
});
|
||||||
|
if (typeof payload.lastSeq === 'number') {
|
||||||
|
lastSeq = payload.lastSeq;
|
||||||
|
}
|
||||||
|
box.scrollTop = box.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
(async function init() {
|
||||||
|
const status = document.getElementById('status');
|
||||||
|
try {
|
||||||
|
const cfg = await loadConfig();
|
||||||
|
renderForm(cfg);
|
||||||
|
status.textContent = 'Bereit';
|
||||||
|
} catch (e) {
|
||||||
|
status.textContent = e.message;
|
||||||
|
}
|
||||||
|
document.getElementById('saveBtn').addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
await saveConfig();
|
||||||
|
} catch (e) {
|
||||||
|
status.textContent = e.message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pollLogs();
|
||||||
|
setInterval(pollLogs, 800);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)HTML";
|
||||||
|
|
||||||
|
server.send(200, "text/html", kPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleGetConfig()
|
||||||
|
{
|
||||||
|
if (!configStoreReady && !initConfigStore())
|
||||||
|
{
|
||||||
|
server.send(500, "text/plain", "Config storage not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
server.sendHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
||||||
|
server.sendHeader("Pragma", "no-cache");
|
||||||
|
server.sendHeader("Expires", "0");
|
||||||
|
server.send(200, "application/json", buildConfigJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
void handlePostConfig()
|
||||||
|
{
|
||||||
|
if (!configStoreReady && !initConfigStore())
|
||||||
|
{
|
||||||
|
server.send(500, "text/plain", "Config storage not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.hasArg("plain"))
|
||||||
|
{
|
||||||
|
server.send(400, "text/plain", "Missing JSON body");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String error;
|
||||||
|
if (!applyConfigJson(server.arg("plain"), error))
|
||||||
|
{
|
||||||
|
Log(String("Config JSON fehlerhaft: ") + error);
|
||||||
|
server.send(400, "text/plain", error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!saveConfigToFile())
|
||||||
|
{
|
||||||
|
Log("Config konnte nicht gespeichert werden");
|
||||||
|
server.send(500, "text/plain", "Could not save config");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(String("Config gespeichert: penUpPos=") + g_iPenUpPos + ", penDownPos=" + g_iPenDownPos);
|
||||||
|
server.send(200, "application/json", buildConfigJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleGetLogs()
|
||||||
|
{
|
||||||
|
uint32_t since = 0;
|
||||||
|
if (server.hasArg("since"))
|
||||||
|
{
|
||||||
|
since = static_cast<uint32_t>(server.arg("since").toInt());
|
||||||
|
}
|
||||||
|
server.send(200, "application/json", buildLogsJson(since));
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleNotFound()
|
||||||
|
{
|
||||||
|
if (server.uri().startsWith("/api/"))
|
||||||
|
{
|
||||||
|
server.send(404, "text/plain", "API endpoint not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleRoot();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ConfigParameter configParameters[] = {
|
||||||
|
// {"penUpPos", &g_iPEN_UP_POS, "Pen Up Position", 5},
|
||||||
|
// {"penDownPos", &g_iPEN_DOWN_POS, "Pen Down Position", 20},
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t configParameterCount = sizeof(configParameters) / sizeof(configParameters[0]);
|
||||||
|
|
||||||
|
bool initConfigStore()
|
||||||
|
{
|
||||||
|
if (!SPIFFS.begin(true))
|
||||||
|
{
|
||||||
|
configStoreReady = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
configStoreReady = loadConfigFromFile();
|
||||||
|
return configStoreReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loadConfigFromFile()
|
||||||
|
{
|
||||||
|
applyDefaults();
|
||||||
|
|
||||||
|
File file = SPIFFS.open(kConfigPath, "r");
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
Log("config.json fehlt, defaults werden gespeichert");
|
||||||
|
return saveConfigToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticJsonDocument<1024> doc;
|
||||||
|
DeserializationError err = deserializeJson(doc, file);
|
||||||
|
file.close();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
Log("config.json ist ungueltig, defaults werden gespeichert");
|
||||||
|
return saveConfigToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray params = doc["parameters"].as<JsonArray>();
|
||||||
|
for (JsonObject item : params)
|
||||||
|
{
|
||||||
|
const char *key = item["key"];
|
||||||
|
if (key == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ConfigParameter *param = findParameter(String(key));
|
||||||
|
if (param == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (item.containsKey("value"))
|
||||||
|
{
|
||||||
|
*param->value = item["value"].as<int>();
|
||||||
|
}
|
||||||
|
if (item.containsKey("description"))
|
||||||
|
{
|
||||||
|
param->description = item["description"].as<String>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log(String("Config geladen: penUpPos=") + g_iPenUpPos + ", penDownPos=" + g_iPenDownPos);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool saveConfigToFile()
|
||||||
|
{
|
||||||
|
File file = SPIFFS.open(kConfigPath, "w");
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
Log("SPIFFS open write failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticJsonDocument<1024> doc;
|
||||||
|
JsonArray params = doc.createNestedArray("parameters");
|
||||||
|
for (size_t i = 0; i < configParameterCount; ++i)
|
||||||
|
{
|
||||||
|
JsonObject item = params.createNestedObject();
|
||||||
|
item["key"] = configParameters[i].key;
|
||||||
|
item["value"] = *configParameters[i].value;
|
||||||
|
item["description"] = configParameters[i].description;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = serializeJsonPretty(doc, file) > 0;
|
||||||
|
file.flush();
|
||||||
|
file.close();
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Log("serializeJsonPretty failed");
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
String buildConfigJson()
|
||||||
|
{
|
||||||
|
StaticJsonDocument<1024> doc;
|
||||||
|
JsonArray params = doc.createNestedArray("parameters");
|
||||||
|
for (size_t i = 0; i < configParameterCount; ++i)
|
||||||
|
{
|
||||||
|
JsonObject item = params.createNestedObject();
|
||||||
|
item["key"] = configParameters[i].key;
|
||||||
|
item["value"] = *configParameters[i].value;
|
||||||
|
item["description"] = configParameters[i].description;
|
||||||
|
}
|
||||||
|
|
||||||
|
String output;
|
||||||
|
serializeJson(doc, output);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool applyConfigJson(const String &payload, String &errorMessage)
|
||||||
|
{
|
||||||
|
StaticJsonDocument<1024> doc;
|
||||||
|
DeserializationError err = deserializeJson(doc, payload);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
errorMessage = "Invalid JSON payload";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray params = doc["parameters"].as<JsonArray>();
|
||||||
|
if (params.isNull())
|
||||||
|
{
|
||||||
|
errorMessage = "JSON must contain 'parameters' array";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JsonObject item : params)
|
||||||
|
{
|
||||||
|
const char *key = item["key"];
|
||||||
|
if (key == nullptr || !item.containsKey("value"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigParameter *param = findParameter(String(key));
|
||||||
|
if (param == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*param->value = item["value"].as<int>();
|
||||||
|
if (item.containsKey("description"))
|
||||||
|
{
|
||||||
|
param->description = item["description"].as<String>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startWebInterface()
|
||||||
|
{
|
||||||
|
initConfigStore();
|
||||||
|
if (kWifiSsid != 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(kWifiSsid, kWifiPassword);
|
||||||
|
|
||||||
|
const unsigned long connectStart = millis();
|
||||||
|
const unsigned long connectTimeoutMs = 10000;
|
||||||
|
while (WiFi.status() != WL_CONNECTED && millis() - connectStart < connectTimeoutMs)
|
||||||
|
{
|
||||||
|
delay(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED)
|
||||||
|
{
|
||||||
|
Serial.println(String("http://") + WiFi.localIP().toString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println("WLAN Verbindung fehlgeschlagen");
|
||||||
|
}
|
||||||
|
|
||||||
|
server.on("/", HTTP_GET, handleRoot);
|
||||||
|
server.on("/api/config", HTTP_GET, handleGetConfig);
|
||||||
|
server.on("/api/config", HTTP_POST, handlePostConfig);
|
||||||
|
server.on("/api/logs", HTTP_GET, handleGetLogs);
|
||||||
|
server.onNotFound(handleNotFound);
|
||||||
|
server.begin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println("Verwende kein WLAN.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleWebInterface()
|
||||||
|
{
|
||||||
|
if (kWifiSsid != NULL)
|
||||||
|
{
|
||||||
|
server.handleClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,84 +1,100 @@
|
|||||||
#include "EggDuino.h"
|
#include "EggDuino.h"
|
||||||
|
|
||||||
|
void queryPen()
|
||||||
void queryPen() {
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
char state;
|
char state;
|
||||||
if (penState==penUpPos)
|
if (g_iPenState == g_iPenUpPos)
|
||||||
state='1';
|
state = '1';
|
||||||
else
|
else
|
||||||
state='0';
|
state = '0';
|
||||||
Serial.print(String(state)+"\r\n");
|
Serial.print(String(state) + "\r\n");
|
||||||
sendAck();
|
sendAck();
|
||||||
}
|
}
|
||||||
|
|
||||||
void queryButton() {
|
void queryButton()
|
||||||
Serial.print(String(prgButtonState) +"\r\n");
|
{
|
||||||
|
Serial.print(String(g_bPrgButtonState) + "\r\n");
|
||||||
sendAck();
|
sendAck();
|
||||||
prgButtonState = 0;
|
g_bPrgButtonState = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void queryLayer() {
|
void queryLayer()
|
||||||
Serial.print(String(layer) +"\r\n");
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
|
Serial.print(String(g_uiLayer) + "\r\n");
|
||||||
sendAck();
|
sendAck();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLayer() {
|
void setLayer()
|
||||||
uint32_t value=0;
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
|
uint32_t value = 0;
|
||||||
char *arg1;
|
char *arg1;
|
||||||
arg1 = SCmd.next();
|
arg1 = SCmd.next();
|
||||||
if (arg1 != NULL) {
|
if (arg1 != NULL)
|
||||||
|
{
|
||||||
value = atoi(arg1);
|
value = atoi(arg1);
|
||||||
layer=value;
|
g_uiLayer = value;
|
||||||
sendAck();
|
sendAck();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sendError();
|
sendError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void queryNodeCount() {
|
void queryNodeCount()
|
||||||
Serial.print(String(nodeCount) +"\r\n");
|
{
|
||||||
|
Serial.print(String(g_uiNodeCount) + "\r\n");
|
||||||
sendAck();
|
sendAck();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setNodeCount() {
|
void setNodeCount()
|
||||||
uint32_t value=0;
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
|
uint32_t value = 0;
|
||||||
char *arg1;
|
char *arg1;
|
||||||
arg1 = SCmd.next();
|
arg1 = SCmd.next();
|
||||||
if (arg1 != NULL) {
|
if (arg1 != NULL)
|
||||||
|
{
|
||||||
value = atoi(arg1);
|
value = atoi(arg1);
|
||||||
nodeCount=value;
|
g_uiNodeCount = value;
|
||||||
sendAck();
|
sendAck();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sendError();
|
sendError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void nodeCountIncrement() {
|
void nodeCountIncrement()
|
||||||
nodeCount=nodeCount++;
|
{
|
||||||
|
g_uiNodeCount = g_uiNodeCount++;
|
||||||
sendAck();
|
sendAck();
|
||||||
}
|
}
|
||||||
|
|
||||||
void nodeCountDecrement() {
|
void nodeCountDecrement()
|
||||||
nodeCount=nodeCount--;
|
{
|
||||||
|
g_uiNodeCount = g_uiNodeCount--;
|
||||||
sendAck();
|
sendAck();
|
||||||
}
|
}
|
||||||
|
|
||||||
void stepperMove() {
|
void stepperMove()
|
||||||
uint16_t duration=0; //in ms
|
{
|
||||||
int penStepsEBB=0; //Pen
|
Log(__FUNCTION__);
|
||||||
int rotStepsEBB=0; //Rot
|
uint16_t duration = 0; // in ms
|
||||||
|
int penStepsEBB = 0; // Pen
|
||||||
|
int rotStepsEBB = 0; // Rot
|
||||||
|
|
||||||
moveToDestination();
|
moveToDestination();
|
||||||
|
|
||||||
if (!parseSMArgs(&duration, &penStepsEBB, &rotStepsEBB)) {
|
if (!parseSMArgs(&duration, &penStepsEBB, &rotStepsEBB))
|
||||||
|
{
|
||||||
sendError();
|
sendError();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendAck();
|
sendAck();
|
||||||
|
|
||||||
if ( (penStepsEBB==0) && (rotStepsEBB==0) ) {
|
if ((penStepsEBB == 0) && (rotStepsEBB == 0))
|
||||||
|
{
|
||||||
delay(duration);
|
delay(duration);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -86,48 +102,55 @@ void stepperMove() {
|
|||||||
prepareMove(duration, penStepsEBB, rotStepsEBB);
|
prepareMove(duration, penStepsEBB, rotStepsEBB);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPen(){
|
void setPen()
|
||||||
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
int cmd;
|
int cmd;
|
||||||
int value;
|
int value;
|
||||||
char *arg;
|
char *arg;
|
||||||
|
char cstrMsg[20];
|
||||||
|
|
||||||
moveToDestination();
|
moveToDestination();
|
||||||
|
|
||||||
arg = SCmd.next();
|
arg = SCmd.next();
|
||||||
if (arg != NULL) {
|
if (arg != NULL)
|
||||||
|
{
|
||||||
cmd = atoi(arg);
|
cmd = atoi(arg);
|
||||||
switch (cmd) {
|
switch (cmd)
|
||||||
case 0:
|
{
|
||||||
penServo.write(penUpPos);
|
case 0:
|
||||||
penState=penUpPos;
|
penServo.write(g_iPenUpPos);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
penServo.write(penDownPos);
|
penServo.write(g_iPenDownPos);
|
||||||
penState=penDownPos;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
sendError();
|
sendError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
char *val;
|
char *val;
|
||||||
val = SCmd.next();
|
val = SCmd.next();
|
||||||
if (val != NULL) {
|
if (val != NULL)
|
||||||
|
{
|
||||||
value = atoi(val);
|
value = atoi(val);
|
||||||
sendAck();
|
sendAck();
|
||||||
delay(value);
|
delay(value);
|
||||||
}
|
}
|
||||||
if (val==NULL && arg !=NULL) {
|
if (val == NULL && arg != NULL)
|
||||||
|
{
|
||||||
sendAck();
|
sendAck();
|
||||||
delay(500);
|
delay(500);
|
||||||
}
|
}
|
||||||
// Serial.println("delay");
|
// Serial.println("delay");
|
||||||
if (val==NULL && arg ==NULL)
|
if (val == NULL && arg == NULL)
|
||||||
sendError();
|
sendError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void togglePen(){
|
void togglePen()
|
||||||
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
int value;
|
int value;
|
||||||
char *arg;
|
char *arg;
|
||||||
|
|
||||||
@@ -144,17 +167,24 @@ void togglePen(){
|
|||||||
delay(value);
|
delay(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void doTogglePen() {
|
void doTogglePen()
|
||||||
if (penState==penUpPos) {
|
{
|
||||||
penServo.write(penDownPos);
|
Log(__FUNCTION__);
|
||||||
penState=penDownPos;
|
if (g_iPenState == g_iPenUpPos)
|
||||||
} else {
|
{
|
||||||
penServo.write(penUpPos);
|
penServo.write(g_iPenDownPos);
|
||||||
penState=penUpPos;
|
g_iPenState = g_iPenDownPos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
penServo.write(g_iPenUpPos);
|
||||||
|
g_iPenState = g_iPenUpPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void enableMotors(){
|
void enableMotors()
|
||||||
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
int cmd;
|
int cmd;
|
||||||
int value;
|
int value;
|
||||||
char *arg;
|
char *arg;
|
||||||
@@ -165,35 +195,45 @@ void enableMotors(){
|
|||||||
val = SCmd.next();
|
val = SCmd.next();
|
||||||
if (val != NULL)
|
if (val != NULL)
|
||||||
value = atoi(val);
|
value = atoi(val);
|
||||||
//values parsed
|
// values parsed
|
||||||
if ((arg != NULL) && (val == NULL)){
|
if ((arg != NULL) && (val == NULL))
|
||||||
switch (cmd) {
|
{
|
||||||
case 0: motorsOff();
|
switch (cmd)
|
||||||
sendAck();
|
{
|
||||||
break;
|
case 0:
|
||||||
case 1: motorsOn();
|
motorsOff();
|
||||||
sendAck();
|
sendAck();
|
||||||
break;
|
break;
|
||||||
default:
|
case 1:
|
||||||
sendError();
|
motorsOn();
|
||||||
|
sendAck();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sendError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//the following implementaion is a little bit cheated, because i did not know, how to implement different values for first and second argument.
|
// the following implementaion is a little bit cheated, because i did not know, how to implement different values for first and second argument.
|
||||||
if ((arg != NULL) && (val != NULL)){
|
if ((arg != NULL) && (val != NULL))
|
||||||
switch (value) {
|
{
|
||||||
case 0: motorsOff();
|
switch (value)
|
||||||
sendAck();
|
{
|
||||||
break;
|
case 0:
|
||||||
case 1: motorsOn();
|
motorsOff();
|
||||||
sendAck();
|
sendAck();
|
||||||
break;
|
break;
|
||||||
default:
|
case 1:
|
||||||
sendError();
|
motorsOn();
|
||||||
|
sendAck();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sendError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stepperModeConfigure(){
|
void stepperModeConfigure()
|
||||||
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
int cmd;
|
int cmd;
|
||||||
int value;
|
int value;
|
||||||
char *arg;
|
char *arg;
|
||||||
@@ -204,63 +244,76 @@ void stepperModeConfigure(){
|
|||||||
val = SCmd.next();
|
val = SCmd.next();
|
||||||
if (val != NULL)
|
if (val != NULL)
|
||||||
value = atoi(val);
|
value = atoi(val);
|
||||||
if ((arg != NULL) && (val != NULL)){
|
if ((arg != NULL) && (val != NULL))
|
||||||
switch (cmd) {
|
{
|
||||||
case 4: penDownPos= (int) ((float) (value-6000)/(float) 133.3); // transformation from EBB to PWM-Servo
|
switch (cmd)
|
||||||
storePenDownPosInEE();
|
{
|
||||||
sendAck();
|
case 4:
|
||||||
break;
|
g_iPenDownPos = (int)((float)(value - 6000) / (float)133.3); // transformation from EBB to PWM-Servo
|
||||||
case 5: penUpPos= (int)((float) (value-6000)/(float) 133.3); // transformation from EBB to PWM-Servo
|
storePenDownPosInEE();
|
||||||
storePenUpPosInEE();
|
sendAck();
|
||||||
sendAck();
|
break;
|
||||||
break;
|
case 5:
|
||||||
case 6: //rotMin=value; ignored
|
g_iPenUpPos = (int)((float)(value - 6000) / (float)133.3); // transformation from EBB to PWM-Servo
|
||||||
sendAck();
|
storePenUpPosInEE();
|
||||||
break;
|
sendAck();
|
||||||
case 7: //rotMax=value; ignored
|
break;
|
||||||
sendAck();
|
case 6: // rotMin=value; ignored
|
||||||
break;
|
sendAck();
|
||||||
case 11: servoRateUp=value;
|
break;
|
||||||
sendAck();
|
case 7: // rotMax=value; ignored
|
||||||
break;
|
sendAck();
|
||||||
case 12: servoRateDown=value;
|
break;
|
||||||
sendAck();
|
case 11:
|
||||||
break;
|
g_iServoRateUp = value;
|
||||||
default:
|
sendAck();
|
||||||
sendError();
|
break;
|
||||||
|
case 12:
|
||||||
|
g_iServoRateDown = value;
|
||||||
|
sendAck();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sendError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendVersion(){
|
void sendVersion()
|
||||||
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
Serial.print(initSting);
|
Serial.print(initSting);
|
||||||
Serial.print("\r\n");
|
Serial.print("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void unrecognized(const char *command){
|
void unrecognized(const char *command)
|
||||||
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
sendError();
|
sendError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ignore(){
|
void ignore()
|
||||||
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
sendAck();
|
sendAck();
|
||||||
}
|
}
|
||||||
|
|
||||||
void makeComInterface(){
|
void makeComInterface()
|
||||||
SCmd.addCommand("v",sendVersion);
|
{
|
||||||
SCmd.addCommand("EM",enableMotors);
|
SCmd.addCommand("v", sendVersion);
|
||||||
SCmd.addCommand("SC",stepperModeConfigure);
|
SCmd.addCommand("EM", enableMotors);
|
||||||
SCmd.addCommand("SP",setPen);
|
SCmd.addCommand("SC", stepperModeConfigure);
|
||||||
SCmd.addCommand("SM",stepperMove);
|
SCmd.addCommand("SP", setPen);
|
||||||
SCmd.addCommand("SE",ignore);
|
SCmd.addCommand("SM", stepperMove);
|
||||||
SCmd.addCommand("TP",togglePen);
|
SCmd.addCommand("SE", ignore);
|
||||||
SCmd.addCommand("PO",ignore); //Engraver command, not implemented, gives fake answer
|
SCmd.addCommand("TP", togglePen);
|
||||||
SCmd.addCommand("NI",nodeCountIncrement);
|
SCmd.addCommand("PO", ignore); // Engraver command, not implemented, gives fake answer
|
||||||
SCmd.addCommand("ND",nodeCountDecrement);
|
SCmd.addCommand("NI", nodeCountIncrement);
|
||||||
SCmd.addCommand("SN",setNodeCount);
|
SCmd.addCommand("ND", nodeCountDecrement);
|
||||||
SCmd.addCommand("QN",queryNodeCount);
|
SCmd.addCommand("SN", setNodeCount);
|
||||||
SCmd.addCommand("SL",setLayer);
|
SCmd.addCommand("QN", queryNodeCount);
|
||||||
SCmd.addCommand("QL",queryLayer);
|
SCmd.addCommand("SL", setLayer);
|
||||||
SCmd.addCommand("QP",queryPen);
|
SCmd.addCommand("QL", queryLayer);
|
||||||
SCmd.addCommand("QB",queryButton); //"PRG" Button,
|
SCmd.addCommand("QP", queryPen);
|
||||||
|
SCmd.addCommand("QB", queryButton); //"PRG" Button,
|
||||||
SCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
|
SCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
|
||||||
}
|
}
|
||||||
@@ -1,81 +1,113 @@
|
|||||||
#include "EggDuino.h"
|
#include "EggDuino.h"
|
||||||
|
|
||||||
inline void loadPenPosFromEE() {
|
void initHardware()
|
||||||
penUpPos = eeprom_read_word(penUpPosEEAddress);
|
{
|
||||||
penDownPos = eeprom_read_word(penDownPosEEAddress);
|
if (!initConfigStore())
|
||||||
penState = penUpPos;
|
{
|
||||||
}
|
g_iPenUpPos = 5;
|
||||||
|
g_iPenDownPos = 20;
|
||||||
|
}
|
||||||
|
g_iPenState = g_iPenUpPos;
|
||||||
|
|
||||||
void initHardware(){
|
g_stepEngine.init();
|
||||||
// enable eeprom wait in avr/eeprom.h functions
|
g_pStepperRotate = g_stepEngine.stepperConnectToPin(step1);
|
||||||
SPMCSR &= ~SELFPRGEN;
|
if (g_pStepperRotate)
|
||||||
|
{
|
||||||
|
// rotMotor.setMaxSpeed(2000.0);
|
||||||
|
// rotMotor.setAcceleration(10000.0);
|
||||||
|
|
||||||
loadPenPosFromEE();
|
g_pStepperRotate->setDirectionPin(dir1);
|
||||||
|
g_pStepperRotate->setEnablePin(enableRotMotor);
|
||||||
|
g_pStepperRotate->setAcceleration(10000);
|
||||||
|
g_pStepperRotate->setAutoEnable(false);
|
||||||
|
}
|
||||||
|
|
||||||
pinMode(enableRotMotor, OUTPUT);
|
// Stepper pen init
|
||||||
pinMode(enablePenMotor, OUTPUT);
|
g_pStepperPen = g_stepEngine.stepperConnectToPin(step2);
|
||||||
|
if (g_pStepperPen)
|
||||||
|
{
|
||||||
|
// penMotor.setMaxSpeed(2000.0);
|
||||||
|
// penMotor.setAcceleration(10000.0);
|
||||||
|
g_pStepperPen->setDirectionPin(dir2);
|
||||||
|
g_pStepperPen->setEnablePin(enablePenMotor);
|
||||||
|
g_pStepperPen->setAcceleration(10000);
|
||||||
|
g_pStepperPen->setAutoEnable(false);
|
||||||
|
}
|
||||||
|
|
||||||
rotMotor.setMaxSpeed(2000.0);
|
|
||||||
rotMotor.setAcceleration(10000.0);
|
|
||||||
penMotor.setMaxSpeed(2000.0);
|
|
||||||
penMotor.setAcceleration(10000.0);
|
|
||||||
motorsOff();
|
motorsOff();
|
||||||
penServo.attach(servoPin);
|
penServo.attach(servoPin);
|
||||||
penServo.write(penState);
|
penServo.write(g_iPenState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void storePenUpPosInEE()
|
||||||
|
{
|
||||||
void storePenUpPosInEE() {
|
saveConfigToFile();
|
||||||
eeprom_update_word(penUpPosEEAddress, penUpPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void storePenDownPosInEE() {
|
void storePenDownPosInEE()
|
||||||
eeprom_update_word(penDownPosEEAddress, penDownPos);
|
{
|
||||||
|
saveConfigToFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendAck(){
|
void sendAck()
|
||||||
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
Serial.print("OK\r\n");
|
Serial.print("OK\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendError(){
|
void sendError()
|
||||||
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
Serial.print("unknown CMD\r\n");
|
Serial.print("unknown CMD\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void motorsOff() {
|
void motorsOff()
|
||||||
digitalWrite(enableRotMotor, HIGH);
|
{
|
||||||
digitalWrite(enablePenMotor, HIGH);
|
Log(__FUNCTION__);
|
||||||
motorsEnabled = 0;
|
g_pStepperPen->disableOutputs();
|
||||||
|
g_pStepperRotate->disableOutputs();
|
||||||
|
g_bMotorsEnabled = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void motorsOn() {
|
void motorsOn()
|
||||||
digitalWrite(enableRotMotor, LOW) ;
|
{
|
||||||
digitalWrite(enablePenMotor, LOW) ;
|
Log(__FUNCTION__);
|
||||||
motorsEnabled = 1;
|
g_pStepperPen->enableOutputs();
|
||||||
|
g_pStepperRotate->enableOutputs();
|
||||||
|
g_bMotorsEnabled = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggleMotors() {
|
void toggleMotors()
|
||||||
if (motorsEnabled) {
|
{
|
||||||
|
Log(__FUNCTION__);
|
||||||
|
if (g_bMotorsEnabled)
|
||||||
|
{
|
||||||
motorsOff();
|
motorsOff();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
motorsOn();
|
motorsOn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parseSMArgs(uint16_t *duration, int *penStepsEBB, int *rotStepsEBB) {
|
bool parseSMArgs(uint16_t *duration, int *penStepsEBB, int *rotStepsEBB)
|
||||||
|
{
|
||||||
char *arg1;
|
char *arg1;
|
||||||
char *arg2;
|
char *arg2;
|
||||||
char *arg3;
|
char *arg3;
|
||||||
arg1 = SCmd.next();
|
arg1 = SCmd.next();
|
||||||
if (arg1 != NULL) {
|
if (arg1 != NULL)
|
||||||
|
{
|
||||||
*duration = atoi(arg1);
|
*duration = atoi(arg1);
|
||||||
arg2 = SCmd.next();
|
arg2 = SCmd.next();
|
||||||
}
|
}
|
||||||
if (arg2 != NULL) {
|
if (arg2 != NULL)
|
||||||
|
{
|
||||||
*penStepsEBB = atoi(arg2);
|
*penStepsEBB = atoi(arg2);
|
||||||
arg3 = SCmd.next();
|
arg3 = SCmd.next();
|
||||||
}
|
}
|
||||||
if (arg3 != NULL) {
|
if (arg3 != NULL)
|
||||||
|
{
|
||||||
*rotStepsEBB = atoi(arg3);
|
*rotStepsEBB = atoi(arg3);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -84,56 +116,62 @@ bool parseSMArgs(uint16_t *duration, int *penStepsEBB, int *rotStepsEBB) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB) {
|
void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB)
|
||||||
if (!motorsEnabled) {
|
{
|
||||||
|
if (!g_bMotorsEnabled)
|
||||||
|
{
|
||||||
motorsOn();
|
motorsOn();
|
||||||
}
|
}
|
||||||
if( (1 == rotStepCorrection) && (1 == penStepCorrection) ){ // if coordinatessystems are identical
|
|
||||||
//set Coordinates and Speed
|
|
||||||
rotMotor.move(rotStepsEBB);
|
|
||||||
rotMotor.setSpeed( abs( (float)rotStepsEBB * (float)1000 / (float)duration ) );
|
|
||||||
penMotor.move(penStepsEBB);
|
|
||||||
penMotor.setSpeed( abs( (float)penStepsEBB * (float)1000 / (float)duration ) );
|
|
||||||
} else {
|
|
||||||
//incoming EBB-Steps will be multiplied by 16, then Integer-maths is done, result will be divided by 16
|
|
||||||
// This make thinks here really complicated, but floating point-math kills performance and memory, believe me... I tried...
|
|
||||||
long rotSteps = ( (long)rotStepsEBB * 16 / rotStepCorrection) + (long)rotStepError; //correct incoming EBB-Steps to our microstep-Setting and multiply by 16 to avoid floatingpoint...
|
|
||||||
long penSteps = ( (long)penStepsEBB * 16 / penStepCorrection) + (long)penStepError;
|
|
||||||
|
|
||||||
int rotStepsToGo = (int) (rotSteps/16); //Calc Steps to go, which are possible on our machine
|
|
||||||
int penStepsToGo = (int) (penSteps/16);
|
|
||||||
|
|
||||||
rotStepError = (long)rotSteps - ((long) rotStepsToGo * (long)16); // calc Position-Error, if there is one
|
if ((1 == fROT_STEP_CORRECTION) && (1 == fPEN_STEP_CORRECTION))
|
||||||
penStepError = (long)penSteps - ((long) penStepsToGo * (long)16);
|
{ // if coordinatessystems are identical
|
||||||
|
// set Coordinates and Speed
|
||||||
|
g_pStepperRotate->setSpeedInTicks(abs((float)rotStepsEBB * (float)1000 / (float)duration));
|
||||||
|
g_pStepperRotate->move(rotStepsEBB);
|
||||||
|
|
||||||
long temp_rotSpeed = ((long)rotStepsToGo * (long)1000 / (long)duration ); // calc Speed in Integer Math
|
g_pStepperPen->setSpeedInTicks(abs((float)penStepsEBB * (float)1000 / (float)duration));
|
||||||
long temp_penSpeed = ((long)penStepsToGo * (long)1000 / (long)duration ) ;
|
g_pStepperPen->move(penStepsEBB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// incoming EBB-Steps will be multiplied by 16, then Integer-maths is done, result will be divided by 16
|
||||||
|
// This make thinks here really complicated, but floating point-math kills performance and memory, believe me... I tried...
|
||||||
|
long rotSteps = ((long)rotStepsEBB * 16 / fROT_STEP_CORRECTION) + (long)g_iRotStepError; // correct incoming EBB-Steps to our microstep-Setting and multiply by 16 to avoid floatingpoint...
|
||||||
|
long penSteps = ((long)penStepsEBB * 16 / fPEN_STEP_CORRECTION) + (long)g_iPenStepError;
|
||||||
|
|
||||||
float rotSpeed= (float) abs(temp_rotSpeed); // type cast
|
int rotStepsToGo = (int)(rotSteps / 16); // Calc Steps to go, which are possible on our machine
|
||||||
float penSpeed= (float) abs(temp_penSpeed);
|
int penStepsToGo = (int)(penSteps / 16);
|
||||||
|
|
||||||
//set Coordinates and Speed
|
g_iRotStepError = (long)rotSteps - ((long)rotStepsToGo * (long)16); // calc Position-Error, if there is one
|
||||||
rotMotor.move(rotStepsToGo); // finally, let us set the target position...
|
g_iPenStepError = (long)penSteps - ((long)penStepsToGo * (long)16);
|
||||||
rotMotor.setSpeed(rotSpeed); // and the Speed!
|
|
||||||
penMotor.move(penStepsToGo);
|
long temp_rotSpeed = ((long)rotStepsToGo * (long)1000 / (long)duration); // calc Speed in Integer Math
|
||||||
penMotor.setSpeed( penSpeed );
|
long temp_penSpeed = ((long)penStepsToGo * (long)1000 / (long)duration);
|
||||||
|
|
||||||
|
float rotSpeed = (float)abs(temp_rotSpeed); // type cast
|
||||||
|
float penSpeed = (float)abs(temp_penSpeed);
|
||||||
|
|
||||||
|
// set Coordinates and Speed
|
||||||
|
g_pStepperRotate->setSpeedInTicks(rotSpeed);
|
||||||
|
g_pStepperRotate->move(rotStepsToGo);
|
||||||
|
|
||||||
|
g_pStepperPen->setSpeedInTicks(penSpeed);
|
||||||
|
g_pStepperPen->move(penStepsToGo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void moveOneStep()
|
||||||
|
{
|
||||||
|
while (g_pStepperPen->isRunning() || g_pStepperRotate->isRunning());
|
||||||
}
|
}
|
||||||
|
|
||||||
void moveOneStep() {
|
void moveToDestination()
|
||||||
if ( penMotor.distanceToGo() || rotMotor.distanceToGo() ) {
|
{
|
||||||
penMotor.runSpeedToPosition(); // Moving.... moving... moving....
|
while (g_pStepperPen->isRunning() || g_pStepperRotate->isRunning());
|
||||||
rotMotor.runSpeedToPosition();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void moveToDestination() {
|
void setprgButtonState()
|
||||||
while ( penMotor.distanceToGo() || rotMotor.distanceToGo() ) {
|
{
|
||||||
penMotor.runSpeedToPosition(); // Moving.... moving... moving....
|
g_bPrgButtonState = 1;
|
||||||
rotMotor.runSpeedToPosition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setprgButtonState(){
|
|
||||||
prgButtonState = 1;
|
|
||||||
}
|
}
|
||||||
|
|||||||
91
src/Logging.cpp
Normal file
91
src/Logging.cpp
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#include "EggDuino.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr size_t kLogCapacity = 80;
|
||||||
|
constexpr size_t kLogLineLength = 160;
|
||||||
|
|
||||||
|
char g_logLines[kLogCapacity][kLogLineLength];
|
||||||
|
uint32_t g_logSeq[kLogCapacity];
|
||||||
|
size_t g_logWritePos = 0;
|
||||||
|
uint32_t g_nextLogSeq = 1;
|
||||||
|
|
||||||
|
void appendJsonEscaped(String &out, const char *text) {
|
||||||
|
out += "\"";
|
||||||
|
for (size_t i = 0; text[i] != '\0'; ++i) {
|
||||||
|
const char c = text[i];
|
||||||
|
switch (c) {
|
||||||
|
case '\\':
|
||||||
|
out += "\\\\";
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
out += "\\\"";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
out += "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
out += "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
out += "\\t";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (static_cast<unsigned char>(c) < 0x20) {
|
||||||
|
out += '?';
|
||||||
|
} else {
|
||||||
|
out += c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out += "\"";
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void Log(const String &message) {
|
||||||
|
snprintf(
|
||||||
|
g_logLines[g_logWritePos],
|
||||||
|
kLogLineLength,
|
||||||
|
"[%010lu] %s",
|
||||||
|
static_cast<unsigned long>(millis()),
|
||||||
|
message.c_str()
|
||||||
|
);
|
||||||
|
g_logSeq[g_logWritePos] = g_nextLogSeq++;
|
||||||
|
g_logWritePos = (g_logWritePos + 1) % kLogCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Log(const char *message) {
|
||||||
|
Log(String(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
String buildLogsJson(uint32_t sinceSeq) {
|
||||||
|
String output;
|
||||||
|
output.reserve(2048);
|
||||||
|
output += "{\"logs\":[";
|
||||||
|
uint32_t lastSeq = sinceSeq;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < kLogCapacity; ++i) {
|
||||||
|
const size_t idx = (g_logWritePos + i) % kLogCapacity;
|
||||||
|
const uint32_t seq = g_logSeq[idx];
|
||||||
|
if (seq == 0 || seq <= sinceSeq) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!first) {
|
||||||
|
output += ",";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
output += "{\"seq\":";
|
||||||
|
output += String(seq);
|
||||||
|
output += ",\"text\":";
|
||||||
|
appendJsonEscaped(output, g_logLines[idx]);
|
||||||
|
output += "}";
|
||||||
|
lastSeq = seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
output += "],\"lastSeq\":";
|
||||||
|
output += String(lastSeq);
|
||||||
|
output += "}";
|
||||||
|
return output;
|
||||||
|
}
|
||||||
123
src/main.cpp
123
src/main.cpp
@@ -29,53 +29,114 @@
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
//make Objects
|
FastAccelStepperEngine g_stepEngine = FastAccelStepperEngine();
|
||||||
AccelStepper rotMotor(AccelStepper::DRIVER, step1, dir1);
|
FastAccelStepper *g_pStepperRotate = NULL;
|
||||||
AccelStepper penMotor(AccelStepper::DRIVER, step2, dir2);
|
FastAccelStepper *g_pStepperPen = NULL;
|
||||||
|
|
||||||
|
// make Objects
|
||||||
Servo penServo;
|
Servo penServo;
|
||||||
SerialCommand SCmd;
|
SerialCommand SCmd;
|
||||||
//create Buttons
|
|
||||||
|
// create Buttons
|
||||||
#ifdef prgButton
|
#ifdef prgButton
|
||||||
Button prgButtonToggle(prgButton, setprgButtonState);
|
Button prgButtonToggle(prgButton, setprgButtonState);
|
||||||
#endif
|
#endif
|
||||||
#ifdef penToggleButton
|
#ifdef penToggleButton
|
||||||
Button penToggle(penToggleButton, doTogglePen);
|
Button penToggle(penToggleButton, doTogglePen);
|
||||||
#endif
|
#endif
|
||||||
#ifdef motorsButton
|
#ifdef motorsButton
|
||||||
Button motorsToggle(motorsButton, toggleMotors);
|
Button motorsToggle(motorsButton, toggleMotors);
|
||||||
#endif
|
#endif
|
||||||
// Variables... be careful, by messing around here, everything has a reason and crossrelations...
|
|
||||||
int penMin=0;
|
|
||||||
int penMax=0;
|
|
||||||
int penUpPos=5; //can be overwritten from EBB-Command SC
|
|
||||||
int penDownPos=20; //can be overwritten from EBB-Command SC
|
|
||||||
int servoRateUp=0; //from EBB-Protocol not implemented on machine-side
|
|
||||||
int servoRateDown=0; //from EBB-Protocol not implemented on machine-side
|
|
||||||
long rotStepError=0;
|
|
||||||
long penStepError=0;
|
|
||||||
int penState=penUpPos;
|
|
||||||
uint32_t nodeCount=0;
|
|
||||||
unsigned int layer=0;
|
|
||||||
boolean prgButtonState=0;
|
|
||||||
uint8_t rotStepCorrection = 16/rotMicrostep ; //devide EBB-Coordinates by this factor to get EGGduino-Steps
|
|
||||||
uint8_t penStepCorrection = 16/penMicrostep ; //devide EBB-Coordinates by this factor to get EGGduino-Steps
|
|
||||||
float rotSpeed=0;
|
|
||||||
float penSpeed=0; // these are local variables for Function SteppermotorMove-Command, but for performance-reasons it will be initialized here
|
|
||||||
boolean motorsEnabled = 0;
|
|
||||||
|
|
||||||
void setup() {
|
// Variables... be careful, by messing around here, everything has a reason and crossrelations...
|
||||||
Serial.begin(9600);
|
int g_iPenUpPos = 5; // can be overwritten from EBB-Command SC
|
||||||
|
int g_iPenDownPos = 20; // can be overwritten from EBB-Command SC
|
||||||
|
int g_iServoRateUp = 0; // from EBB-Protocol not implemented on machine-side
|
||||||
|
int g_iServoRateDown = 0; // from EBB-Protocol not implemented on machine-side
|
||||||
|
long g_iRotStepError = 0;
|
||||||
|
long g_iPenStepError = 0;
|
||||||
|
int g_iPenState = g_iPenUpPos;
|
||||||
|
uint32_t g_uiNodeCount = 0;
|
||||||
|
unsigned int g_uiLayer = 0;
|
||||||
|
boolean g_bPrgButtonState = 0;
|
||||||
|
float fROT_STEP_CORRECTION = 16.0 / rotMicrostep; // devide EBB-Coordinates by this factor to get EGGduino-Steps
|
||||||
|
float fPEN_STEP_CORRECTION = 16.0 / penMicrostep; // devide EBB-Coordinates by this factor to get EGGduino-Steps
|
||||||
|
boolean g_bMotorsEnabled = 0;
|
||||||
|
|
||||||
|
// Stepper Test
|
||||||
|
#ifdef TEST
|
||||||
|
// #define dirPinStepper 16
|
||||||
|
// #define enablePinStepper 12
|
||||||
|
// #define stepPinStepper 26
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("Starting...");
|
||||||
|
Log("Starting...");
|
||||||
makeComInterface();
|
makeComInterface();
|
||||||
initHardware();
|
initHardware();
|
||||||
|
startWebInterface();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
uint8_t g_uiState = 0;
|
||||||
|
unsigned long g_uiLastTim = millis();
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
#ifdef TEST
|
||||||
|
|
||||||
moveOneStep();
|
unsigned long uiNow = millis();
|
||||||
|
motorsOn();
|
||||||
|
|
||||||
|
if (uiNow - g_uiLastTim > 5000)
|
||||||
|
{
|
||||||
|
g_uiLastTim = uiNow;
|
||||||
|
switch (g_uiState)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
Log(String(g_uiState));
|
||||||
|
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
|
||||||
|
g_pStepperRotate->setAcceleration(10000);
|
||||||
|
g_pStepperRotate->move(1000);
|
||||||
|
g_uiState++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
Log(String(g_uiState));
|
||||||
|
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
|
||||||
|
g_pStepperRotate->setAcceleration(10000);
|
||||||
|
g_pStepperRotate->move(-1000);
|
||||||
|
g_uiState++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
Log(String(g_uiState));
|
||||||
|
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
|
||||||
|
g_pStepperPen->setAcceleration(10000);
|
||||||
|
g_pStepperPen->move(1000);
|
||||||
|
g_uiState++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
Log(String(g_uiState));
|
||||||
|
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
|
||||||
|
g_pStepperPen->setAcceleration(10000);
|
||||||
|
g_pStepperPen->move(-1000);
|
||||||
|
g_uiState = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Log("Alive");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// moveOneStep();
|
||||||
SCmd.readSerial();
|
SCmd.readSerial();
|
||||||
|
handleWebInterface();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef penToggleButton
|
#ifdef penToggleButton
|
||||||
penToggle.check();
|
penToggle.check();
|
||||||
|
|||||||
Reference in New Issue
Block a user