Code for RTD
const int analogPin = A0;
const int heaterPin = 8;
const float R_known = 100.0;
const float V_in = 5.0;
double setpoint = 150.0; // Desired temperature
double Kp = 0.05; // Proportional gain
double Ki = 0.005; // Integral gain
double prevError = 0;
double integral = 0;
unsigned long lastTime = 0;
void setup() {
Serial.begin(9600);
pinMode(heaterPin, OUTPUT);
}
void loop() {
// Time tracking for dt
unsigned long now = millis();
double dt = (now - lastTime) / 1000.0; // convert ms to seconds
if (dt <= 0) dt = 0.01; // Avoid divide by zero
lastTime = now;
// Read temperature
int adcValue = analogRead(analogPin);
float V_out = (adcValue / 1023.0) * V_in;
double temperature = 36.9457 * V_out - 17.0644;
// PID calculations
double error = setpoint - temperature;
integral += error * dt;
double cutoff_hi = 1 / Ki;
if (error > 0)
cutoff_hi -= (Kp / Ki) * error;
if (integral > cutoff_hi)
integral = cutoff_hi;
if (integral < 0)
integral = 0;
double derivative = (error - prevError) / dt;
double output = Kp * error + Ki * integral;
prevError = error;
// Clamp output to PWM range (0–255)
output = constrain(255 * output, 0, 255);
// Apply PWM to MOSFET
analogWrite(heaterPin, (int)output);
// Serial debug
Serial.print("Temp: ");
Serial.print(temperature);
Serial.print(" °C, PWM: ");
Serial.println(output);
delay(200);
}
Code for X-Y Positioning Stage
#include <math.h> // Needed for round()
const int stepPinX = 3;
const int dirPinX = 4;
const int stepPinY = 5;
const int dirPinY = 6;
const int stepsPerRev = 4000;
const float mmPerRevX = 0.65; // X-axis
const float mmPerRevY = 0.5; // Y-axis
const int delayTime = 200;
float currentPosX = 0.0;
float currentPosY = 0.0;
const float minPosX = -10.0;
const float maxPosX = 10.0;
const float minPosY = -5.0;
const float maxPosY = 5.0;
String inputString = "";
void setup() {
pinMode(stepPinX, OUTPUT);
pinMode(dirPinX, OUTPUT);
pinMode(stepPinY, OUTPUT);
pinMode(dirPinY, OUTPUT);
Serial.begin(9600);
}
void loop() {
if (Serial.available()) {
char c = Serial.read();
if (c == '\n' || c == '\r') {
inputString.trim();
if (inputString.equalsIgnoreCase("commands")) {
printCommandMenu();
} else if (inputString.equalsIgnoreCase("x reset")) {
moveToZero('x');
} else if (inputString.equalsIgnoreCase("y reset")) {
moveToZero('y');
} else if (inputString.equalsIgnoreCase("x zero")) {
currentPosX = 0.0;
Serial.println("X position reset to 0.0 mm (no movement).");
printPosition();
} else if (inputString.equalsIgnoreCase("y zero")) {
currentPosY = 0.0;
Serial.println("Y position reset to 0.0 mm (no movement).");
printPosition();
}
else if (inputString.startsWith("x ") || inputString.startsWith("y ")) {
char axis = tolower(inputString.charAt(0));
String mmString = inputString.substring(2);
mmString.trim();
float mm = mmString.toFloat();
performMovement(axis, mm);
}
else if (isSimultaneousInput(inputString)) {
float xVal = 0.0, yVal = 0.0;
parseSimultaneous(inputString, xVal, yVal);
moveXYSimultaneous(xVal, yVal);
}
else {
Serial.println("Invalid input. Use 'x (mm)', 'y (mm)' or '(x,y)'");
Serial.println("--------------------------------------------");
}
inputString = "";
} else {
inputString += c;
}
}
}
void printCommandMenu() {
Serial.println("On initial start up, ensure that X is zeroed at approximately 15mm on the X-axis micrometer head,");
Serial.println("and Y is zeroed at approximately 6.5 mm on the Y-axis micrometer.");
Serial.println("Commands:");
Serial.println(" x (mm), y (mm)");
Serial.println(" x reset, y reset");
Serial.println(" x zero, y zero");
Serial.println(" or enter: (x,y) for simultaneous XY move");
Serial.println(" type: commands — to show this help");
Serial.println("--------------------------------------------");
}
bool isSimultaneousInput(String s) {
return s.indexOf(',') >= 0 || s.indexOf(' ') >= 0;
}
void parseSimultaneous(String s, float &xVal, float &yVal) {
s.replace(',', ' ');
s.trim();
int sep = s.indexOf(' ');
if (sep >= 0) {
xVal = s.substring(0, sep).toFloat();
yVal = s.substring(sep + 1).toFloat();
}
}
void moveXYSimultaneous(float xMM, float yMM) {
float xNew = constrain(currentPosX + xMM, minPosX, maxPosX);
float yNew = constrain(currentPosY + yMM, minPosY, maxPosY);
xMM = xNew - currentPosX;
yMM = yNew - currentPosY;
long xSteps = round(abs(xMM) / mmPerRevX * stepsPerRev);
long ySteps = round(abs(yMM) / mmPerRevY * stepsPerRev);
long maxSteps = max(xSteps, ySteps);
if (xSteps == 0 && ySteps == 0) {
Serial.println("No movement — at limits or zero input.");
printPosition();
return;
}
Serial.print("Simultaneous move: X ");
Serial.print(xMM);
Serial.print(" mm (");
Serial.print(xSteps);
Serial.print(" steps), Y ");
Serial.print(yMM);
Serial.print(" mm (");
Serial.print(ySteps);
Serial.println(" steps)");
digitalWrite(dirPinX, (xMM >= 0) ? LOW : HIGH);
digitalWrite(dirPinY, (yMM >= 0) ? LOW : HIGH);
long xCounter = 0, yCounter = 0;
for (long i = 0; i < maxSteps; i++) {
if (i * xSteps >= xCounter * maxSteps && xSteps > 0) {
digitalWrite(stepPinX, HIGH); delayMicroseconds(delayTime);
digitalWrite(stepPinX, LOW); delayMicroseconds(delayTime);
xCounter++;
}
if (i * ySteps >= yCounter * maxSteps && ySteps > 0) {
digitalWrite(stepPinY, HIGH); delayMicroseconds(delayTime);
digitalWrite(stepPinY, LOW); delayMicroseconds(delayTime);
yCounter++;
}
}
currentPosX += xMM;
currentPosY += yMM;
printPosition();
}
void performMovement(char axis, float mm) {
float ¤tPos = (axis == 'x') ? currentPosX : currentPosY;
int stepPin = (axis == 'x') ? stepPinX : stepPinY;
int dirPin = (axis == 'x') ? dirPinX : dirPinY;
float mmPerRev = (axis == 'x') ? mmPerRevX : mmPerRevY;
float minLimit = (axis == 'x') ? minPosX : minPosY;
float maxLimit = (axis == 'x') ? maxPosX : maxPosY;
float proposed = currentPos + mm;
if (proposed > maxLimit) {
mm = maxLimit - currentPos;
Serial.print("Clamped ");
Serial.print(axis);
Serial.print(" move to +");
Serial.print(maxLimit, 1);
Serial.println(" mm.");
} else if (proposed < minLimit) {
mm = minLimit - currentPos;
Serial.print("Clamped ");
Serial.print(axis);
Serial.print(" move to ");
Serial.print(minLimit, 1);
Serial.println(" mm.");
}
if (mm == 0.0) {
Serial.println("No movement — at limit or zero input.");
printPosition();
return;
}
digitalWrite(dirPin, (mm >= 0) ? LOW : HIGH);
long steps = round(abs(mm) / mmPerRev * stepsPerRev);
Serial.print("Moving ");
Serial.print(axis);
Serial.print(" by ");
Serial.print(mm);
Serial.print(" mm (");
Serial.print(steps);
Serial.println(" steps)");
for (long i = 0; i < steps; i++) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(delayTime);
digitalWrite(stepPin, LOW);
delayMicroseconds(delayTime);
}
currentPos += mm;
printPosition();
}
void moveToZero(char axis) {
float ¤tPos = (axis == 'x') ? currentPosX : currentPosY;
int stepPin = (axis == 'x') ? stepPinX : stepPinY;
int dirPin = (axis == 'x') ? dirPinX : dirPinY;
float mmPerRev = (axis == 'x') ? mmPerRevX : mmPerRevY;
if (currentPos == 0.0) {
Serial.print(axis);
Serial.println(" already at 0.0 mm.");
printPosition();
return;
}
float mm = -currentPos;
digitalWrite(dirPin, (mm >= 0) ? LOW : HIGH);
long steps = round(abs(mm) / mmPerRev * stepsPerRev);
Serial.print("Returning ");
Serial.print(axis);
Serial.print(" to 0.0 mm (");
Serial.print(steps);
Serial.println(" steps)");
for (long i = 0; i < steps; i++) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(delayTime);
digitalWrite(stepPin, LOW);
delayMicroseconds(delayTime);
}
currentPos = 0.0;
Serial.print(axis);
Serial.println(" reset complete.");
printPosition();
}
void printPosition() {
Serial.print("Current position: (");
Serial.print(currentPosX, 3);
Serial.print(" mm, ");
Serial.print(currentPosY, 3);
Serial.println(" mm)");
Serial.println("--------------------------------------------");
}
}