Final Project
In this step, I focused on positioning the stepper motors in the optimal configuration for a compact and efficient design. Initially, I faced challenges in finding the right positions for a compact layout. My first attempt was to position the solenoid facing down, with the stepper motor shaft of the X-axis facing up. However, this design proved problematic for a few reasons:
In Autodesk Inventor Professional, I used the Derive tool to import the stepper motor STEP file for accurate referencing. Then, I utilized the Direct tool to move and rotate the solids until I achieved the desired positioning. This process ensured precise alignment and integration of the stepper motors within the overall design.
The IR sensor used is from my Input Devices Assignment. The sensor features a 6mm gap between the top and bottom LEDs for the dual purpose of detecting the X carriage and paper feeding into the system.
The IR sensor didn't fit correctly because the pinheads were interfering during installation. I modified the 3D model to adjust the measurements. Additionally, the mounting hole for an M3 screw broke, so it was reinforced for greater reliability and longevity.
I also tested the alignment of the Gates GT2 6mm belts to ensure smooth operation. Proper alignment is crucial; if the belts are not aligned correctly, they can rub against other components. Ensuring the correct distances will contribute to a well-built X carriage assembly.
For the X carriage assembly, I wanted the IR sensor to serve dual functions: acting as a limit switch for the solenoid axis and as a paper feeder detector. To achieve this, I added a flap to the carriage that blocks the light of the IR LED when homing to the maximum position. Once homed, it moves to the zero position, clearing the IR sensor for the paper feeder procedure. This design choice simplified the machine, making it more affordable and easier to build.
For the belt system, I used GT2 6mm Gates belts and attached them to the carriage with a piece of filament. This approach significantly reduced the complexity of the assembly and made it super compact. It worked well because there is no load on the toolhead, so the belt attachment system performed as expected, providing a reliable and efficient solution.
The paper feeder mechanism is responsible for precisely and efficiently feeding an A4 sheet of paper to ensure consistent results during operation. The roller feeds the paper through two guides on both sides to maintain alignment. These guides must be smooth and have gentle transitions to prevent paper jamming.
To achieve smooth and precise paper feeding, I added spring rollers above the feeder rod. This creates additional friction between the paper and the feeder rod. Initially, I planned to use metal springs for the spring roller mechanism. However, I opted for the natural flexibility of the thermoplastics used in 3D printing to act as springs and hold the rollers in place. This decision was made to reduce the number of components, resulting in a cheaper, simpler, and more effective design.
For this 3D model, I decided to print only the sections that interface with other components. This approach promotes responsible material management by minimizing plastic usage without compromising the workflow. Printing just the necessary sections allows for quicker iterations and reduces plastic consumption during the prototyping stage.
At this stage, I was testing the friction between the feeder rod and the spring rollers, as well as the alignment of the IR sensor with the paper guides.
To minimize the flexing of the acrylic during operation, I added a rib for reinforcement. This rib was fabricated using 3D printing.
For machining large PCBs, it is essential to ensure that the base on which the PCB is mounted is perfectly flat. The milling process only removes 0.1 mm of material, so any variations in the flatness of the PCB will result in poor quality. To achieve the necessary flatness, I decided to mill the entire acrylic base of the Roland machine using an 8 mm flat end mill. After milling, I sanded the surface to remove any machining residue. With the base now perfectly flat, it is ready for PCB milling.
Always remember to use personal protective equipment (PPE) such as safety glasses and a mask during these procedures to protect yourself from dust and debris.
For this prototype board, I encountered issues with improper placement and connections. The drivers were inverted, so I had to mirror them since all the through-hole components will be placed on the back of the PCB. For testing purposes, I soldered them on the top of the PCB, which was very uncomfortable. Additionally, I made a poor connection with the power jack, necessitating the soldering of a piece of wire to jump the 12V input.
For the integration of the electronics, I decided to place them near the stepper motors in a strategic position. This placement ensures good accessibility to the power input for 12V and the USB-C port of the Seeeduino Xiao for serial communication. My aim was to make the design as compact as possible, and I believe I achieved that goal. The electronics are positioned to facilitate easy access while maintaining a clean and efficient layout, contributing to the overall compactness and functionality of the system.
For that purpose, I imported the motor mount .ipt file and used it as a derived part to ensure compatibility with already designed components. This allowed me to take into consideration the existing design elements while positioning the new components. I then positioned the PCB 3D model using the direct tool until I found an appropriate position.
For the integration of the electronics, I designed an electronics box with several key features to ensure functionality and ease of use. The box includes mounting holes and spacers for securely holding the PCB in place, ensuring stability and proper positioning. Additionally, the front part of the box features a removable lid, allowing for easy access to the electronics and stepper motors. This design facilitates maintenance and adjustments without needing to disassemble the entire system. The box also includes feet on the bottom to provide stability and prevent sliding or tipping over, ensuring that the electronics remain secure during operation.
These features contribute to a compact and efficient design, providing good accessibility to critical components while maintaining a clean and organized layout. This approach achieves a streamlined and practical integration of the electronics into the overall system.
The reason why I use derived parts while designing is to ensure that my designs fit correctly and do not have any interference. This approach allows me to consider existing components and their dimensions accurately. Above, you can see some photos of the fully integrated mechanical and electronics box assemblies.
I developed a program that enables G-code control via serial communication. The G-code command G28 is designated for homing the axes. When this command is received, the X-carriage first homes to its maximum position and then moves to position 0. Once the X-carriage is homed and positioned at 0, the paper feeder activates. It stops when the paper is detected, then feeds the paper to the Y-axis 0 position. When all axes are in position, the machine enables linear movements using the G1 command: G1 X for the X carriage and G1 Y for the paper feeder. The solenoid activates with the P command.
The machine also includes safety features. It sends serial messages indicating if the axes are not homed or if movement exceeds limits. Additionally, it reports when the axes are homed and ready after a G28 command.
For the embossing process, I created macros that can be called via serial commands to execute specific movements and punches. The idea is for the master PC to send G-code commands through the serial interface. The PC processes the text, translates it using a matrix, and generates the corresponding G-code for the Coquiduino Braille printer to emboss.
Currently, the machine prints sample procedures pre-loaded into the Seeeduino Xiao. However, the machine is fully functional and capable of receiving G-code commands from a PC. I used ChatGPT to assist me in creating parts of the code, which I then integrated and adapted to meet my requirements and preferences.
// Program Code: Coquiduino CNC Braille Printer//
// Written by: Jorge Suarez de Freitas + ChatGPT//
#include <queue>
#include <AccelStepper.h>
// Define pins for Motor 1 (Linear Axis with Homing)
#define STEP_PIN1 4
#define DIR_PIN1 3
// Define pins for Motor 2 (Paper Feeder)
#define STEP_PIN2 2
#define DIR_PIN2 1
// Shared enable pin
#define ENABLE_PIN 0
#define LIMIT_SWITCH_PIN 10
#define SOLENOID_PIN 9
// Motor configuration
AccelStepper stepper1(AccelStepper::DRIVER, STEP_PIN1, DIR_PIN1);
AccelStepper stepper2(AccelStepper::DRIVER, STEP_PIN2, DIR_PIN2);
// Variables for homing
bool homingDoneX = false;
bool homingDoneY = false;
// Configuration for paper feeder advancement
const float rollerDiameter = 8.0; // Roller diameter in mm
const float rollerPerimeter = 3.14159 * rollerDiameter; // Roller perimeter
const float stepsPerRevolution = 200.0;
const float microsteps = 16.0;
const float distancePerMicrostep = rollerPerimeter / (stepsPerRevolution * microsteps);
// Configuration for linear axis advancement
const float distancePerMicrostepX = 40.0 / (stepsPerRevolution * microsteps); // 40 mm per revolution with 20 teeth and GT2 (2 mm per tooth)
const float maxXPosition = 184.8; // Maximum position for X-axis in mm
const float maxYPosition = 1000.0; // Maximum position for X-axis in mm
// Define a struct to hold move commands
struct MoveCommand {
char axis;
float position;
bool solenoid;
};
// Queue to hold move commands
std::queue<MoveCommand> commandQueue;
void setup() {
// Initialize the limit switch and solenoid
pinMode(LIMIT_SWITCH_PIN, INPUT);
pinMode(SOLENOID_PIN, OUTPUT);
// Initialize motors
stepper1.setMaxSpeed(1000);
stepper1.setAcceleration(20000);
stepper2.setMaxSpeed(500);
stepper2.setAcceleration(10000);
// Enable motors
pinMode(ENABLE_PIN, OUTPUT);
digitalWrite(ENABLE_PIN, LOW);
delay(100);
Serial.begin(115200); // Set baud rate to match the one used in the Arduino IDE
Serial.println("System ready. Sending to home...");
}
void loop() {
// Read G-code commands from serial port
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
processGCode(command);
}
// Process the command queue
processCommandQueue();
}
// Function to perform homing of both axes
void homeAllAxes() {
homeLinearAxis();
while (!homingDoneX) {
delay(10); // Wait until homing of the linear axis is complete
}
MoveXAxis0();
homePaperFeeder();
}
void MoveXAxis0() {
while (stepper1.distanceToGo() != 0) {
stepper1.run();
}
stepper1.moveTo(0); // Move to position 0
while (stepper1.distanceToGo() != 0) {
stepper1.run();
}
}
// Function to perform homing of the linear axis
void homeLinearAxis() {
stepper1.setMaxSpeed(8000); // Set maximum speed
stepper1.setSpeed(10000); // Set custom homing speed
while (digitalRead(LIMIT_SWITCH_PIN) == LOW) { // Adjusted limit switch condition
stepper1.moveTo(stepper1.currentPosition() + 20); // Move in the positive direction
stepper1.run();
}
stepper1.setCurrentPosition(maxXPosition / distancePerMicrostepX); // Set position to 200 mm
homingDoneX = true;
Serial.println("X-Axis homing completed.");
}
// Function to perform homing of the paper feeder
void homePaperFeeder() {
stepper2.setMaxSpeed(4000); // Set maximum speed
stepper2.setSpeed(3000); // Set custom homing speed
while (digitalRead(LIMIT_SWITCH_PIN) == LOW) { // Adjusted limit switch condition
stepper2.moveTo(stepper2.currentPosition() + 20); // Move in the positive direction
stepper2.run();
}
// Move an additional 10mm in the positive direction
stepper2.move(10 / distancePerMicrostep); // Convert 10mm to steps
stepper2.runToPosition(); // Wait for the movement to complete
delay(200);
stepper2.move(20 / distancePerMicrostep); // Convert 10mm to steps
stepper2.runToPosition(); // Wait for the movement to complete
// Set position to 0 mm
stepper2.setCurrentPosition(0);
homingDoneY = true;
Serial.println("Paper feeder homing completed.");
}
// Function to process G-code commands
void processGCode(String command) {
if (command.startsWith("G28")) {
// Command to perform homing
homeAllAxes();
} else if (command.startsWith("G1")) {
// Move linear axis
int indexX = command.indexOf('X');
int indexY = command.indexOf('Y');
if (indexX >= 0) {
float posX = command.substring(indexX + 1).toFloat();
// Check if movement is within safe bounds for X-axis
if (posX >= 0 && posX <= maxXPosition) {
commandQueue.push({'X', posX, false});
} else {
Serial.println("Error: Movement out of bounds for X-axis.");
}
}
// Move paper feeder
if (indexY >= 0) {
float posY = command.substring(indexY + 1).toFloat();
// Check if movement is within safe bounds for Y-axis
if (posX >= 0 && posX <= maxYPosition) {
commandQueue.push({'Y', posY, false});
} else {
Serial.println("Error: Movement out of bounds for Y-axis.");
}
}
} else if (command.startsWith("P")) {
commandQueue.push({'P', 0, true});
} else if (command.startsWith("HELLOWORLD")) {
HELLOWORLD();
} else if (command.startsWith("NEIL")) {
NEIL();
} else if (command.startsWith("FAB")) {
THISISFABACADEMY2024();
}
}
void HELLOWORLD() {
// Define starting positions
int startX = 0;
int startY = 0;
// Print "H"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "E"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "L"
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "L"
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "O"
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 10, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 15;
// Print "W"
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 10, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 10, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 10, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "O"
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 10, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "R"
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 10, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "L"
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "D"
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 10, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 10, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
NEIL();
THISISFABACADEMY2024();
// Move pen away from drawing area
commandQueue.push({'Y', 300, false});
commandQueue.push({'X', 180, false});
}
void NEIL() {
// Define starting positions
int startX = 0;
int startY = 30;
// Print "H"
commandQueue.push({'X', startX + 0, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 0, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "I"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
startX += 15;
// Print "N"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "E"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "I" (2nd and 3rd dots)
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY , false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "L"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
// // Move pen away from drawing area
// commandQueue.push({'Y', startY + 300, false});
// commandQueue.push({'X', startX + 180, false});
}
void THISISFABACADEMY2024() {
// Define starting positions
int startX = 0;
int startY = 60;
// Print "T"
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY , false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "H"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "I" (2nd and 4th dots)
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "S" (1st, 2nd, 4th, and 5th dots)
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print space (move pen to the next character position)
startX += 10;
// Print "I" (2nd and 4th dots)
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "S" (1st, 2nd, 4th, and 5th dots)
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
// Move to the next line
startX = 0;
startY += 20;
// Print "F" (1st, 2nd, 3rd, and 5th dots)
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX , false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "A" (1st dot)
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "B" (1st and 2nd dots)
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print space (move pen to the next character position)
startX += 10;
// Print "A"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "C"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "A"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "D" (1st, 4th, and 5th dots)
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "E" (1st and 5th dots)
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "M" (1st, 3rd, and 4th dots)
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
startX += 10;
// Print "Y"
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 5, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
commandQueue.push({'X', startX + 5, false});
commandQueue.push({'Y', startY + 10, false});
commandQueue.push({'P', 0, true});
// // Move pen away from drawing area
// commandQueue.push({'Y', 300, false});
// commandQueue.push({'X', 180, false});
}
// Function to move the linear axis
void moveLinearAxis(float position) {
if (homingDoneX) {
long steps = position / distancePerMicrostepX; // Convert mm to steps
stepper1.moveTo(steps);
} else {
Serial.println("Error: Linear axis homing not completed.");
}
}
// Function to move the paper feeder
void movePaperFeeder(float distance) {
if (homingDoneY) {
long steps = distance / distancePerMicrostep; // Convert mm to steps
stepper2.moveTo(steps);
} else {
Serial.println("Error: Paper feeder homing not completed.");
}
}
// Function to activate the solenoid
void activateSolenoid() {
digitalWrite(SOLENOID_PIN, HIGH);
delay(300);
digitalWrite(SOLENOID_PIN, LOW);
delay(100);
}
// Function to process the command queue
void processCommandQueue() {
if (!commandQueue.empty()) {
MoveCommand cmd = commandQueue.front();
if (stepper1.distanceToGo() == 0 && stepper2.distanceToGo() == 0) {
if (cmd.axis == 'X') {
moveLinearAxis(cmd.position);
} else if (cmd.axis == 'Y') {
movePaperFeeder(cmd.position);
} else if (cmd.solenoid) {
activateSolenoid();
}
commandQueue.pop();
}
}
// Run steppers
stepper1.run();
stepper2.run();
}
Coquiduino Braille Printer by Jorge Alejandro Suarez de Freitas Ferrari is licensed under CC BY-NC 4.0