Started on the webserver sketch, connected it to the motor code with tcp communication, implemented a simple webserver interface to control the device
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
#include <AccelStepper.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
// === Pin Definitions ===
|
||||
#define M1_STEP_PIN 0 // Reel motor step
|
||||
#define M1_DIR_PIN 1 // Reel motor direction
|
||||
|
||||
#define M2_STEP_PIN 2 // Carriage motor step
|
||||
#define M2_DIR_PIN 3 // Carriage motor direction
|
||||
|
||||
#define START_SW_PIN 6 // Home limit switch
|
||||
#define END_SW_PIN 5 // End limit switch
|
||||
|
||||
// Wi-Fi credentials
|
||||
const char* ssid = "";
|
||||
const char* password = "";
|
||||
|
||||
WiFiServer motorServer(3333); // Open port 3333
|
||||
WiFiClient activeClient; // Store the current client
|
||||
|
||||
String recvBuffer = "";
|
||||
|
||||
AccelStepper reelMotor(AccelStepper::DRIVER, M1_STEP_PIN, M1_DIR_PIN);
|
||||
AccelStepper carriageMotor(AccelStepper::DRIVER, M2_STEP_PIN, M2_DIR_PIN);
|
||||
|
||||
// === Motor Configuration ===
|
||||
const int MOTOR_STEPS = 200; // 200 steps/rev for NEMA 17
|
||||
const int MICROSTEPPING = 8; // Set on driver board (MS1/MS2 jumpers)
|
||||
const float DISTANCE_PER_REVOLUTION = 2.0; // mm per revolution (best to do a revolution by hand and measure distance and input it here)
|
||||
const float STEPS_PER_MM = (MOTOR_STEPS * MICROSTEPPING) / DISTANCE_PER_REVOLUTION;
|
||||
|
||||
// === Speed Settings ===
|
||||
const float REEL_RPM = 40; // Reel motor speed
|
||||
float CARRIAGE_RPM = 100; // Carriage motor speed (will be dynamically set)
|
||||
#define FORWARD -1 // In my setup the carriage is moving forward is in negative speed, meaning away from the motor, that why we need to invert the movement in tracking
|
||||
|
||||
// System state management
|
||||
enum SystemState {
|
||||
IDLE,
|
||||
SYSTEM_CALIBRATION,
|
||||
REEL_CALIBRATION,
|
||||
REELING
|
||||
};
|
||||
SystemState currentSystemState = IDLE;
|
||||
|
||||
enum SystemCalibrationState {
|
||||
FIND_START,
|
||||
FIND_END
|
||||
};
|
||||
SystemCalibrationState systemCalibrationState = FIND_START;
|
||||
|
||||
// Position tracking
|
||||
float railLength = 0;
|
||||
float currentPositionMM = 0;
|
||||
float targetPosition = 0;
|
||||
bool isMovingToEnd = false;
|
||||
|
||||
// Gap values
|
||||
float reelGapStartPosition = -1;
|
||||
float reelGapEndPosition = -1;
|
||||
|
||||
bool starterSet = false; // Is start gap position set?
|
||||
bool endSet = false; // Is start gap position set?
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("Motor ESP Connecting");
|
||||
// Connect to Wi-Fi
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("\nMotor ESP connected. IP: " + WiFi.localIP().toString());
|
||||
|
||||
// FIXME : Perhaps use UDP communication instead maybe it will not block the ESP32C3 as much
|
||||
motorServer.begin(); // Start TCP server
|
||||
|
||||
pinMode(START_SW_PIN, INPUT_PULLUP);
|
||||
pinMode(END_SW_PIN, INPUT_PULLUP);
|
||||
|
||||
// Configure reel motor
|
||||
reelMotor.setMaxSpeed((REEL_RPM * MOTOR_STEPS * MICROSTEPPING) / 60.0);
|
||||
reelMotor.setSpeed(-reelMotor.maxSpeed()); // To reel the line in toward the fishing reel, i need the fishing reel to move anti-clockwise
|
||||
reelMotor.setMinPulseWidth(50);
|
||||
|
||||
// Configure carriage motor
|
||||
carriageMotor.setMaxSpeed((CARRIAGE_RPM * MOTOR_STEPS * MICROSTEPPING) / 60.0);
|
||||
carriageMotor.setSpeed(carriageMotor.maxSpeed());
|
||||
carriageMotor.setMinPulseWidth(50);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Accept new clients
|
||||
if (!activeClient || !activeClient.connected()) {
|
||||
activeClient = motorServer.available();
|
||||
}
|
||||
|
||||
if (activeClient && activeClient.connected() && activeClient.available()) {
|
||||
while (activeClient.available()) {
|
||||
char c = activeClient.read();
|
||||
|
||||
if (c == '\n') {
|
||||
recvBuffer.trim(); // Remove trailing \r or whitespace
|
||||
if (recvBuffer.length() > 0) {
|
||||
handleCommand(recvBuffer, activeClient);
|
||||
}
|
||||
recvBuffer = ""; // Reset for next command
|
||||
} else {
|
||||
recvBuffer += c;
|
||||
// Optional: protect against overflow
|
||||
if (recvBuffer.length() > 200) recvBuffer = ""; // Reset if too long
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main state machine
|
||||
switch (currentSystemState) {
|
||||
case IDLE: break;
|
||||
case REELING: runReeling(); break;
|
||||
case REEL_CALIBRATION: runCalibration(); break;
|
||||
case SYSTEM_CALIBRATION:
|
||||
if (systemCalibrationState == FIND_START) runFindStart();
|
||||
else if (systemCalibrationState == FIND_END) runFindEnd();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void runFindStart() {
|
||||
// Force the carriage motor to find the start switch by moving it positive direction, meaning toward the motor
|
||||
if(carriageMotor.speed() <= 0){
|
||||
carriageMotor.setSpeed(carriageMotor.maxSpeed());
|
||||
}
|
||||
|
||||
carriageMotor.runSpeed();
|
||||
|
||||
//
|
||||
if (digitalRead(START_SW_PIN) == LOW) {
|
||||
carriageMotor.stop();
|
||||
carriageMotor.setSpeed(0);
|
||||
currentPositionMM = 0; // Start from zero
|
||||
carriageMotor.setCurrentPosition(0);
|
||||
carriageMotor.setSpeed(carriageMotor.maxSpeed()); // Always move forward from here
|
||||
systemCalibrationState = FIND_END;
|
||||
}
|
||||
}
|
||||
|
||||
void runFindEnd() {
|
||||
|
||||
// Force the carriage motor to find the end switch by moving it negative direction, meaning away from the motor
|
||||
if(carriageMotor.speed() >= 0){
|
||||
carriageMotor.setSpeed(carriageMotor.maxSpeed() * FORWARD);
|
||||
}
|
||||
|
||||
if (carriageMotor.runSpeed()) {
|
||||
currentPositionMM += (1.0 / STEPS_PER_MM); // Calculate the new position when the motor actually does steps
|
||||
}
|
||||
|
||||
if (digitalRead(END_SW_PIN) == LOW) {
|
||||
carriageMotor.stop();
|
||||
carriageMotor.setSpeed(0);
|
||||
|
||||
railLength = currentPositionMM; // Assign the maximum length the motor can travel
|
||||
targetPosition = railLength;
|
||||
|
||||
currentSystemState = IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void runReeling() {
|
||||
|
||||
if(reelGapStartPosition < 0 || reelGapEndPosition < 0){
|
||||
// TODO : Maybe throw an error for the user
|
||||
stopMotors();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWithinRange(currentPositionMM, reelGapStartPosition, reelGapEndPosition)) {
|
||||
|
||||
if(reelMotor.speed() == 0){
|
||||
reelMotor.setSpeed(-reelMotor.maxSpeed());
|
||||
}
|
||||
reelMotor.runSpeed();
|
||||
}
|
||||
|
||||
// === Oscillate effect ===
|
||||
if (!isMovingToEnd) {
|
||||
if (runMoveToPosition(reelGapStartPosition)) {
|
||||
isMovingToEnd = true;
|
||||
}
|
||||
} else {
|
||||
if (runMoveToPosition(reelGapEndPosition)) {
|
||||
isMovingToEnd = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void runCalibration() {
|
||||
runMoveToPosition(targetPosition);
|
||||
}
|
||||
|
||||
bool runMoveToPosition(float position) {
|
||||
|
||||
// If it want's to move out of bounds, stop the motors, maybe it will damage the setup
|
||||
if(position < 0.0f || position > railLength){
|
||||
stopMotors();
|
||||
return false;
|
||||
}
|
||||
|
||||
float distance = abs(currentPositionMM - position);
|
||||
float margin = 0.01f;
|
||||
|
||||
if (distance <= margin) {
|
||||
carriageMotor.setSpeed(0);
|
||||
carriageMotor.stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
int direction = sign(position - currentPositionMM);
|
||||
int previousDirection = (carriageMotor.speed() / FORWARD) / carriageMotor.maxSpeed();
|
||||
|
||||
if (previousDirection != direction) {
|
||||
carriageMotor.setSpeed(carriageMotor.maxSpeed() * direction * FORWARD);
|
||||
}
|
||||
|
||||
if (carriageMotor.runSpeed()) {
|
||||
currentPositionMM += (carriageMotor.speed() > 0) ? (-1.0 / STEPS_PER_MM) : (1.0 / STEPS_PER_MM);
|
||||
|
||||
if (distance <= margin) {
|
||||
carriageMotor.setSpeed(0);
|
||||
carriageMotor.stop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void stopMotors(){
|
||||
currentSystemState = IDLE;
|
||||
carriageMotor.stop();
|
||||
carriageMotor.setSpeed(0);
|
||||
reelMotor.stop();
|
||||
reelMotor.setSpeed(0);
|
||||
}
|
||||
|
||||
float calculateCarriageRPM(float reelRPM, float gapLengthMM) {
|
||||
float timeForReelRevs = 4.0f * 60.0f / reelRPM;
|
||||
float mmPerSec = gapLengthMM / timeForReelRevs;
|
||||
float carriageRevsPerSec = mmPerSec / DISTANCE_PER_REVOLUTION;
|
||||
return carriageRevsPerSec * 60.0f;
|
||||
}
|
||||
|
||||
bool isWithinRange(float currentPosition, float start, float end) {
|
||||
return currentPosition >= start && currentPosition <= end;
|
||||
}
|
||||
|
||||
float sign(float value) {
|
||||
return float((value > 0) - (value < 0));
|
||||
}
|
||||
|
||||
String stateToString(SystemState systemState) {
|
||||
switch(systemState) {
|
||||
case IDLE: return "IDLE";
|
||||
case REELING: return "REELING";
|
||||
case REEL_CALIBRATION: return "REEL_CALIBRATION";
|
||||
case SYSTEM_CALIBRATION: return "SYSTEM_CALIBRATION";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void handleCommand(const String& cmd, WiFiClient& client) {
|
||||
if (cmd.startsWith("starter-position-input")) {
|
||||
float value;
|
||||
int scanned = sscanf(cmd.c_str(), "starter-position-input %f", &value);
|
||||
if (scanned == 1) {
|
||||
reelGapStartPosition = value;
|
||||
starterSet = true;
|
||||
|
||||
if (starterSet && endSet) {
|
||||
float gap = reelGapEndPosition - reelGapStartPosition;
|
||||
float newCarriageRPM = calculateCarriageRPM(REEL_RPM, gap);
|
||||
CARRIAGE_RPM = newCarriageRPM;
|
||||
float maxSpeed = (newCarriageRPM * MOTOR_STEPS * MICROSTEPPING) / 60.0;
|
||||
carriageMotor.setMaxSpeed(maxSpeed);
|
||||
}
|
||||
|
||||
client.println("OK");
|
||||
} else {
|
||||
client.println("Invalid starter-position-input command");
|
||||
}
|
||||
|
||||
} else if (cmd.startsWith("end-position-input")) {
|
||||
float value;
|
||||
int scanned = sscanf(cmd.c_str(), "end-position-input %f", &value);
|
||||
if (scanned == 1) {
|
||||
reelGapEndPosition = value;
|
||||
endSet = true;
|
||||
|
||||
if (starterSet && endSet) {
|
||||
float gap = reelGapEndPosition - reelGapStartPosition;
|
||||
float newCarriageRPM = calculateCarriageRPM(REEL_RPM, gap);
|
||||
CARRIAGE_RPM = newCarriageRPM;
|
||||
float maxSpeed = (newCarriageRPM * MOTOR_STEPS * MICROSTEPPING) / 60.0;
|
||||
carriageMotor.setMaxSpeed(maxSpeed);
|
||||
}
|
||||
|
||||
client.println("OK");
|
||||
} else {
|
||||
client.println("Invalid end-position-input command");
|
||||
}
|
||||
} else if (cmd == "status") {
|
||||
StaticJsonDocument<200> doc;
|
||||
doc["position"] = currentPositionMM;
|
||||
doc["state"] = stateToString(currentSystemState);
|
||||
doc["railLength"] = railLength;
|
||||
doc["starterSet"] = starterSet;
|
||||
doc["endSet"] = endSet;
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
client.println(json);
|
||||
|
||||
} else if (cmd == "calibrate") {
|
||||
currentSystemState = SYSTEM_CALIBRATION;
|
||||
systemCalibrationState = FIND_START;
|
||||
client.println("System calibration started");
|
||||
|
||||
} else if (cmd == "reel_cal") {
|
||||
if(currentSystemState != REEL_CALIBRATION){
|
||||
currentSystemState = REEL_CALIBRATION;
|
||||
|
||||
} else {
|
||||
currentSystemState = IDLE;
|
||||
}
|
||||
|
||||
if(starterSet && endSet){
|
||||
targetPosition = (reelGapEndPosition - reelGapStartPosition) / 2.0f;
|
||||
}else {
|
||||
targetPosition = 0;
|
||||
}
|
||||
|
||||
client.println("Reel calibration mode");
|
||||
|
||||
} else if (cmd == "start") {
|
||||
if (starterSet && endSet) {
|
||||
currentSystemState = REELING;
|
||||
client.println("Started reeling");
|
||||
} else {
|
||||
client.println("Error: Gap not set");
|
||||
}
|
||||
|
||||
} else if (cmd == "stop") {
|
||||
stopMotors();
|
||||
client.println("Stopped");
|
||||
} else if (cmd.startsWith("move")) {
|
||||
float value;
|
||||
int scanned = sscanf(cmd.c_str(), "move %f", &value);
|
||||
if (scanned == 1) {
|
||||
targetPosition = value;
|
||||
currentSystemState = REEL_CALIBRATION; // Use existing logic to move to target
|
||||
client.println("OK");
|
||||
} else {
|
||||
client.println("Invalid end-position-input command");
|
||||
}
|
||||
|
||||
}else {
|
||||
client.println("Unknown command");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user