Last active
September 8, 2020 18:52
-
-
Save xaxxontech/49f6895f4df0fe2923baab58985fd5b1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
ASCII Serial Commands | |
f - fan on | |
o - fan off | |
1 - heater1 on | |
0 - heater1 off | |
3 - heater2 on | |
2 - heater2 off | |
a - auto temp control 1 start | |
b - auto temp control 2 start | |
n - auto temp control stop, both heaters off | |
4,a - temp setpoint 1, deg C [0-255] | |
5,a - temp setpoint 2, dec C [0-255] | |
p,a - actuator PWM [0-255] | |
u - up actuator | |
d - down actuator | |
s - stop actuator | |
z - zero load cell | |
l,a - load target, lbs [0-255] | |
g - auto down force target control start | |
j - auto down force target control start, always super slow | |
h - auto down force target control stop, actuator off | |
x - get board ID | |
v - toggle verbose | |
y - get version | |
*/ | |
// #include <SPI.h> | |
#include "Adafruit_MAX31855.h" | |
#include "HX711.h" | |
/* Pins */ | |
// thermocouple 1 | |
#define T1MAXDO A3 | |
#define T1MAXCS A4 | |
#define T1MAXCLK A5 | |
#define HEATERRELAY1 9 | |
// thermocouple 2 | |
#define T2MAXDO 5 | |
#define T2MAXCS 6 | |
#define T2MAXCLK A2 | |
#define HEATERRELAY2 10 | |
// hbridge | |
#define PWMA 3 // pwm actuator | |
#define IN1 2 // direction actuator | |
#define IN2 4 // direction actuator | |
#define PWMB 11 // pwm fan | |
#define IN3 7 | |
#define IN4 8 | |
// actuator pot | |
#define ACTUATORPOS A1 | |
// load cell | |
#define DOUT 12 | |
#define CLK A0 | |
// initialize the Thermocouples | |
Adafruit_MAX31855 thermocouple1(T1MAXCLK, T1MAXCS, T1MAXDO); | |
Adafruit_MAX31855 thermocouple2(T2MAXCLK, T2MAXCS, T2MAXDO); | |
// initialize load cell | |
HX711 scale(DOUT, CLK); | |
// command byte buffer | |
const int MAX_BUFFER = 32; | |
int buffer[MAX_BUFFER]; | |
int commandSize = 0; | |
// temp | |
unsigned long nextTemp = 0; | |
const int TEMPINTERVAL = 1000; | |
const double MAXTEMP = 150; | |
bool fanBlowing = false; | |
// temp setpoint control 1 | |
double setPoint1 = 50; // dec C | |
const long DUTYWINDOW1 = 10000; // ms | |
const double DUTYPERCENT1 = 0.5; | |
unsigned long windowStartTime1; | |
bool tempControlActive1 = false; | |
double lastTemp1 = 0; | |
double lastTempDiff1 = 0; | |
bool heating1 = false; | |
// temp setpoint control 2 | |
double setPoint2 = 50; // dec C | |
const long DUTYWINDOW2 = 10000; // ms | |
const double DUTYPERCENT2 = 0.4; | |
unsigned long windowStartTime2; | |
bool tempControlActive2 = false; | |
double lastTemp2 = 0; | |
double lastTempDiff2 = 0; | |
bool heating2 = false; | |
// print output | |
const int OUTPUTINTERVAL = 1000; | |
double nextOutput = 0; | |
// actuator | |
// actuator direction constants | |
#define STOP 0 | |
#define DOWN 1 | |
#define UP 2 | |
int actuatorPWM = 255; | |
const int UPPERLIMIT = 990; // limit switch stop | |
const int LOWERLIMIT = 35; // limit switch stop | |
int actuatorPosition = 0; | |
int actuatorDirection = STOP; | |
boolean pulseMode = false; | |
unsigned long nextPositionCheck = 0; | |
const int POSITIONCHECKINTERVAL = 10; // ms | |
boolean alwaysPulse = false; | |
// loadcell | |
// load cell calibration | |
#define CALIBRATION_FACTOR -7050.0 //This value is obtained using the SparkFun_HX711_Calibration sketch | |
double targetLoad = 25.0; // arbitrary, can be user set | |
const double MAXLOAD = 170.0; // lbs | |
double pulseThreshold = targetLoad*0.6; // lbs | |
double loadTolerance = targetLoad*0.05; // lbs | |
// auto force control | |
unsigned long nextLoadCheck = 0; | |
const int LOADCHECKINTERVAL = 10; // ms | |
double currentload = 0; | |
boolean forceControlActive = false; | |
unsigned long time = 0; | |
boolean verbose = false; | |
const int HANDSHAKEDELAY = 3000; | |
unsigned long startup = 0; | |
void setup() { | |
Serial.begin(115200); | |
Serial.println("<reset>"); | |
pinMode(HEATERRELAY1, OUTPUT); | |
heater1off(); | |
pinMode(HEATERRELAY2, OUTPUT); | |
heater2off(); | |
pinMode(PWMA, OUTPUT); | |
pinMode(PWMB, OUTPUT); | |
pinMode(IN1, OUTPUT); | |
pinMode(IN2, OUTPUT); | |
pinMode(IN3, OUTPUT); | |
pinMode(IN4, OUTPUT); | |
pinMode(ACTUATORPOS, INPUT); | |
// fan fwd, off | |
fanOff(); | |
digitalWrite(IN3, HIGH); // only goes one direction | |
digitalWrite(IN4, LOW); // only goes one direction | |
// actuator off | |
actuatorStop(); | |
// load cell zero | |
scale.set_scale(CALIBRATION_FACTOR); //This value is obtained by using the SparkFun_HX711_Calibration sketch | |
scale.tare(); //Assuming there is no weight on the scale at start up, reset the scale to 0 | |
// delay(500); // wait for above commands to complete? | |
startup = millis() + HANDSHAKEDELAY; | |
} | |
void loop() { | |
time = millis(); | |
if( Serial.available() > 0) { | |
manageCommand(); | |
} | |
if (time < startup) return; | |
if (time >= nextTemp) { | |
nextTemp = time + TEMPINTERVAL; | |
getTemp1(); | |
getTemp2(); | |
if (tempControlActive1) { temp1Control(); } | |
if (tempControlActive2) { temp2Control(); } | |
} | |
if (time >= nextOutput) { | |
nextOutput = time + OUTPUTINTERVAL; | |
if (!verbose) nextOutput = time + OUTPUTINTERVAL/2; | |
printOutput(); | |
} | |
if (time >= nextLoadCheck) { | |
checkLoad(); | |
nextLoadCheck = time + LOADCHECKINTERVAL; | |
if (forceControlActive) downForceControl(); | |
} | |
if (time >= nextPositionCheck) { | |
actuatorPosition = getActuatorPosition(); | |
// checkPosition(); | |
nextPositionCheck = time + POSITIONCHECKINTERVAL; | |
} | |
} | |
void manageCommand() { | |
int input = Serial.read(); | |
buffer[commandSize] = input; | |
commandSize ++; | |
if((input == 13 || input == 10) && buffer[0] != 'p' && buffer[0] != 'l' && buffer[0] != '4' && buffer[0] != '5') { | |
parseCommand(); | |
commandSize = 0; | |
} | |
else if ((buffer[0] == 'p' || buffer[0] != 'l' || buffer[0] != '4' || buffer[0] != '5') && commandSize == 2) { | |
parseCommand(); | |
commandSize = 0; | |
} | |
} | |
void parseCommand(){ | |
if (buffer[0] == '1') heater1on(); | |
else if (buffer[0] == '3') heater2on(); | |
else if (buffer[0] == '0') heater1off(); | |
else if (buffer[0] == '2') heater2off(); | |
else if (buffer[0] == 'f') fanOn(); | |
else if (buffer[0] == 'o') fanOff(); | |
else if (buffer[0] == 'a') temp1ControlStart(); | |
else if (buffer[0] == 'b') temp2ControlStart(); | |
else if (buffer[0] == 'n') tempControlStop(); | |
else if (buffer[0] == '4') { | |
setPoint1 = (double) buffer[1]; | |
if (setPoint1 > MAXTEMP) setPoint1 = MAXTEMP; | |
} | |
else if (buffer[0] == '5') { | |
setPoint2 = (double) buffer[1]; | |
if (setPoint2 > MAXTEMP) setPoint2 = MAXTEMP; | |
} | |
// actuator | |
else if (buffer[0] == 'p') { // pwm set (unused?) | |
actuatorPWM = buffer[1]; | |
analogWrite(PWMA, actuatorPWM); | |
} | |
else if (buffer[0] == 'u') { | |
actuatorUp(); | |
actuatorDirection = UP; | |
downForceStop(); | |
} | |
else if (buffer[0] == 'd') { | |
actuatorDown(); | |
actuatorDirection = DOWN; | |
} | |
else if (buffer[0] == 's') { | |
actuatorStop(); | |
actuatorDirection = STOP; | |
downForceStop(); | |
} | |
// load cell | |
else if (buffer[0] == 'z') scale.tare(); | |
else if (buffer[0] == 'l') { | |
targetLoad = (double) buffer[1]; | |
if (targetLoad > MAXLOAD) targetLoad = MAXLOAD; | |
pulseThreshold = targetLoad*0.6; // lbs | |
loadTolerance = targetLoad*0.05; // lbs | |
Serial.print("target load set to :"); | |
Serial.println(targetLoad); | |
// Serial.print("<"); | |
// Serial.print(targetLoad); | |
// Serial.println(">"); | |
} | |
else if (buffer[0] == 'g') { | |
downForceStart(); | |
alwaysPulse = false; | |
} | |
else if (buffer[0] == 'h') downForceStop(); | |
else if (buffer[0] == 'j') { | |
alwaysPulse = true; | |
pulseMode = true; | |
downForceStart(); | |
} | |
else if (buffer[0] == 'x') Serial.println("<id::energybarpress>"); | |
else if (buffer[0] == 'v') { | |
if (verbose) verbose = false; | |
else { | |
verbose = true; | |
Serial.println("verbose true"); | |
} | |
} | |
else if(buffer[0] == 'y') version(); | |
} | |
void heater1on() { | |
if (lastTemp1 >= MAXTEMP) { | |
Serial.println("Max temp 1 reached"); | |
return; | |
} | |
digitalWrite(HEATERRELAY1, HIGH); | |
if (!heating1) Serial.println("heater1 ON"); | |
heating1 = true; | |
} | |
void heater2on() { | |
if (lastTemp2 >= MAXTEMP) { | |
Serial.println("Max temp 2 reached"); | |
return; | |
} | |
digitalWrite(HEATERRELAY2, HIGH); | |
if (!heating2) Serial.println("heater2 ON"); | |
heating2 = true; | |
} | |
void heater1off() { | |
digitalWrite(HEATERRELAY1, LOW); | |
if (heating1) Serial.println("heater 1 OFF"); | |
heating1 = false; | |
} | |
void heater2off() { | |
digitalWrite(HEATERRELAY2, LOW); | |
if (heating2) Serial.println("heater 2 OFF"); | |
heating2 = false; | |
} | |
void fanOn() { | |
analogWrite(PWMB, 255); | |
if (!fanBlowing) Serial.println("fan ON"); | |
fanBlowing = true; | |
} | |
void fanOff() { | |
analogWrite(PWMB, 0); | |
if (fanBlowing) Serial.println("fan OFF"); | |
fanBlowing = false; | |
} | |
void temp1ControlStart() { | |
tempControlActive1 = true; | |
windowStartTime1 = time; | |
Serial.println("temp 1 control ON"); | |
} | |
void temp2ControlStart() { | |
tempControlActive2 = true; | |
windowStartTime2 = time; | |
Serial.println("temp 2 control ON"); | |
} | |
void tempControlStop() { | |
tempControlActive1 = false; | |
tempControlActive2 = false; | |
Serial.println("temp control 1&2 OFF"); | |
heater1off(); | |
heater2off(); | |
} | |
void temp1Control() { | |
if (isnan(lastTemp1)) return; | |
if (time - windowStartTime1 > DUTYWINDOW1) { //time to shift the Relay Window | |
windowStartTime1 = time; | |
} | |
double target = setPoint1; | |
if (lastTempDiff1 > 0.25) target -= 3; | |
else if (lastTempDiff1 <= 0) target += 10; | |
double gap = target-lastTemp1; // current temperature gap to setpoint | |
double dutyCycle = 1; | |
if (gap < 10) dutyCycle = 0; // completely off when almost there (includes overshoots) | |
else if (gap < 20) dutyCycle = (gap -10)/10; // slow down when getting close | |
double output = dutyCycle * DUTYWINDOW1 * DUTYPERCENT1; | |
if (output > time- windowStartTime1 && lastTemp1 < setPoint1) heater1on(); | |
else heater1off(); | |
} | |
void temp2Control() { | |
if (isnan(lastTemp2)) return; | |
if (time - windowStartTime2 > DUTYWINDOW2) { //time to shift the Relay Window | |
windowStartTime2 = time; | |
} | |
double target = setPoint2; | |
if (lastTempDiff2 > 0.25) target -= 3; | |
else if (lastTempDiff2 <= 0) target += 10; | |
double gap = target-lastTemp2; // current temperature gap to setpoint | |
double dutyCycle = 1; | |
if (gap < 10) dutyCycle = 0; // completely off when almost there (includes overshoots) | |
else if (gap < 20) dutyCycle = (gap -10)/10; // slow down when getting close | |
double output = dutyCycle * DUTYWINDOW2 * DUTYPERCENT2; | |
if (output > time- windowStartTime2 && lastTemp2 < setPoint2) heater2on(); | |
else heater2off(); | |
} | |
void getTemp1() { | |
double c = thermocouple1.readCelsius(); | |
if (isnan(c)) heater1off(); | |
lastTempDiff1 = c - lastTemp1; | |
lastTemp1 = c; | |
if (lastTemp1 >= MAXTEMP) heater1off(); | |
} | |
void getTemp2() { | |
double c = thermocouple2.readCelsius(); | |
if (isnan(c)) heater2off(); | |
lastTempDiff2 = c - lastTemp2; | |
lastTemp2 = c; | |
if (lastTemp2 >= MAXTEMP) heater2off(); | |
} | |
void actuatorStop() { | |
analogWrite(PWMA, 0); | |
} | |
void actuatorUp() { | |
digitalWrite(IN1, LOW); | |
digitalWrite(IN2, HIGH); | |
analogWrite(PWMA, actuatorPWM); | |
} | |
void actuatorDown() { | |
digitalWrite(IN1, HIGH); | |
digitalWrite(IN2, LOW); | |
analogWrite(PWMA, actuatorPWM); | |
} | |
void checkLoad() { // load safety, down or up, auto and non auto | |
currentload = scale.get_units() * -1; // -1 converts down force to positive | |
double maxload = MAXLOAD; | |
if (!forceControlActive) maxload = pulseThreshold; | |
if (currentload > maxload + loadTolerance && actuatorDirection != UP) // down | |
// || currentload < -pulseThreshold) // up | |
{ | |
actuatorStop(); | |
actuatorDirection = STOP; | |
Serial.print("load limit exceeded"); | |
} | |
} | |
void downForceStart() { | |
forceControlActive = true; | |
} | |
void downForceStop() { | |
forceControlActive = false; | |
if (actuatorDirection == DOWN) { | |
actuatorStop(); | |
actuatorDirection = STOP; | |
} | |
alwaysPulse = false; | |
} | |
void downForceControl() { // auto force setpoint, downward movement only | |
if (currentload > pulseThreshold) pulseMode = true; | |
else if (pulseMode && !alwaysPulse) { // cancel pulseMode | |
pulseMode = false; | |
// resume moving if necessary | |
if (actuatorDirection == DOWN) actuatorDown(); | |
} | |
if (currentload < targetLoad - loadTolerance && actuatorDirection != DOWN) { // start | |
actuatorDown(); | |
actuatorDirection = DOWN; | |
} | |
else if (currentload > targetLoad - loadTolerance && actuatorDirection != STOP) { | |
actuatorStop(); | |
actuatorDirection = STOP; | |
Serial.println("load target reached"); | |
} | |
else if (pulseMode && currentload < targetLoad - loadTolerance) pulse(DOWN); | |
else if (pulseMode && currentload > targetLoad + loadTolerance) pulse(UP); | |
} | |
void pulse(int dir) { | |
int DELAY = 30; | |
if (targetLoad <= 25) DELAY = 10; | |
else if (targetLoad >25 && targetLoad <65) DELAY = 20; | |
if (dir == DOWN) { | |
actuatorDown(); | |
delay(DELAY); | |
actuatorStop(); | |
delay(100); | |
} | |
else { // dir == UP | |
actuatorUp(); | |
delay(10); | |
actuatorStop(); | |
delay(500); | |
} | |
} | |
void checkPosition() { // unused | |
int TOLERANCE = 10; | |
if (actuatorDirection == STOP) return; | |
if ( (actuatorPosition > UPPERLIMIT - TOLERANCE && actuatorDirection == UP) || | |
(actuatorPosition < LOWERLIMIT + TOLERANCE && actuatorDirection == DOWN) ) { | |
actuatorStop(); | |
actuatorDirection = STOP; | |
// forceControlActive = false; | |
Serial.println("position limit reached"); | |
} | |
} | |
int getActuatorPosition() { | |
int c = analogRead(ACTUATORPOS); | |
return c; | |
} | |
void printOutput() { | |
if (!verbose) { | |
printShortOutput(); | |
return; | |
} | |
Serial.print("actuator direction: "); | |
if (actuatorDirection == STOP) Serial.println("STOP"); | |
else if (actuatorDirection == UP) Serial.println("UP"); | |
else if (actuatorDirection == DOWN) Serial.println("DOWN"); | |
Serial.print("current load: "); | |
Serial.println(currentload); | |
Serial.print("target load: "); | |
Serial.println(targetLoad); | |
Serial.print("force control: "); | |
if (!forceControlActive) Serial.println("OFF"); | |
else Serial.println("ACTIVE"); | |
Serial.print("actuator position: "); | |
Serial.println(actuatorPosition); | |
// temps | |
Serial.print("Zone 1: internal = "); | |
Serial.print(thermocouple1.readInternal()); | |
Serial.print(", C = "); | |
Serial.print(lastTemp1); | |
if (tempControlActive1) { | |
Serial.print(", temp conrol 1 ON, setpoint1: "); | |
Serial.print(setPoint1); | |
} | |
if (heating1) Serial.print(", heater1 ON"); | |
Serial.println(""); | |
Serial.print("Zone 2: internal = "); | |
Serial.print(thermocouple2.readInternal()); | |
Serial.print( ", C = "); | |
Serial.print(lastTemp2); | |
if (tempControlActive2) { | |
Serial.print(", temp conrol 2 ON, setpoint2: "); | |
Serial.print(setPoint2); | |
} | |
if (heating2) Serial.print(", heater2 ON"); | |
Serial.println(""); | |
if (fanBlowing) Serial.println("fan ON"); | |
else Serial.println("fan OFF"); | |
Serial.println(" "); | |
} | |
void printShortOutput() { | |
Serial.print("<"); | |
if (actuatorDirection == DOWN || forceControlActive) Serial.print("DOWN"); | |
else if (actuatorDirection == UP) Serial.print("UP"); | |
else Serial.print("STOP"); | |
Serial.print(" "); | |
Serial.print(currentload); // current force lbs | |
Serial.print(" "); | |
Serial.print(targetLoad); // force target lbs | |
Serial.print(" "); | |
Serial.print(actuatorPosition); // actuator position 0-1024 UP=+ | |
Serial.print(" "); | |
Serial.print(thermocouple1.readInternal()); // ambient temp deg C | |
Serial.print(" "); | |
Serial.print(lastTemp1); // upper temp deg C | |
Serial.print(" "); | |
Serial.print(setPoint1); // upper setpoint deg C | |
Serial.print(" "); | |
if (tempControlActive1) Serial.print("YES"); // temp control active YES/NO | |
else Serial.print("NO"); | |
Serial.print(" "); | |
Serial.print(lastTemp2); // lower temp dec C | |
Serial.print(" "); | |
Serial.print(setPoint2); // lower setpoint deg C | |
Serial.print(" "); | |
if (fanBlowing) Serial.print("ON"); // fan ON/OFF | |
else Serial.print("OFF"); | |
Serial.print(" "); | |
if (heating1) Serial.print("ON"); // heater1 ON/OFF | |
else Serial.print("OFF"); | |
Serial.print(" "); | |
if (heating2) Serial.print("ON"); // heater2 ON/OFF | |
else Serial.print("OFF"); | |
Serial.println(">"); | |
} | |
void version() { | |
Serial.println("<version:1.01>"); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment