#include <Arduino.h>
#include <avr/io.h>
#include <math.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <PID_v1.h>
#include <PID_AutoTune_v0.h>
uint8_t seg[10] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 };
uint8_t birler, onlar, yuzler, binler, digit;
float currentTemp;
uint16_t temp;
byte ATuneModeRemember = 2;
float input, output, setpoint;
float kp = 2, ki = 0.5, kd = 2;
float kpmodel = 1.5, taup = 100, theta[50];
float outputStart = 5;
float aTuneStep = 50, aTuneNoise = 1, aTuneStartValue = 100;
unsigned int aTuneLookBack = 20;
boolean tuning = false;
uint32_t modelTime;
uint16_t windowSize = 300;
uint32_t windowStartTime, nextSwitchTime;
bool relayStatus;
boolean useSimulation = false;
PID myPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);
PID_ATune aTune(&input, &output);
void adc_init() {
ADCSRA = 0;
ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADIE);
sei();
}
uint16_t analogNoiseReducedRead(byte pinNumber) {
uint16_t reading = 0;
ADCSRA |= (1 << ADEN) | (1 << ADIF);
ADMUX = (0x0F & pinNumber) | (1 << REFS0);
_delay_us(32);
set_sleep_mode(SLEEP_MODE_ADC);
for (uint8_t i = 0; i < 32; i++) {
sleep_mode();
while (ADCSRA & (1 << ADSC))
;
reading += ADC;
}
ADCSRA &= ~(1 << ADEN);
return (reading >> 5);
}
float getTemp() {
float sicaklik;
sicaklik = analogNoiseReducedRead(4);
sicaklik = log(((10240000 / sicaklik) - 10000));
sicaklik = 1 / (0.001129148f + (0.000234125f + (0.0000000876741f * sicaklik * sicaklik)) * sicaklik);
sicaklik = (sicaklik - 273.15f);
return (sicaklik);
}
EMPTY_INTERRUPT(ADC_vect);
void send_data(uint8_t data_out) {
for (uint8_t i = 0; i < 8; i++) {
if (data_out & 0x80) {
PORTB |= (1 << 2);
} else {
PORTB &= ~(1 << 2);
}
PORTB |= (1 << 1);
PORTB &= ~(1 << 1);
data_out <<= 1;
}
PORTB |= (1 << 0);
PORTB &= ~(1 << 0);
}
void mcu_init() {
DDRB |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 5);
DDRD |= (1 << 2);
DDRC |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3);
DDRC &= ~(1 << 4);
}
void changeAutoTune() {
if (!tuning) {
//Set the output to the desired starting frequency.
output = aTuneStartValue;
aTune.SetNoiseBand(aTuneNoise);
aTune.SetOutputStep(aTuneStep);
aTune.SetLookbackSec((int)aTuneLookBack);
AutoTuneHelper(true);
tuning = true;
} else { //cancel autotune
aTune.Cancel();
tuning = false;
AutoTuneHelper(false);
}
}
void AutoTuneHelper(boolean start) {
if (start) ATuneModeRemember = myPID.GetMode();
else myPID.SetMode(ATuneModeRemember);
}
void DoModel() {
//cycle the dead time
for (byte i = 0; i < 49; i++) {
theta[i] = theta[i + 1];
}
//compute the input
input = (kpmodel / taup) * (theta[0] - outputStart) + input * (1 - 1 / taup) + ((float)random(-10, 10)) / 100;
}
void setup() { ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mcu_init();
adc_init();
_delay_ms(3000);
if (useSimulation) {
for (byte i = 0; i < 50; i++) {
theta[i] = outputStart;
}
modelTime = 0;
}
setpoint = 37.7f;
myPID.SetOutputLimits(0, windowSize);
myPID.SetMode(AUTOMATIC);
if (tuning) {
tuning = false;
changeAutoTune();
tuning = true;
}
windowStartTime = millis();
}
void loop() { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t now = millis();
currentTemp = getTemp();
if (!useSimulation) { //pull the input in from the real world
input = currentTemp;
}
if (tuning) {
byte val = (aTune.Runtime());
if (val != 0) {
tuning = false;
}
if (!tuning) { //we're done, set the tuning parameters
kp = aTune.GetKp();
ki = aTune.GetKi();
kd = aTune.GetKd();
myPID.SetTunings(kp, ki, kd);
AutoTuneHelper(false);
}
}
else myPID.Compute();
if (useSimulation) {
theta[30] = output;
if (now >= modelTime) {
modelTime += 100;
DoModel();
}
}
uint32_t msNow = millis();
if (msNow - windowStartTime >= windowSize) {
windowStartTime = msNow;
}
if (!relayStatus && output > (msNow - windowStartTime)) {
if (msNow > nextSwitchTime) {
nextSwitchTime = msNow + 1;
relayStatus = true;
PORTD |= (1 << 2);
}
} else if (relayStatus && output < (msNow - windowStartTime)) {
if (msNow > nextSwitchTime) {
nextSwitchTime = msNow + 1;
relayStatus = false;
PORTD &= ~(1 << 2);
}
}
temp = currentTemp * 100;
digit++;
digit &= 0x03;
birler = (temp % 10);
onlar = (temp / 10) % 10;
yuzler = (temp / 100) % 10;
binler = (temp / 1000) % 10;
switch (digit) {
case 0: send_data(seg[binler]); break;
case 1:
if (digit == 1) send_data(seg[yuzler] & 0x7F);
else send_data(seg[yuzler]);
break;
case 2: send_data(seg[onlar]); break;
case 3: send_data(seg[birler]); break;
}
PORTC = (1 << digit);
return;
}