Skip to content

Instantly share code, notes, and snippets.

@xaxxontech
Last active September 8, 2020 18:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xaxxontech/49f6895f4df0fe2923baab58985fd5b1 to your computer and use it in GitHub Desktop.
Save xaxxontech/49f6895f4df0fe2923baab58985fd5b1 to your computer and use it in GitHub Desktop.
/*
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