I am currently working on a heating stage project and I wanted to know if I can use a 12V AC Adapter to convert 120VAC to 12VDC to power my Arduino Uno Rev3 with its power barrel. The adapter is rated for 12 V and 850 mA. I also have one that is 9 V and 750 mA as well. I am trying to plug in the adapter into the barrel and use the Vin to power a polyimide heating element and use the 5V pin on the Arduino to power a LCD display, encoder, and Max675 module for an encoder. Is the 12V AC adapter safe to use and is the Arduino able to power components using Vin and 5V pin? I have attached a schematic to explain how the circuit is wired. It is based on a Youtube video for PID control.
I also am having issues with my rotary encoder. I am using a 360 Degree Rotary Encoder Module KY-040 but when I have it running only using the 5V pin on the Arduino the menu on the LCD menu constantly change while increasing the PID constants.
Here is my code:
/* Project Details:
* The heating stage device will use an Arduino as a PID controller to regulate the heat liquid crystal samples will use.
* The device will use two polyimide heating elements to apply heat to the top and bottom of the sample.
* The sample holding case will be made of aluminum for its good conductive properties.
* It is assumed that both sides will have the same temperature, but an average will be used for calculations.
* The heating stage will also incorporate a 5mm hole so that POM images can be taken.
*/
/* Code Info:
* Uses Max6675 Module for two thermal couple sensors to tell temperature and prints to LCD
* The code will provide information for if the two sensors are reading the same temperature.
* This revision of the code will add in the encoder and set temperature for the device.
* For the PID control an average will be taken for the temperature calculations.
* The ecoder will be used to determine the setpoint and will allow the user to have access to 4 different menus.
* Menu: (When selecting a value press the encoder to slect that value and move to the next menu)
* 1) setpoint
* 2) Set P value
* 3) Set I value
* 4) Set D value
* Once values are selected press the encoder again to start.
*/
/* Notes:
* For the liquid crystal display and Max667 Module the appropriat libraries must be installed
* For the Max667 Module portion of the code install the MAX6675 library designed for the chip and k type thermalcouple.
* For LCD I2C portion of code install the LiquidCrystal I2C libray.
* To operate the heating stage a 12 VDC powersupply will be required.
*/
/* Pinouts:
* Max6675 Module(x2) ==> Arduino
* - CS ==> D10 (Top) D6 (Bottom)
* - SO ==> D12 (Top) D4 (Bottom)
* - SCK ==> D13 (Top) D5 (Bottom)
* - Vcc ==> Vcc - 5v (Both)
* - Gnd ==> Gnd (Both)
* i2c LCD Module ==> Arduino
* - SCL ==> A5
* - SDA ==> A4
* - Vcc ==> Vcc - 5v
* - Gnd ==> Gnd
* Encoder ==> Arduino
* - PWM_pin ==> D3
* - Clock ==> D8
* - Data ==> D9
* - Switch ==> D11
/* Code: */
#include <SPI.h>
//LCD config
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4); //sometimes the adress is not 0x3f. Change to 0x27 if it dosn't work.
/* Input For Encoder */
int PWM_pin = 3; //Pin for PWM signal to the MOSFET driver (the BJT npn with pullup)
int clk = 4; //Pin 1 from rotary encoder
int data = 5; //Pin 2 from rotary encoder
int SW = 11; //Switch
/* Variables: */
float temperature_read = 0; // Used in set temperature calculations
float set_temperature = 0; //Default temperature setpoint. Leave it 0 and control it with rotary encoder
float Top_temperature_read = 0.0; // Used for reading top part etemperature
float Bottom_temperature_read = 0.0; // Used for reading bottom part temperature
float avg_temperature = 0.0; // Used for average temperature for the two plates
float PID_error = 0; // Error of PID control
float previous_error = 0; // Used for Derivative portion of code
float elapsedTime, Time, timePrev; // Time variables
float PID_value = 0; // Value for PWM
int button_pressed = 0; // Used for encoder code portion for menu selection
int menu_activated=0; // Menu for prompt selection
float last_set_temperature = 0; // Used for updating temperature
//Variables for rotary encoder state detection
int clk_State;
int Last_State;
bool dt_State;
//PID constants
int kp = 0; // Proportional Term
int ki = 0; // Integral Term
int kd = 0; // Derivative Term
int PID_p = 0; // PID value for Poportional Term
int PID_i = 0; // PID value for Intergral Term
int PID_d = 0; // PID value for Derivative Term
float last_kp = 0; // Update value for Proportional Term
float last_ki = 0; // Update value for Itergral Term
float last_kd = 0; // Update value for Derivative Term
int PID_values_fixed = 0;
//Pins for the SPI with MAX6675
#define TOP_MAX6675_CS 10
#define TOP_MAX6675_SO 12
#define TOP_MAX6675_SCK 13
#define BOT_MAX6675_CS 6
#define BOT_MAX6675_SO 4
#define BOT_MAX6675_SCK 5
void setup()
{
pinMode(PWM_pin,OUTPUT);
TCCR2B = TCCR2B & B11111000 | 0x03; // pin 3 and 11 PWM frequency of 928.5 Hz
Time = millis();
//Encoder Triggers
Last_State = (PINB & B00000001); //Detect first state of the encoder
PCICR |= (1 << PCIE0); //enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); //Set pin D4 trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT1); //Set pin D5 trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT2); //Set pin D11 trigger an interrupt on state change.
//I/O
pinMode(11,INPUT); // For switch
pinMode(9,INPUT); // For data
pinMode(8,INPUT); // For clock
// For the LCD
lcd.init();
lcd.backlight();
}
void loop()
{
float Top_Temp = readThermocouple_TOP(); // Reads as a decimal
float Bot_Temp = readThermocouple_BOT(); // Reads as a decimal
float avg_temperature = (Top_Temp + Bot_Temp)/2;
if(menu_activated==0)
{
// First we read the real value of temperature
temperature_read = avg_temperature ;
//Next we calculate the error between the setpoint and the real value
PID_error = set_temperature - temperature_read + 3;
//Calculate the P value
PID_p = 0.01*kp * PID_error;
//Calculate the I value in a range on +-3
PID_i = 0.01*PID_i + (ki * PID_error);
//For derivative we need real time to calculate speed change rate
timePrev = Time; // the previous time is stored before the actual time read
Time = millis(); // actual time read
elapsedTime = (Time - timePrev) / 1000;
//Now we can calculate the D calue
PID_d = 0.01*kd*((PID_error - previous_error)/elapsedTime);
//Final total PID value is the sum of P + I + D
PID_value = PID_p + PID_i + PID_d;
//We define PWM range between 0 and 255
if(PID_value < 0)
{ PID_value = 0; }
if(PID_value > 255)
{ PID_value = 255; }
//Now we can write the PWM signal to the mosfet on digital pin D3
//Since we activate the MOSFET with a 0 to the base of the BJT, we write 255-PID value (inverted)
analogWrite(PWM_pin,255-PID_value);
previous_error = PID_error; //Remember to store the previous error for next loop.
delay(300); //Refresh rate + delay of LCD print
//lcd.clear();
lcd.setCursor(0,0);
lcd.print("PID TEMP control");
lcd.setCursor(0,1);
lcd.print("S:");
lcd.setCursor(2,1);
lcd.print(set_temperature,1);
lcd.setCursor(9,1);
lcd.print("R:");
lcd.setCursor(11,1);
lcd.print(temperature_read,1);
}//end of menu 0 (PID control)
{
//First page of menu (temp setpoint)
if(menu_activated == 1)
{
analogWrite(PWM_pin,255);
if(set_temperature != last_set_temperature)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Set temperature");
lcd.setCursor(0,1);
lcd.print(set_temperature);
}
last_set_temperature = set_temperature;
}//end of menu 1
//Second page of menu (P set)
if(menu_activated == 2)
{
if(kp != last_kp)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Set P value ");
lcd.setCursor(0,1);
lcd.print(kp);
}
last_kp = kp;
}//end of menu 2
//Third page of menu (I set)
if(menu_activated == 3)
{
if(ki != last_ki)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Set I value ");
lcd.setCursor(0,1);
lcd.print(ki);
}
last_ki = ki;
}//end of menu 3
//Forth page of menu (D set)
if(menu_activated == 4)
{
if(kd != last_kd)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Set D value ");
lcd.setCursor(0,1);
lcd.print(kd);
}
last_kd = kd;
}//end of menu 4
}
}
//Functions that reads the SPI data from MAX6675
/* Top Thermal Couple Function */
double readThermocouple_TOP()
{
uint16_t v;
pinMode(TOP_MAX6675_CS, OUTPUT); // Output
pinMode(TOP_MAX6675_SO, INPUT);
pinMode(TOP_MAX6675_SCK, OUTPUT);
digitalWrite(TOP_MAX6675_CS, LOW);
delay(1);
// Read in 16 bits,
// 15 = 0 always
// 14..2 = 0.25 degree counts MSB First
// 2 = 1 if thermocouple is open circuit
// 1..0 = uninteresting status
v = shiftIn(TOP_MAX6675_SO, TOP_MAX6675_SCK, MSBFIRST);
v <<= 8;
v |= shiftIn(TOP_MAX6675_SO, TOP_MAX6675_SCK, MSBFIRST);
digitalWrite(TOP_MAX6675_CS, HIGH);
if (v & 0x4)
{
// Bit 2 indicates if the thermocouple is disconnected
return NAN;
}
// The lower three bits (0,1,2) are discarded status bits
v >>= 3;
// The remaining bits are the number of 0.25 degree (C) counts
return v*0.25;
}
/* Bottom Thermal Couple Function */
double readThermocouple_BOT()
{
uint16_t v;
pinMode(BOT_MAX6675_CS, OUTPUT); // Output
pinMode(BOT_MAX6675_SO, INPUT);
pinMode(BOT_MAX6675_SCK, OUTPUT);
digitalWrite(BOT_MAX6675_CS, LOW);
delay(1);
// Read in 16 bits,
// 15 = 0 always
// 14..2 = 0.25 degree counts MSB First
// 2 = 1 if thermocouple is open circuit
// 1..0 = uninteresting status
v = shiftIn(BOT_MAX6675_SO, BOT_MAX6675_SCK, MSBFIRST);
v <<= 8;
v |= shiftIn(BOT_MAX6675_SO, BOT_MAX6675_SCK, MSBFIRST);
digitalWrite(BOT_MAX6675_CS, HIGH);
if (v & 0x4)
{
// Bit 2 indicates if the thermocouple is disconnected
return NAN;
}
// The lower three bits (0,1,2) are discarded status bits
v >>= 3;
// The remaining bits are the number of 0.25 degree (C) counts
return v*0.25;
}
//The interruption vector for push button and rotary encoder
ISR(PCINT0_vect)
{
if(menu_activated==1)
{
clk_State = (PINB & B00000001); //pin 8 state? It is HIGH?
dt_State = (PINB & B00000010);
if (clk_State != Last_State)
{
// If the data state is different to the clock state, that means the encoder is rotating clockwise
if (dt_State != clk_State) {
set_temperature = set_temperature+0.5 ;
}
else {
set_temperature = set_temperature-0.5;
}
}
Last_State = clk_State; // Updates the previous state of the clock with the current state
}
if(menu_activated==2)
{
clk_State = (PINB & B00000001); //pin 8 state?
dt_State = (PINB & B00000010);
if (clk_State != Last_State){
// If the data state is different to the clock state, that means the encoder is rotating clockwise
if (dt_State != clk_State) {
kp = kp+1 ;
}
else {
kp = kp-1;
}
}
Last_State = clk_State; // Updates the previous state of the clock with the current state
}
if(menu_activated==3)
{
clk_State = (PINB & B00000001); //pin 8 state?
dt_State = (PINB & B00000010);
if (clk_State != Last_State){
// If the data state is different to the clock state, that means the encoder is rotating clockwise
if (dt_State != clk_State) {
ki = ki+1 ;
}
else {
ki = ki-1;
}
}
Last_State = clk_State; // Updates the previous state of the clock with the current state
}
if(menu_activated==4)
{
clk_State = (PINB & B00000001); //pin 4 state?
dt_State = (PINB & B00000010);
if (clk_State != Last_State){
// If the data state is different to the clock state, that means the encoder is rotating clockwise
if (dt_State != clk_State) {
kd = kd+1 ;
}
else {
kd = kd-1;
}
}
Last_State = clk_State; // Updates the previous state of the clock with the current state
}
//Push button was pressed!
if (PINB & B00001000) //Pin D11 is HIGH?
{
button_pressed = 1;
}
//We navigate through the 4 menus with each button pressed
else if(button_pressed == 1)
{
if(menu_activated==4)
{
menu_activated = 0;
PID_values_fixed=1;
button_pressed=0;
delay(1000);
}
if(menu_activated==3)
{
menu_activated = menu_activated + 1;
button_pressed=0;
kd = kd + 1;
delay(1000);
}
if(menu_activated==2)
{
menu_activated = menu_activated + 1;
button_pressed=0;
ki = ki + 1;
delay(1000);
}
if(menu_activated==1)
{
menu_activated = menu_activated + 1;
button_pressed=0;
kp = kp + 1;
delay(1000);
}
if(menu_activated==0 && PID_values_fixed != 1)
{
menu_activated = menu_activated + 1;
button_pressed=0;
set_temperature = set_temperature+1;
delay(1000);
}
PID_values_fixed = 0;
}
}