1
0
mirror of https://github.com/bdring/Grbl_Esp32.git synced 2025-08-30 17:49:56 +02:00

Merge Dxl test (#596)

* Changes, not ready yet

* Some updates for M67

* WIP

* Updates

* Code cleanup and speed tweaks

* Prep for PR

* Cleaned up user I/O to use UNDEFINED_PIN as defaults.

* Cleaned up spurious code.
This commit is contained in:
bdring
2020-09-14 20:23:56 -05:00
committed by GitHub
parent 50bae92bfb
commit adec46c753
23 changed files with 551 additions and 96 deletions

View File

@@ -108,7 +108,7 @@ bool kinematics_pre_homing(uint8_t cycle_mask))
void kinematics_post_homing() {}
#endif
#ifdef USE_FWD_KINEMATIC
#ifdef USE_FWD_KINEMATICS
/*
The status command uses forward_kinematics() to convert
your motor positions to cartesian X,Y,Z... coordinates.

View File

@@ -134,7 +134,7 @@ bool kinematics_pre_homing(uint8_t cycle_mask))
void kinematics_post_homing() {}
#endif
#ifdef USE_FWD_KINEMATIC
#ifdef USE_FWD_KINEMATICS
/*
The status command uses forward_kinematics() to convert
your motor positions to cartesian X,Y,Z... coordinates.

View File

@@ -0,0 +1,312 @@
/*
parallel_delta.cpp -
Copyright (c) 2019 Barton Dring @buildlog,
Jason Huggins, Tapster Robotics
Kinematics for a parallel delta robot.
==================== How it Works ====================================
On a delta machine, Grbl axis units are in radians
The kinematics converts the cartesian moves in gcode into
the radians to move the arms. The Grbl motion planner never sees
the actual cartesian values.
To make the moves straight and smooth on a delta, the cartesian moves
are broken into small segments where the non linearity will not be noticed.
This is similar to how Grgl draws arcs.
If you request MPos status it will tell you the position in
arm angles. The MPos will report in cartesian values using forward kinematics.
The arm 0 values (angle) are the arms at horizontal.
Positive angles are below horizontal.
The machine's Z zero point in the kinematics is parallel to the arm axes.
Delta_z_offset is the offset to the end effector joints at arm angle zero.
The is calculated at startup and used in the forward kinematics
Feedrate in gcode is in the cartesian uints. This must be converted to the
angles. This is done by calculating the segment move distance and the angle
move distance and applying that ration to the feedrate.
TODO Cleanup
Update so extra axes get delt with ... passed through properly
============================================================================
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
FYI: http://forums.trossenrobotics.com/tutorials/introduction-129/delta-robot-kinematics-3276/
Better: http://hypertriangle.com/~alex/delta-robot-tutorial/
*/
enum class KinematicError : uint8_t {
NONE = 0,
OUT_OF_RANGE = 1,
ANGLE_TOO_NEGATIVE = 2,
};
// trigonometric constants to speed calculations
const float sqrt3 = 1.732050807;
const float dtr = M_PI / (float)180.0; // degrees to radians
const float sin120 = sqrt3 / 2.0;
const float cos120 = -0.5;
const float tan60 = sqrt3;
const float sin30 = 0.5;
const float tan30 = 1.0 / sqrt3;
// the geometry of the delta
const float rf = RADIUS_FIXED; // radius of the fixed side (length of motor cranks)
const float re = RADIUS_EFF; // radius of end effector side (length of linkages)
const float f = LENGTH_FIXED_SIDE; // sized of fixed side triangel
const float e = LENGTH_EFF_SIDE; // size of end effector side triangle
static float last_angle[N_AXIS] = { 0.0, 0.0, 0.0 }; // A place to save the previous motor angles for distance/feed rate calcs
float delta_z_offset; // Z offset of the effector from the arm centers
// prototypes for helper functions
int calc_forward_kinematics(float* angles, float* cartesian);
KinematicError delta_calcInverse(float* cartesian, float* angles);
KinematicError delta_calcAngleYZ(float x0, float y0, float z0, float& theta);
float three_axis_dist(float* point1, float* point2);
void machine_init() {
// Calculate the Z offset at the motor zero angles ...
// Z offset is the z distance from the motor axes to the end effector axes at zero angle
float angles[N_AXIS] = { 0.0, 0.0, 0.0 };
float cartesian[N_AXIS] = { 0.0, 0.0, 0.0 };
calc_forward_kinematics(angles, cartesian); // Sets the cartesian values
// print a startup message to show the kinematics are enables
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Delata Kinematics Init: %s Z Offset:%4.3f", MACHINE_NAME, cartesian[Z_AXIS]);
}
bool user_defined_homing() { // true = do not continue with normal Grbl homing
return true;
}
void inverse_kinematics(float* target, plan_line_data_t* pl_data, float* position) //The target and position are provided in MPos
{
float dx, dy, dz; // distances in each cartesian axis
float motor_angles[N_AXIS];
float seg_target[N_AXIS]; // The target of the current segment
float feed_rate = pl_data->feed_rate; // save original feed rate
bool start_position_erorr = false;
bool show_error = true; // shows error once
// see if start is in work area...if not skip segments and try to go to target
KinematicError status = delta_calcInverse(position, motor_angles);
if (status == KinematicError::OUT_OF_RANGE) {
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Start position error");
start_position_erorr = true;
}
// Check the destination to see if it is in work area
status = delta_calcInverse(target, motor_angles);
if (status == KinematicError::OUT_OF_RANGE) {
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Target unreachable");
return;
}
position[X_AXIS] += gc_state.coord_offset[X_AXIS];
position[Y_AXIS] += gc_state.coord_offset[Y_AXIS];
position[Z_AXIS] += gc_state.coord_offset[Z_AXIS];
// calculate cartesian move distance for each axis
dx = target[X_AXIS] - position[X_AXIS];
dy = target[Y_AXIS] - position[Y_AXIS];
dz = target[Z_AXIS] - position[Z_AXIS];
float dist = sqrt((dx * dx) + (dy * dy) + (dz * dz));
// determine the number of segments we need ... round up so there is at least 1 (except when dist is 0)
uint32_t segment_count = ceil(dist / SEGMENT_LENGTH);
float segment_dist = dist / ((float)segment_count); // distance of each segment...will be used for feedrate conversion
for (uint32_t segment = 1; segment <= segment_count; segment++) {
// determine this segment's target
seg_target[X_AXIS] = position[X_AXIS] + (dx / float(segment_count) * segment);
seg_target[Y_AXIS] = position[Y_AXIS] + (dy / float(segment_count) * segment);
seg_target[Z_AXIS] = position[Z_AXIS] + (dz / float(segment_count) * segment);
// calculate the delta motor angles
KinematicError status = delta_calcInverse(seg_target, motor_angles);
if (status == KinematicError ::NONE) {
float delta_distance = three_axis_dist(motor_angles, last_angle);
// save angles for next distance calc
memcpy(last_angle, motor_angles, sizeof(motor_angles));
if (pl_data->motion.rapidMotion) {
pl_data->feed_rate = feed_rate;
} else {
pl_data->feed_rate = (feed_rate * delta_distance / segment_dist);
}
mc_line(motor_angles, pl_data);
} else {
if (show_error) {
grbl_msg_sendf(CLIENT_SERIAL,
MsgLevel::Info,
"Error:%d, Angs X:%4.3f Y:%4.3f Z:%4.3f]\r\n\r\n",
status,
motor_angles[0],
motor_angles[1],
motor_angles[2]);
show_error = false;
}
}
}
}
// inverse kinematics: cartesian -> angles
// returned status: 0=OK, -1=non-existing position
KinematicError delta_calcInverse(float* cartesian, float* angles) {
angles[0] = angles[1] = angles[2] = 0;
KinematicError status = KinematicError::NONE;
status = delta_calcAngleYZ(cartesian[X_AXIS], cartesian[Y_AXIS], cartesian[Z_AXIS], angles[0]);
if (status != KinematicError ::NONE) {
return status;
}
status = delta_calcAngleYZ(cartesian[X_AXIS] * cos120 + cartesian[Y_AXIS] * sin120,
cartesian[Y_AXIS] * cos120 - cartesian[X_AXIS] * sin120,
cartesian[Z_AXIS],
angles[1]); // rotate coords to +120 deg
if (status != KinematicError ::NONE) {
return status;
}
status = delta_calcAngleYZ(cartesian[X_AXIS] * cos120 - cartesian[Y_AXIS] * sin120,
cartesian[Y_AXIS] * cos120 + cartesian[X_AXIS] * sin120,
cartesian[Z_AXIS],
angles[2]); // rotate coords to -120 deg
if (status != KinematicError ::NONE) {
return status;
}
//grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "xyx (%4.3f,%4.3f,%4.3f) ang (%4.3f,%4.3f,%4.3f)", x0, y0, z0, theta1, theta2, theta3);
return status;
}
// inverse kinematics: angles -> cartesian
int calc_forward_kinematics(float* angles, float* catesian) {
float t = (f - e) * tan30 / 2;
float y1 = -(t + rf * cos(angles[0]));
float z1 = -rf * sin(angles[0]);
float y2 = (t + rf * cos(angles[1])) * sin30;
float x2 = y2 * tan60;
float z2 = -rf * sin(angles[1]);
float y3 = (t + rf * cos(angles[2])) * sin30;
float x3 = -y3 * tan60;
float z3 = -rf * sin(angles[2]);
float dnm = (y2 - y1) * x3 - (y3 - y1) * x2;
float w1 = y1 * y1 + z1 * z1;
float w2 = x2 * x2 + y2 * y2 + z2 * z2;
float w3 = x3 * x3 + y3 * y3 + z3 * z3;
// x = (a1*z + b1)/dnm
float a1 = (z2 - z1) * (y3 - y1) - (z3 - z1) * (y2 - y1);
float b1 = -((w2 - w1) * (y3 - y1) - (w3 - w1) * (y2 - y1)) / 2.0;
// y = (a2*z + b2)/dnm;
float a2 = -(z2 - z1) * x3 + (z3 - z1) * x2;
float b2 = ((w2 - w1) * x3 - (w3 - w1) * x2) / 2.0;
// a*z^2 + b*z + c = 0
float a = a1 * a1 + a2 * a2 + dnm * dnm;
float b = 2 * (a1 * b1 + a2 * (b2 - y1 * dnm) - z1 * dnm * dnm);
float c = (b2 - y1 * dnm) * (b2 - y1 * dnm) + b1 * b1 + dnm * dnm * (z1 * z1 - re * re);
// discriminant
float d = b * b - (float)4.0 * a * c;
if (d < 0)
return -1; // non-existing point
catesian[Z_AXIS] = -(float)0.5 * (b + sqrt(d)) / a;
catesian[X_AXIS] = (a1 * catesian[Z_AXIS] + b1) / dnm;
catesian[Y_AXIS] = (a2 * catesian[Z_AXIS] + b2) / dnm;
return 0;
}
// helper functions, calculates angle theta1 (for YZ-pane)
KinematicError delta_calcAngleYZ(float x0, float y0, float z0, float& theta) {
float y1 = -0.5 * 0.57735 * f; // f/2 * tg 30
y0 -= 0.5 * 0.57735 * e; // shift center to edge
// z = a + b*y
float a = (x0 * x0 + y0 * y0 + z0 * z0 + rf * rf - re * re - y1 * y1) / (2 * z0);
float b = (y1 - y0) / z0;
// discriminant
float d = -(a + b * y1) * (a + b * y1) + rf * (b * b * rf + rf);
if (d < 0)
return KinematicError::OUT_OF_RANGE; // non-existing point
float yj = (y1 - a * b - sqrt(d)) / (b * b + 1); // choosing outer point
float zj = a + b * yj;
//theta = 180.0 * atan(-zj / (y1 - yj)) / M_PI + ((yj > y1) ? 180.0 : 0.0);
theta = atan(-zj / (y1 - yj)) + ((yj > y1) ? M_PI : 0.0);
if (theta < MAX_NEGATIVE_ANGLE) {
return KinematicError::ANGLE_TOO_NEGATIVE;
}
return KinematicError::NONE;
}
// Determine the unit distance between (2) 3D points
float three_axis_dist(float* point1, float* point2) {
return sqrt(((point1[0] - point2[0]) * (point1[0] - point2[0])) + ((point1[1] - point2[1]) * (point1[1] - point2[1])) +
((point1[2] - point2[2]) * (point1[2] - point2[2])));
}
// called by reporting for WPos status
void forward_kinematics(float* position) {
float calc_fwd[N_AXIS];
int status;
// convert the system position in steps to radians
float position_radians[N_AXIS];
int32_t position_steps[N_AXIS]; // Copy current state of the system position variable
memcpy(position_steps, sys_position, sizeof(sys_position));
system_convert_array_steps_to_mpos(position_radians, position_steps);
// detmine the position of the end effector joint center.
status = calc_forward_kinematics(position_radians, calc_fwd);
if (status == 0) {
// apply offsets and set the returned values
position[X_AXIS] = calc_fwd[X_AXIS] - gc_state.coord_system[X_AXIS] + gc_state.coord_offset[X_AXIS];
position[Y_AXIS] = calc_fwd[Y_AXIS] - gc_state.coord_system[Y_AXIS] + gc_state.coord_offset[Y_AXIS];
position[Z_AXIS] = calc_fwd[Z_AXIS] - gc_state.coord_system[Z_AXIS] + gc_state.coord_offset[Z_AXIS];
} else {
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "MSG:Fwd Kin Error");
}
}
bool kinematics_pre_homing(uint8_t cycle_mask) {
return true;
}
void kinematics_post_homing() {}
void user_m30() {}

View File

@@ -539,6 +539,42 @@
# define DYNAMIXEL_RTS UNDEFINED_PIN
#endif
// ================ User Digital I/O ==============================
#ifndef USER_DIGITAL_PIN_0
# define USER_DIGITAL_PIN_0 UNDEFINED_PIN
#endif
#ifndef USER_DIGITAL_PIN_1
# define USER_DIGITAL_PIN_1 UNDEFINED_PIN
#endif
#ifndef USER_DIGITAL_PIN_2
# define USER_DIGITAL_PIN_2 UNDEFINED_PIN
#endif
#ifndef USER_DIGITAL_PIN_3
# define USER_DIGITAL_PIN_3 UNDEFINED_PIN
#endif
// ================ User Analog I/O ==============================
#ifndef USER_ANALOG_PIN_0
# define USER_ANALOG_PIN_0 UNDEFINED_PIN
#endif
#ifndef USER_ANALOG_PIN_1
# define USER_ANALOG_PIN_1 UNDEFINED_PIN
#endif
#ifndef USER_ANALOG_PIN_2
# define USER_ANALOG_PIN_2 UNDEFINED_PIN
#endif
#ifndef USER_ANALOG_PIN_3
# define USER_ANALOG_PIN_3 UNDEFINED_PIN
#endif
#ifndef USER_ANALOG_PIN_0_FREQ
# define USER_ANALOG_PIN_0_FREQ 5000
#endif

View File

@@ -106,7 +106,7 @@ void inverse_kinematics(float* target, plan_line_data_t* pl_data, float* positio
bool kinematics_pre_homing(uint8_t cycle_mask);
void kinematics_post_homing();
// Called if USE_FWD_KINEMATIC is defined
// Called if USE_FWD_KINEMATICS is defined
void forward_kinematics(float* position);
// Called if MACRO_BUTTON_0_PIN or MACRO_BUTTON_1_PIN or MACRO_BUTTON_2_PIN is defined

View File

@@ -24,9 +24,7 @@
*/
#define MACHINE_NAME "6 Pack Controller StepStick XYZ"
#ifdef N_AXIS
#undef N_AXIS
#endif
#define N_AXIS 3
#ifdef ENABLE_SD_CARD
#undef ENABLE_SD_CARD
@@ -60,9 +58,6 @@
#define X_STEPPER_MS3 I2SO(3) // X_CS
#define Y_STEPPER_MS3 I2SO(6) // Y_CS
#define Z_STEPPER_MS3 I2SO(11) // Z_CS
#define A_STEPPER_MS3 I2SO(14) // A_CS
#define B_STEPPER_MS3 I2SO(19) // B_CS
#define C_STEPPER_MS3 I2SO(22) // C_CS
#define STEPPER_RESET GPIO_NUM_19

View File

@@ -2,7 +2,7 @@
// clang-format off
/*
dxl_delta.h
dxl_delta_module.h
2020 - Bart Dring
@@ -19,9 +19,16 @@
*/
#define MACHINE_NAME "Dynamixel Delta"
// In Socket #3
#define DYNAMIXEL_RXD GPIO_NUM_26
#define DYNAMIXEL_RTS GPIO_NUM_4
#define DYNAMIXEL_TXD GPIO_NUM_16
/*
#define DYNAMIXEL_TXD GPIO_NUM_4
#define DYNAMIXEL_RXD GPIO_NUM_13
#define DYNAMIXEL_RTS GPIO_NUM_17
*/
#define SERVO_TIMER_INTerval 100 // milliseconds
@@ -30,7 +37,7 @@
#define Z_DYNAMIXEL_ID 3 // protocol ID
// limit servo to 180 degree motion
#define DXL_COUNT_MIN 1024
#define DXL_COUNT_MAX 3072
#define DXL_COUNT_MIN 1024
#define DXL_COUNT_MAX 3072
#define SPINDLE_TYPE SpindleType::NONE
#define SPINDLE_TYPE SpindleType::PWM

View File

@@ -245,10 +245,10 @@
// so non-Cartesian machines can be implemented.
// #define USE_KINEMATICS
// USE_FWD_KINEMATIC enables the forward_kinematics() function
// USE_FWD_KINEMATICS enables the forward_kinematics() function
// that converts motor positions in non-Cartesian coordinate
// systems back to Cartesian form, for status reports.
//#define USE_FWD_KINEMATIC
//#define USE_FWD_KINEMATICS
// USE_TOOL_CHANGE enables the user_tool_change() function
// that implements custom tool change procedures.

View File

@@ -38,7 +38,7 @@
#define SEGMENT_LENGTH 0.5 // segment length in mm
#define USE_KINEMATICS
#define USE_FWD_KINEMATIC // report in cartesian
#define USE_FWD_KINEMATICS // report in cartesian
#define USE_M30
#define X_STEP_PIN GPIO_NUM_15

View File

@@ -0,0 +1,111 @@
#pragma once
// clang-format off
/*
tapster3.h
2019 - Bart Dring, Jason Huggins (Tapster Robotics)
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
#define MACHINE_NAME "Tapster 3 Delta (Dynamixel)"
#define CUSTOM_CODE_FILENAME "Custom/parallel_delta.cpp"
#define N_AXIS 3
// ================= Firmware Customization ===================
#define USE_KINEMATICS // there are kinematic equations for this machine
#define USE_FWD_KINEMATICS // report in cartesian
#define USE_MACHINE_INIT // There is some custom initialization for this machine
// ================== Delta Geometry ===========================
#define RADIUS_FIXED 70.0; // radius of the fixed side (length of motor cranks)
#define RADIUS_EFF 133.5; // radius of end effector side (length of linkages)
#define LENGTH_FIXED_SIDE 179.437f; // sized of fixed side triangel
#define LENGTH_EFF_SIDE 86.6025f; // size of end effector side triangle
#define SEGMENT_LENGTH 0.5 // segment length in mm
#define MAX_NEGATIVE_ANGLE -45 // in negative radians how far can the arms go up before damaging machine (max pi/2)
// =================== Machine Hardware Definition =============
#define DYNAMIXEL_TXD GPIO_NUM_4
#define DYNAMIXEL_RXD GPIO_NUM_13
#define DYNAMIXEL_RTS GPIO_NUM_17
#define X_DYNAMIXEL_ID 1 // protocol ID
#define Y_DYNAMIXEL_ID 2 // protocol ID
#define Z_DYNAMIXEL_ID 3 // protocol ID
// limit servo to 180 degree motion
#define DXL_COUNT_MIN 1024
#define DXL_COUNT_MAX 3072
#define SERVO_TIMER_INTERVAL 50
#define USER_DIGITAL_PIN_0 GPIO_NUM_25
#define USER_DIGITAL_PIN_1 GPIO_NUM_26
#define USER_DIGITAL_PIN_2 GPIO_NUM_27
#define USER_ANALOG_PIN_0 GPIO_NUM_2
#define USER_ANALOG_PIN_0_FREQ 50 // for use with RC servos duty range 5% to 10%
#define USER_ANALOG_PIN_1 GPIO_NUM_15
#define USER_ANALOG_PIN_1_FREQ 50 // for use with RC servos duty range 5% to 10%
#define USER_ANALOG_PIN_2 GPIO_NUM_16
#define USER_ANALOG_PIN_2_FREQ 50 // for use with RC servos duty range 5% to 10%
// ===================== Default Settings ==============================
#define DEFAULT_STEP_PULSE_MICROSECONDS 3
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 200 // 200ms
#define DEFAULT_STATUS_REPORT_MASK 1
#define DEFAULT_STEPPING_INVERT_MASK 0 // uint8_t
#define DEFAULT_DIRECTION_INVERT_MASK 0 // uint8_t
#define DEFAULT_INVERT_ST_ENABLE 0 // boolean
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_HOMING_ENABLE 0
#define DEFAULT_HOMING_DIR_MASK 0
// the following must have the same values for each axis
#define DEFAULT_X_STEPS_PER_MM 800 // value is actually arbitrary, but keep it smallish
#define DEFAULT_Y_STEPS_PER_MM DEFAULT_X_STEPS_PER_MM
#define DEFAULT_Z_STEPS_PER_MM DEFAULT_X_STEPS_PER_MM
#define DEFAULT_X_MAX_RATE 200.0 // units/min
#define DEFAULT_Y_MAX_RATE DEFAULT_X_MAX_RATE
#define DEFAULT_Z_MAX_RATE DEFAULT_X_MAX_RATE
#define DEFAULT_X_ACCELERATION 200.0 // 10*60*60 mm/min^2 = 10 mm/sec^2
#define DEFAULT_Y_ACCELERATION DEFAULT_X_ACCELERATION // 10*60*60 mm/min^2 = 10 mm/sec^2
#define DEFAULT_Z_ACCELERATION DEFAULT_X_ACCELERATION
#define DEFAULT_X_MAX_TRAVEL 3.14159265 // 180° in radians
#define DEFAULT_Y_MAX_TRAVEL DEFAULT_X_MAX_TRAVEL //
#define DEFAULT_Z_MAX_TRAVEL DEFAULT_X_MAX_TRAVEL //
#define ARM_INTERNAL_ANGLE 2.866 // due to mounting angle
#define DEFAULT_X_HOMING_MPOS (DEFAULT_X_MAX_TRAVEL / 2.0)
#define DEFAULT_Y_HOMING_MPOS DEFAULT_X_HOMING_MPOS
#define DEFAULT_Z_HOMING_MPOS DEFAULT_X_HOMING_MPOS
#define SPINDLE_TYPE SpindleType::NONE

View File

@@ -208,10 +208,10 @@
// so non-Cartesian machines can be implemented.
// #define USE_KINEMATICS
// USE_FWD_KINEMATIC enables the forward_kinematics() function
// USE_FWD_KINEMATICS enables the forward_kinematics() function
// that converts motor positions in non-Cartesian coordinate
// systems back to Cartesian form, for status reports.
//#define USE_FWD_KINEMATIC
//#define USE_FWD_KINEMATICS
// USE_TOOL_CHANGE enables the user_tool_change() function
// that implements custom tool change procedures.

View File

@@ -41,6 +41,8 @@ namespace Motors {
if (_tx_pin == UNDEFINED_PIN || _rx_pin == UNDEFINED_PIN || _rts_pin == UNDEFINED_PIN) {
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Dymanixel Error. Missing pin definitions");
is_active = false;
return;
}
@@ -58,7 +60,10 @@ namespace Motors {
config_message(); // print the config
test(); // ping the motor
if (!test()) { // ping the motor
is_active = false;
return;
}
set_disable(true); // turn off torque so we can set EEPROM registers
set_operating_mode(DXL_CONTROL_MODE_POSITION); // set it in the right control mode
@@ -111,6 +116,7 @@ namespace Motors {
} else {
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "%s Axis Dynamixel Servo ID %d Ping failed", _axis_name, _id);
return false;
}
return true;
@@ -139,9 +145,10 @@ namespace Motors {
void Dynamixel2::set_disable(bool disable) {
uint8_t param_count = 1;
_disabled = disable;
if (_disabled == disable)
return;
//grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "%s Axis %s", _axis_name, disable ? "Disable" : "Enable");
_disabled = disable;
if (_disabled)
dxl_write(DXL_ADDR_TORQUE_EN, param_count, 0);
@@ -155,6 +162,9 @@ namespace Motors {
}
void Dynamixel2::update() {
if (!is_active)
return;
if (_disabled) {
dxl_read_position();
} else {
@@ -223,8 +233,6 @@ namespace Motors {
uint32_t Dynamixel2::dxl_read_position() {
uint8_t data_len = 4;
//int32_t pos_min_steps, pos_max_steps; // in steps
//int32_t dxl_position, dxl_count_min, dxl_count_max;
dxl_read(DXL_PRESENT_POSITION, data_len);
@@ -239,11 +247,14 @@ namespace Motors {
int32_t pos_min_steps = lround(_position_min * axis_settings[_axis_index]->steps_per_mm->get());
int32_t pos_max_steps = lround(_position_max * axis_settings[_axis_index]->steps_per_mm->get());
sys_position[_axis_index] = map(dxl_position, DXL_COUNT_MIN, DXL_COUNT_MAX, pos_min_steps, pos_max_steps);
int32_t temp = map(dxl_position, DXL_COUNT_MIN, DXL_COUNT_MAX, pos_min_steps, pos_max_steps);
sys_position[_axis_index] = temp;
plan_sync_position();
return dxl_position;
} else {
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Data len arror: %d", data_len);
return 0;
}
}
@@ -362,6 +373,7 @@ namespace Motors {
//determine the location of the axis
float target = system_convert_axis_steps_to_mpos(sys_position, axis); // get the axis machine position in mm
float travel = axis_settings[axis]->max_travel->get();
float mpos = axis_settings[axis]->home_mpos->get();
@@ -382,8 +394,6 @@ namespace Motors {
// map the mm range to the servo range
dxl_position = (uint32_t)mapConstrain(target, position_min, position_max, dxl_count_min, dxl_count_max);
//grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "Range: %5.3f/%5.3f Target:%5.3f Pos %d", position_min, position_max, target, dxl_position);
tx_message[++msg_index] = current_id; // ID of the servo
tx_message[++msg_index] = dxl_position & 0xFF; // data
tx_message[++msg_index] = (dxl_position & 0xFF00) >> 8; // data

View File

@@ -108,8 +108,7 @@ namespace Motors {
static uint16_t dxl_update_crc(uint16_t crc_accum, char* data_blk_ptr, uint8_t data_blk_size);
static void dxl_bulk_goal_position(); // set all motorsd init_uart(uint8_t id, uint8_t axis_index, uint8_t dual_axis_index);
bool _disabled;
float _homing_position;
float _dxl_count_min;

View File

@@ -68,5 +68,6 @@ namespace Motors {
float _position_max = 0; // position in millimeters
bool _can_home = true;
bool _disabled;
};
}

View File

@@ -332,19 +332,16 @@ bool motors_have_type_id(motor_class_id_t id) {
}
void motors_set_disable(bool disable) {
static bool previous_state = false;
static bool previous_state = true;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Motors disable %d", disable);
/*
if (previous_state == disable) {
return;
}
previous_state = disable;
if (step_enable_invert->get()) {
disable = !disable; // Apply pin invert.
}
digitalWrite(STEPPERS_DISABLE_PIN, disable);
previous_state = disable;
*/
// now loop through all the motors to see if they can individually diable
for (uint8_t gang_index = 0; gang_index < MAX_GANGED; gang_index++) {
@@ -352,6 +349,13 @@ void motors_set_disable(bool disable) {
myMotor[axis][gang_index]->set_disable(disable);
}
}
// invert only inverts the global stepper disable pin.
if (step_enable_invert->get()) {
disable = !disable; // Apply pin invert.
}
digitalWrite(STEPPERS_DISABLE_PIN, disable);
}
void motors_read_settings() {

View File

@@ -42,7 +42,7 @@ namespace Motors {
uint8_t _pwm_pin;
uint8_t _channel_num;
uint32_t _current_pwm_duty;
bool _disabled;
float _homing_position;

View File

@@ -274,10 +274,15 @@ namespace Motors {
if (has_errors)
return;
digitalWrite(disable_pin, disable);
if (_disabled == disable)
return;
_disabled = disable;
digitalWrite(disable_pin, _disabled);
#ifdef USE_TRINAMIC_ENABLE
if (disable) {
if (_disabled) {
tmcstepper->toff(TRINAMIC_TOFF_DISABLE);
} else {
if (_mode == TRINAMIC_MODE_STEALTHCHOP) {

View File

@@ -196,6 +196,11 @@ float constrain_float(float in, float min, float max) { // DrawBot_Badge
return in;
}
long mapConstrain(long x, long in_min, long in_max, long out_min, long out_max) {
x = constrain(x, in_min, in_max);
return map(x, in_min, in_max, out_min, out_max);
}
float mapConstrain(float x, float in_min, float in_max, float out_min, float out_max) {
x = constrain_float(x, in_min, in_max);
return map_float(x, in_min, in_max, out_min, out_max);

View File

@@ -668,7 +668,7 @@ void report_realtime_status(uint8_t client) {
if (bit_istrue(status_mask->get(), BITFLAG_RT_STATUS_POSITION_TYPE)) {
strcat(status, "|MPos:");
} else {
#ifdef USE_FWD_KINEMATIC
#ifdef USE_FWD_KINEMATICS
forward_kinematics(print_position);
#endif
strcat(status, "|WPos:");

View File

@@ -328,7 +328,7 @@ void make_settings() {
// TODO Settings - also need to clear, but not set, soft_limits
arc_tolerance = new FloatSetting(GRBL, WG, "12", "GCode/ArcTolerance", DEFAULT_ARC_TOLERANCE, 0, 1);
junction_deviation = new FloatSetting(GRBL, WG, "11", "GCode/JunctionDeviation", DEFAULT_JUNCTION_DEVIATION, 0, 10);
status_mask = new IntSetting(GRBL, WG, "10", "Report/Status", DEFAULT_STATUS_REPORT_MASK, 0, 2);
status_mask = new IntSetting(GRBL, WG, "10", "Report/Status", DEFAULT_STATUS_REPORT_MASK, 0, 3);
probe_invert = new FlagSetting(GRBL, WG, "6", "Probe/Invert", DEFAULT_INVERT_PROBE_PIN);
limit_invert = new FlagSetting(GRBL, WG, "5", "Limits/Invert", DEFAULT_INVERT_LIMIT_PINS);

View File

@@ -34,7 +34,7 @@ volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor
volatile uint8_t sys_rt_exec_debug;
#endif
UserOutput::AnalogOutput* myAnalogOutputs[MaxUserDigitalPin];
UserOutput::AnalogOutput* myAnalogOutputs[MaxUserDigitalPin];
UserOutput::DigitalOutput* myDigitalOutputs[MaxUserDigitalPin];
xQueueHandle control_sw_queue; // used by control switch debouncing
@@ -95,53 +95,17 @@ void system_ini() { // Renamed from system_init() due to conflict with esp32 fi
SPI.begin(GRBL_SPI_SCK, GRBL_SPI_MISO, GRBL_SPI_MOSI, GRBL_SPI_SS);
#endif
// Setup USER_DIGITAL_PINs controlled by M62, M63, M64, & M65
#ifdef USER_DIGITAL_PIN_0
// Setup M62,M63,M64,M65 pins
myDigitalOutputs[0] = new UserOutput::DigitalOutput(0, USER_DIGITAL_PIN_0);
#else
myDigitalOutputs[0] = new UserOutput::DigitalOutput();
#endif
#ifdef USER_DIGITAL_PIN_1
myDigitalOutputs[1] = new UserOutput::DigitalOutput(1, USER_DIGITAL_PIN_1);
#else
myDigitalOutputs[1] = new UserOutput::DigitalOutput();
#endif
#ifdef USER_DIGITAL_PIN_2
myDigitalOutputs[2] = new UserOutput::DigitalOutput(2, USER_DIGITAL_PIN_2);
#else
myDigitalOutputs[2] = new UserOutput::DigitalOutput();
#endif
#ifdef USER_DIGITAL_PIN_3
myDigitalOutputs[3] = new UserOutput::DigitalOutput(3, USER_DIGITAL_PIN_3);
#else
myDigitalOutputs[3] = new UserOutput::DigitalOutput();
#endif
myDigitalOutputs[0] = new UserOutput::DigitalOutput(1, USER_DIGITAL_PIN_1);
myDigitalOutputs[0] = new UserOutput::DigitalOutput(2, USER_DIGITAL_PIN_2);
myDigitalOutputs[0] = new UserOutput::DigitalOutput(3, USER_DIGITAL_PIN_3);
// Setup M67 Pins
#ifdef USER_ANALOG_PIN_0
// Setup M67 Pins
myAnalogOutputs[0] = new UserOutput::AnalogOutput(0, USER_ANALOG_PIN_0, USER_ANALOG_PIN_0_FREQ);
#else
myAnalogOutputs[0] = new UserOutput::AnalogOutput();
#endif
#ifdef USER_ANALOG_PIN_1
myAnalogOutputs[1] = new UserOutput::AnalogOutput(1, USER_ANALOG_PIN_1, USER_ANALOG_PIN_1_FREQ);
#else
myAnalogOutputs[1] = new UserOutput::AnalogOutput();
#endif
#ifdef USER_ANALOG_PIN_2
myAnalogOutputs[2] = new UserOutput::AnalogOutput(2, USER_ANALOG_PIN_2, USER_ANALOG_PIN_2_FREQ);
#else
myAnalogOutputs[2] = new UserOutput::AnalogOutput();
#endif
#ifdef USER_ANALOG_PIN_3
myAnalogOutputs[3] = new UserOutput::AnalogOutput(3, USER_ANALOG_PIN_3, USER_ANALOG_PIN_3_FREQ);
#else
myAnalogOutputs[3] = new UserOutput::AnalogOutput();
#endif
}
#ifdef ENABLE_CONTROL_SW_DEBOUNCE
@@ -169,7 +133,7 @@ void IRAM_ATTR isr_control_inputs() {
xQueueSendFromISR(control_sw_queue, &evt, NULL);
}
#else
uint8_t pin = system_control_get_state();
uint8_t pin = system_control_get_state();
system_exec_control_pin(pin);
#endif
}
@@ -435,9 +399,6 @@ bool sys_pwm_control(uint8_t io_num_mask, float duty, bool synchronized) {
return cmd_ok;
}
// Call this function to get an RMT channel number
// returns -1 for error
int8_t sys_get_next_RMT_chan_num() {

View File

@@ -78,7 +78,8 @@ namespace UserOutput {
}
void AnalogOutput::config_message() {
grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "User Analog Output:%d on Pin:%s", _number, pinName(_pin).c_str());
grbl_msg_sendf(
CLIENT_SERIAL, MsgLevel::Info, "User Analog Output:%d on Pin:%s Freq:%0.0fHz", _number, pinName(_pin).c_str(), _pwm_frequency);
}
// returns true if able to set value

View File

@@ -57,12 +57,10 @@ Synchronized means all steps in the buffers must complete before the I/O is chan
- Reset - All pins will be set to the off state
- Alarm Mode - No change in pin state
- Error - No change in pins state
- End of File - TBD
- End of File - No change in pins state
### Setup
M62 - M65
```
#define USER_DIGITAL_PIN_0 GPIO_NUM_xx
#define USER_DIGITAL_PIN_1 GPIO_NUM_xx
@@ -79,14 +77,24 @@ M62 - M65
### Frequency
Defining a frequency is optional. If you do not define one for a pin the default of 5000 will be used. If you are using the PWM to control an RC Servo, you should set it to something around 50.
Defining a frequency is optional. If you do not define one for a pin the default of 5000 will be used.
### Resolution
The resolution is dependent on the frequency used. The PWM is based on a 80MHz timer. If you have a 10KHz frequency, you have 80,000,000 / 10,000 you have a maximum of an 8,000 count resolution. The resolution is based on bits so you need to round down to the nearest bit value which would be 14 bit or 4096. The highest bit value allowed is 13 bits.
The resolution is dependent on the frequency used. The PWM is based on a 80MHz timer. If you have a 10KHz frequency, 80,000,000 / 10,000 gives you a maximum of an 8,000 count resolution. The resolution is based on bits so you need to round down to the nearest bit value which would be 12 bit or 4096. The highest bit value allowed is 13 bits.
This is all done behind the scenes, you only have to provide the frequency and duty.
### Duty
The duty is a percentage of the period. It you are looking for a specific pulse width, you need to determine the period, which is 1/freq. If your frequency is 100Hz your period is 10ms. If you want a 1ms pulse, you would set the duty to 0.10%.
The duty is a percentage of the period. It you are looking for a specific pulse width, you need to determine the period, which is 1/freq. If your frequency is 100Hz your period is 10ms. If you want a 1ms pulse, you would set the duty to 0.10%. Duty is a floating point number. A value of XX.XX will get you the highest resolution. Values above 100% are set at 100%. Negative numbers are set to 0%.
### Use With RC Servos
RC Servos set their position based on the pulse length of a PWM signal. The standard frequency for RC servos PWM is 50Hz, some digital servos can handle a higher rate, but it will not improve performance in this application and could overheat some servos.
The standard pulse range for RC servos is 1ms to 2ms. Some servos have a wider range.
With a 50Hz frequency the period is 20ms. Therefore a 1ms pulse is 5% duty and a 2ms pulse is 10% duty. Use the 2 values to to go from one end of travel to the other. You can use values in between to go to other positions. You can experiment with a little less or more if you servos have a bigger range.
You can also set the duty to 0%. This generally turns a servo off and allows it to free wheel and not draw much power.