// Version 6.3
// 05/31/2021
// Auto, Time, Joul and Pause modes.
// Streamlined Measure_A and Measure_V functions.
// Streamlined Measure_T by creating Convert_T.
// Added Measure_T back into Anneal_Sub.
// Added Convert_T to Error_Loop.
// Removed elapsed anneal time update durring Anneal_Sub.
// Function run time value comments updated.
// Start-up now Program 0 AUTO.
#include <Wire.h>
#include <EEPROM.h>
#include <elapsedMillis.h>
#include <JC_Button.h>
#include <OneWire.h>
#include "ABlocks_LiquidCrystal_I2C.h"
boolean b_Error = false;
char Print_Buffer[8] = "";
double Amperage = 0;
double Amp_Max = 0;
double Voltage = 0;
double Joules = 0;
const double Max_Amperes = 27.5;
const double V_Constant = 8.2750; // For resistors 51K and 6.8K, 57.8k/6.8k = 8.5 adjust as needed for accuracy
int Anneal_Time = 2500;
int Case_Program_Num = 0;
int Actual_Time = 0;
int Joule_Time = 0;
int Mode = 0;
int Anneal_Energy = 1200;
int Glow = 1023;
int Glow_Set = 50; // 1023 is dark, max glow is 0
int16_t C_Temp;
int16_t Last_Temp = 20;
const int A_Constant = 530; // Adjust as needed for accuracy
const int ls_Case_Name_size = 10;
const int Test_Anneal_Time = 2500;
const int Case_Settle = 500;
const int Drop_Delay = 10;
const int Door_Close = 10;
const int Detector_Threshold = 500; // Sets the detector trigger threshold of the IR receiver. 1000 = high
const int Fan_On_Temp = 120;
const int Test_Anneal_Energy = 1200;
const int Case_Glow_Program_Num = 20; // ls_Case_Name_size + 10
String s_Case_Text;
String s_Error_Text;
String s_Mode_Text;
//************************THIS IS WHERE YOU CUSTOMIZE CARTRIDGE CASE NAMES*****
//************************ **
String ls_Case_Name[10] = { // 11 characters maximum **
String("*AUTO MODE*"), // **
String("NATO 5.56 "), // **
String("6.5 PRC "), // **
String("300 WBY "), // **
String("Four "), // **
String("Five "), // **
String("Six "), // **
String("Seven "), // **
String("Eight "), // **
String("Test ")}; // **
//*****************************************************************************
//*****************************************************************************
LiquidCrystal_I2C lcd(0x27,20,4);
Button
Tm_Up(2), // Add time
Tm_Dn(3), // Subtract time
Pgm_Up(4), // Next program
Pgm_Dn(5), // Previous program
Int_Err(6); // Interupt anneal and clear error
elapsedMillis Annealing; // Used to measure total annealing time
elapsedMillis Measuring; // Used to calculate joules
void fnc_EEPROM_updateDouble(int ee, double value) {
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++)
EEPROM.update(ee++, *p++);
}
double fnc_EEPROM_readDouble(int ee) {
double value = 0.0;
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return value;
}
void EEPROM_Initialize() {
fnc_EEPROM_updateDouble((int)(0*4),3000);
fnc_EEPROM_updateDouble((int)(1*4),3000);
fnc_EEPROM_updateDouble((int)(2*4),3000);
fnc_EEPROM_updateDouble((int)(3*4),3000);
fnc_EEPROM_updateDouble((int)(4*4),3000);
fnc_EEPROM_updateDouble((int)(5*4),3000);
fnc_EEPROM_updateDouble((int)(6*4),3000);
fnc_EEPROM_updateDouble((int)(7*4),3000);
fnc_EEPROM_updateDouble((int)(8*4),3000);
fnc_EEPROM_updateDouble((int)(255*4),(-537537));
}
double Measure_A() { // 1m sec to run function, 30 amp sensor returns 66mA/V; 20 amp sensor, 100mA/V, both OA = 2.5V input
Amperage = (analogRead(A1) - A_Constant) * 5 / 67.584; // Sensor returns 66mV/A (0.66x1024=67.584) Starts at 2.5V(511), adjust A_Constatnt for accuracy
if (Amperage >= 0) {
if (Amperage > Amp_Max)
Amp_Max = Amperage;
}
else
Amperage = 0;
}
double Measure_V() { // 1m sec to run function, Voltage sensor 6.8K and 51K reistors
Voltage = analogRead(A0) * V_Constant * 5 / 1024;
if (Voltage < 0)
Voltage = 0;
}
int16_t Measure_T(int x, byte start) { // 6m sec to run function, DS18B20 Temperature Sensor 4.7K pullup rsistor on middle pin to +5
OneWire ds(7);
byte i;
byte data[2];
do {
ds.reset();
ds.write(0xCC); // Skip command
ds.write(0xBE); // Read 1st 2 bytes of Scratchpad
for (i=0; i<2; i++) data[i] = ds.read();
C_Temp = (data[1]<<8) | data[0];
C_Temp >>= 4;
if (data[1] & 128) C_Temp |= 61440;
if (data[0] & 8) ++C_Temp;
ds.reset();
ds.write(0xCC); //Skip command
ds.write(0x44,1); //Start conversion, assuming 5V connected
if (start) delay(1000);
}
while
(start--);
if (C_Temp >= 82) { // ZVS PCB opperating temperature -58F to 230F (-50C to 110C)
digitalWrite(8,LOW);
digitalWrite(10,HIGH);
lcd.setCursor(17,1);
lcd.print("MAX");
s_Error_Text = String (" TEMP HIGH ERROR! ");
b_Error = true;
}
}
void Convert_T() { // 7m sec to run function
Measure_T(7,0);
double F_Temp;
if (C_Temp <= Last_Temp - 20) //check for bogus data
return;
Last_Temp = C_Temp;
F_Temp = C_Temp * 9 / 5 + 32; // Convert C to F adjust as needed for accuracy
if
(F_Temp >= Fan_On_Temp) digitalWrite(10,HIGH); // Turn fan on if temp gets high
else
digitalWrite(10,LOW);
lcd.setCursor(17,1);
dtostrf(F_Temp,3,0,Print_Buffer);
lcd.print(Print_Buffer);
}
void Anneal_Sub() { // 1m sec to run function witout Measure_T, 6m sec with Measure_T
Int_Err.read();
Measure_T(7,0);
Measure_A();
Measure_V();
Glow = analogRead(A3);
Joule_Time = Measuring;
Measuring = Measuring - Joule_Time;
Joules = Joules + (Voltage * Amperage * Joule_Time / 1000);
//lcd.setCursor(3,2); // These 3 update timer durring anneal
//dtostrf(float(Annealing)/1000,4,2,Print_Buffer); // taking 8m sec to execute and were
//lcd.print(Print_Buffer); // removed to speed-up Anneal_Sub
// Anneal manual button interrupt
if (Int_Err.wasPressed()) {
s_Error_Text = String (" ANNEAL INTERUPTED! ");
b_Error = true;
return;
}
// Anneal over max amperes interrupt
if (Amperage >= Max_Amperes) {
s_Error_Text = String (" AMP HIGH ERROR ");
b_Error = true;
}
}
void Anneal_Main() {
//int Func_Time; // Used for computing time of a function
b_Error = false;
Joules = 0;
Amp_Max = 0;
Glow = 1023;
Convert_T();
delay(Case_Settle);
digitalWrite(8,HIGH); // SSR relay on
Annealing = 0;
Measuring = 0;
lcd.setCursor(0,3);
lcd.print(" ANNEALING CASE ");
if (Case_Program_Num == 0) {
while (Glow > Glow_Set && !b_Error) {
//elapsedMillis Timer; // Used for computing time of a function
Anneal_Sub();
//Func_Time = Timer; // Used for computing time of a function
//s_Error_Text = Func_Time; // Used for computing time of a function
//b_Error = true; // Used for computing time of a function
}
}
else if (Mode == 1) {
while ((Annealing <= Anneal_Time - 10) && !b_Error) // Skip last 10m sec to avoid overshoot
Anneal_Sub();
while (Annealing < Anneal_Time && !b_Error)
delay(1);
}
else
while ((Joules < Anneal_Energy - 2) && !b_Error) // Skip last 2J to avoid overshoot
Anneal_Sub();
// Anneal complete
digitalWrite(8,LOW);
Joule_Time = Measuring;
Actual_Time = Annealing;
Joules = Joules + (Voltage * Amperage * Joule_Time / 1000);
lcd.setCursor(15,2);
dtostrf(Joules,4,0,Print_Buffer);
lcd.print(Print_Buffer);
lcd.setCursor(3,2);
dtostrf(float(Actual_Time)/1000,4,2,Print_Buffer);
lcd.print(Print_Buffer);
lcd.setCursor(9,1);
dtostrf(Amp_Max,3,1,Print_Buffer);
lcd.print(Print_Buffer);
lcd.setCursor(10,2);
dtostrf(Glow,4,0,Print_Buffer);
lcd.print(Print_Buffer);
Convert_T();
if (b_Error)
Error_Loop();
// Case detector
do {
digitalWrite(9,HIGH); // Trap door relay open
lcd.setCursor(0,3);
lcd.print(" *****CASE JAM***** ");
}
while
(analogRead(A2) < Detector_Threshold);
delay(Drop_Delay);
digitalWrite(9,LOW); // Trap door relay close
delay(Door_Close);
lcd.setCursor(0,3);
lcd.print(" WAITING FOR CASE ");
}
void Error_Loop() {
delay(26);
do {
lcd.setCursor(0,3);
lcd.print(s_Error_Text);
Convert_T();
Int_Err.read();
} while
(!Int_Err.wasPressed());
b_Error = false;
delay(26);
}
void Anneal_Set() { // Set Anneal Time
if (Case_Program_Num == 0) {
Glow_Set = fnc_EEPROM_readDouble(Case_Glow_Program_Num*4);
return;
}
if (Mode == 1) {
if (Case_Program_Num != (ls_Case_Name_size - 1))
Anneal_Time = fnc_EEPROM_readDouble(Case_Program_Num*4);
else
Anneal_Time = Test_Anneal_Time;
}
else // Set Anneal Energy
if (Case_Program_Num != (ls_Case_Name_size - 1))
Anneal_Energy = fnc_EEPROM_readDouble((Case_Program_Num + 10)*4);
else
Anneal_Energy = Test_Anneal_Energy;
}
void Anneal_Change() {
if (Case_Program_Num == 0) {
if (fnc_EEPROM_readDouble(Case_Glow_Program_Num*4) != Glow_Set)
fnc_EEPROM_updateDouble((int)(Case_Glow_Program_Num*4),Glow_Set);
return;
}
if (Mode == 1) {// Anneal Time Change
if ((fnc_EEPROM_readDouble(Case_Program_Num*4) != Anneal_Time) && (Case_Program_Num != (ls_Case_Name_size - 1)))
fnc_EEPROM_updateDouble((int)(Case_Program_Num*4),Anneal_Time);
}
else // Anneal_Energy_Change()
if ((fnc_EEPROM_readDouble((Case_Program_Num + 10)*4) != Anneal_Energy) && (Case_Program_Num != (ls_Case_Name_size - 1)))
fnc_EEPROM_updateDouble((int)((Case_Program_Num + 10)*4),Anneal_Energy);
}
void Display() { // 52m sec to run funtion
s_Case_Text = ls_Case_Name[(Case_Program_Num)];
lcd.setCursor(0,0);
lcd.print(Case_Program_Num);
lcd.setCursor(2,0);
lcd.print(s_Case_Text);
if (Case_Program_Num == 0) {
lcd.setCursor(14,0);
lcd.print("G=");
dtostrf(Glow_Set,4,0,Print_Buffer);
}
else if (Mode == 1) {
lcd.setCursor(14,0);
lcd.print("T=");
dtostrf(float(Anneal_Time)/1000,4,2,Print_Buffer);
}
else {
lcd.setCursor(14,0);
lcd.print("J=");
dtostrf(Anneal_Energy,4,0,Print_Buffer);
}
lcd.setCursor(16,0);
lcd.print(Print_Buffer);
Measure_V();
lcd.setCursor(2,1);
dtostrf(Voltage,3,1,Print_Buffer);
lcd.print(Print_Buffer);
Convert_T();
if (b_Error)
Error_Loop();
}
void setup() {
if ((fnc_EEPROM_readDouble((int)(255*4)) != -537537))
EEPROM_Initialize();
Tm_Up.begin(); // Pin 2 Blue, Add time
Tm_Dn.begin(); // Pin 3 Purple, Subtract time
Pgm_Up.begin(); // Pin 4 Yellow, Next program
Pgm_Dn.begin(); // Pin 5 Green, Previous program
Int_Err.begin(); // Pin 6 Brown, Interupt anneal and clear error
Measure_T(7,1); // Pin 7 Temperature sensor
pinMode(8,OUTPUT); // Pin 8 SSR control
pinMode(9,OUTPUT); // Pin 9 Trap door control
pinMode(10,OUTPUT);// Pin 10 Fan
pinMode(A0,INPUT); // Pin A0 Measure voltage
pinMode(A1,INPUT); // Pin A1 Measure Amperage
pinMode(A2,INPUT); // Pin A2 IR break beam
pinMode(A3,INPUT); // Pin A3 Flame sensor
lcd.begin();
lcd.noCursor();
lcd.clear();
lcd.display();
lcd.setCursor(1,0);
lcd.print(":");
lcd.setCursor(14,0);
lcd.print("T=");
lcd.setCursor(0,1);
lcd.print("V=");
lcd.setCursor(7,1);
lcd.print("A=");
lcd.setCursor(14,1);
lcd.print("F=");
lcd.setCursor(0,2);
lcd.print("Ta=");
lcd.setCursor(19,2);
lcd.print("J");
lcd.setCursor(8,2);
lcd.print("G=");
lcd.setCursor(0,3);
lcd.print(" WAITING FOR CASE ");
digitalWrite(8,LOW); // Annealer power SSR
digitalWrite(9,LOW); // Trap Door
digitalWrite(10,LOW); // Fan
}
void loop() {
Display();
Tm_Up.read(); // Pin 2 Add time
Tm_Dn.read(); // Pin 3 Subtract time
Pgm_Up.read(); // Pin 4 Next program
Pgm_Dn.read(); // Pin 5 Previous program
Int_Err.read(); // Pin 6 Cycle Mode, interupt anneal and clear error
// Interup button pressed cycle mode, Mode 0 pause, Mode 1 time anneal, Mode 2 joule anneal
if (Int_Err.wasPressed()) {
if (Case_Program_Num == 0) {
if (Mode != 0)
Anneal_Change();
if (++Mode > 1)
Mode = 0;
}
else {
if (Mode != 0) // Changing out of modes 1-3 check for value change
Anneal_Change();
if (++Mode > 2) // Cycle from Mode 2 back to Mode 0
Mode = 0;
if (Mode == 2) // Going from Mode 1 to Mode 2, set Joule value from EEPROM
Anneal_Set();
}
Display();
}
if (Mode == 0) {
s_Error_Text = String (" PAUSE MODE ");
Error_Loop();
Mode = 1;
Anneal_Set(); // Going from Mode 0 to Mode 1, set Time value from EEPROM
lcd.setCursor(0,3);
lcd.print(" WAITING FOR CASE ");
}
// Up time/energy button pressed Pin 2
if (Tm_Up.wasPressed()) {
if (Mode == 1 && Case_Program_Num != 0) { //Max anneal time 9.99 seconds
Anneal_Time = Anneal_Time + 50;
if (Anneal_Time > 9950)
Anneal_Time = 9990;
}
else if (Mode == 2 && Case_Program_Num != 0) { //Max anneal energy 9999 jouls
Anneal_Energy = Anneal_Energy + 5;
if (Anneal_Energy > 9999)
Anneal_Energy = 9999;
}
else { // Max Glow_Set 1023
if (++Glow_Set > 1023)
Glow_Set = 1023;
}
Display();
}
// Up time/energy button held Pin 2
while (Tm_Up.pressedFor(750)) { //Max anneal time 9.99 seconds
if (Mode == 1 && Case_Program_Num != 0) { //Max anneal time 9.99 seconds
Anneal_Time = Anneal_Time + 50;
if (Anneal_Time > 9950)
Anneal_Time = 9990;
}
else if (Mode == 2 && Case_Program_Num != 0) { //Max anneal energy 9999 jouls
Anneal_Energy = Anneal_Energy + 5;
if (Anneal_Energy > 9999)
Anneal_Energy = 9999;
}
else { // Max Glow_Set 1023
if (++Glow_Set > 1023)
Glow_Set = 1023;
}
Display();
delay(50);
Tm_Up.read();
}
// Down time/energy button pressed Pin 3
if (Tm_Dn.wasPressed()) { //Min anneal time 0.25 seconds
if (Mode == 1 && Case_Program_Num != 0) {
Anneal_Time = Anneal_Time - 50;
if (Anneal_Time <= 250)
Anneal_Time = 250;
}
else if (Mode == 2 && Case_Program_Num != 0) { //Min anneal energy 500 jouls
Anneal_Energy = Anneal_Energy - 5;
if (Anneal_Energy <= 100)
Anneal_Energy = 100;
}
else { //Min Glow_Set 0
if (--Glow_Set < 0)
Glow_Set = 0;
}
Display();
}
// Down time/energy button held Pin 3
while (Tm_Dn.pressedFor(750)) {
if (Mode == 1 && Case_Program_Num != 0) {
Anneal_Time = Anneal_Time - 50;
if (Anneal_Time <= 250)
Anneal_Time = 250;
}
else if (Mode == 2 && Case_Program_Num != 0) { //Min anneal energy 500 jouls
Anneal_Energy = Anneal_Energy - 5;
if (Anneal_Energy <= 100)
Anneal_Energy = 100;
}
else { // Min Glow_Set 0
if (--Glow_Set < 0)
Glow_Set = 0;
}
Display();
delay(50);
Tm_Dn.read();
}
// Up program button pressed Pin 4
if (Pgm_Up.wasPressed()) {
Anneal_Change();
if ((++Case_Program_Num > (ls_Case_Name_size - 1)))
Case_Program_Num = 0;
Anneal_Set();
Display();
}
// Down program button pressed Pin 5
if (Pgm_Dn.wasPressed()) {
Anneal_Change();
if (--Case_Program_Num < 0)
Case_Program_Num = (ls_Case_Name_size - 1);
Anneal_Set();
Display();
}
// Case detector IR Emitter diode + 150 Ohm reistor to 5V, IR Receiver Diode + 10K to gnd
if (analogRead(A2) < Detector_Threshold)
Anneal_Main();
}