This is an old revision of the document!
Autor: Stănică Ovidiu-Ștefan
Grupa: 336CA
Pistolul de măsurat temperatura nu intră în contact cu suprafața a cărei temperatură este măsurată.
Pentru eșantionarea temperaturii se folosește un senzor de temperatură cu raze infraroșii MXL90614.
Pentru măsurarea cât mai potrivită, vor fi introduse 3 moduri de măsurare, specifice unor medii de temperatură.
Senzorul de temperatură are un interval de măsurare de -70 până la +360. Deoarece deviația senzorului este procentuală cu temperatura masurată, am ales să ignor extremele intervalului și să constrâng măsurarea între -30 si +300.
Pistolul are 2 butoane prin care e controlată măsurarea temperaturii. Un buton, numit declanșator, activează măsurarea temperaturii cât timp e apăsat, în continuu. Temperatura este afișată pe LCD, pe rândul de jos, în partea stângă. În partea dreaptă e afișată temperatura trecută prin filtrul de procesare.
Pistolul oferă 3 filtre de procesare:
* Minim (MIN) păstrează doar minimul temperaturilor măsurate;
* Maxim (MAX) păstrează doar minimul temperaturilor măsurate;
* Average (AVG) face media temperaturilor măsurate;
* None (—) afișează ultima temperatură măsurată;
Filtrul de procesare e resetat când se începe o nouă măsuratoare (adică când se apasă declanșatorul)
Carcasa pistolului va fi făcută la o imprimanta 3D. (sau cel puțin voi încerca să folosesc un soft de design, Fusion 360)
Voi avea nevoie de următoarele componente:
* Arduino Uno
* LCD 1602 + Adaptor I2C
* Buton schimbat mod măsurare
* Buton declanșat măsurarea
* Baterie
* Fire
* Întrerupător on/off
Display-ul si senzorul le-am pus pe aceeasi “placa”, conectate la Arduino printr-un cablu de Ethernet. Am folosit cablu de ethernet pentru ca are ecranare cat de cat buna, si I2C este gandit sa fie folosit pe acelasi circuit board, deci nu suporta interferente prea mari.
Butoanele sunt conectare direct langa Arduino.
Am folosit un timer (timer2) pe Atmega ca sa fac citirea senzorului de 2500 ori pe secunda.
Pentru ca senzorul de temperatura este interfatat pe I2C, si comunicatia pe I2C are nevoie ca interrupt-urile sa fie pornite, nu pot face citirea senzorului direct in interrupt-ul de timer. In loc, interrupt-ul seteaza un flag global. Bucla principala verifica constant acest flag, si daca este setat, il elimina si face o citire a senzorului.
Afisarea la display se face de 10 ori pe secunda. In mod ideal, afisare s-ar putea face mai des, dar refresh rate-ul display-ului nu permite (dureaza ~70ms ca un pixel sa-si schimbe starea)
Filtrele sunt evaluate odata cu refresh-ul senzorului. Daca se cere schimbarea filtrului, inainte de citire, se reseteaza datele de acumulare si e schimbata functia de filtru.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_MLX90614.h>
byte stage1[] = {
0x00,
0x00,
0x00,
0x10,
0x10,
0x10,
0x18,
0x1C
};
byte stage2[] = {
0x1C,
0x18,
0x10,
0x10,
0x10,
0x00,
0x00,
0x00
};
byte stage3[] = {
0x1F,
0x03,
0x01,
0x00,
0x00,
0x00,
0x00,
0x00
};
byte stage4[] = {
0x00,
0x00,
0x00,
0x01,
0x01,
0x01,
0x03,
0x07
};
char line1[19] = { 'C', 'U', 'R', 'R', 'E', 'N', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0 };
char line2[19] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0 };
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
LiquidCrystal_I2C lcd(0x3F, 16, 2);
bool changeFilterLastState = false;
bool measureLastState = false;
#define MIN_TEMP (-50)
#define MAX_TEMP (+320)
typedef int filter_type;
#define FILTER_MIN (1)
#define FILTER_MAX (2)
#define FILTER_CUR (0)
filter_type currentFilter = FILTER_CUR;
double accumulator = 0;
double immediate = 0;
bool measuring = false;
int measuringStage = 0;
volatile bool readPending = false;
long lastRead = 0;
filter_type getNextFilter() {
if (currentFilter == FILTER_CUR) return FILTER_MIN;
if (currentFilter == FILTER_MIN) return FILTER_MAX;
if (currentFilter == FILTER_MAX) return FILTER_CUR;
return FILTER_CUR;
}
void resetFilter() {
if (currentFilter == FILTER_CUR) accumulator = 0;
if (currentFilter == FILTER_MIN) accumulator = MAX_TEMP;
if (currentFilter == FILTER_MAX) accumulator = MIN_TEMP;
}
void feedFilter(double imm) {
immediate = imm;
if (currentFilter == FILTER_CUR) {
accumulator = imm;
}
if (currentFilter == FILTER_MIN) {
accumulator = accumulator > imm ? imm : accumulator;
}
if (currentFilter == FILTER_MAX) {
accumulator = accumulator < imm ? imm : accumulator;
}
}
double readSensor() {
return constrain(mlx.readObjectTempC(), MIN_TEMP, MAX_TEMP);
}
ISR(TIMER2_COMPA_vect){
readPending = true;
}
void readSensorAndUpdate() {
bool measureThisState = /*!(PIND & PD5);*/ !digitalRead(5);
if (measureThisState) {
measuring = true;
if (!measureLastState) {
resetFilter();
}
feedFilter(readSensor());
} else {
measuring = false;
bool changeFilterThisState = /*!(PINB & PB1)*/ !digitalRead(9);
if (changeFilterThisState && !changeFilterLastState) {
currentFilter = getNextFilter();
resetFilter();
feedFilter(readSensor());
}
changeFilterLastState = changeFilterThisState;
}
measureLastState = measureThisState;
}
/**
* Format: -56.22
* 74.78
* 313.01
* 6 chars length plus 2 spaces after, always fill 8!
*/
void formatTemp(double temp, char * loc) {
int cursor = 0;
if (temp < 0) {
loc[cursor++] = '-';
temp = -temp;
}
int temp100 = ((int)(temp * 100)) % 100;
int temp1 = (int)(temp);
if (temp1 / 100) {
loc[cursor++] = '0' + (temp1 / 100);
temp1 = temp1 % 100;
}
if (temp1 / 10) {
loc[cursor++] = '0' + (temp1 / 10);
temp1 = temp1 % 10;
}
loc[cursor++] = '0' + temp1;
loc[cursor++] = '.';
loc[cursor++] = '0' + (temp100 / 10);
loc[cursor++] = '0' + (temp100 % 10);
while (cursor != 8) {
loc[cursor++] = ' ';
}
}
void writeDisplay() {
formatTemp(immediate, line2);
if (currentFilter != FILTER_CUR) {
formatTemp(accumulator, line2 + 10);
if (currentFilter == FILTER_MAX) {
line1[10] = 'M'; line1[11] = 'A'; line1[12] = 'X';
}
if (currentFilter == FILTER_MIN) {
line1[10] = 'M'; line1[11] = 'I'; line1[12] = 'N';
}
} else {
line1[10] = line1[11] = line1[12] = ' ';
line2[8] = line2[9] = line2[10] = line2[11] = line2[12] = line2[13] = line2[14] = line2[15] = ' ';
}
if (measuring) {
if (measuringStage == 0) line1[8] = 1;
if (measuringStage == 1) line1[8] = 2;
if (measuringStage == 2) line1[8] = 3;
if (measuringStage == 3) line1[8] = 4;
if (measuringStage == 3) measuringStage = 0;
else measuringStage = measuringStage + 1;
} else {
line1[8] = ' ';
}
line1[16] = line2[16] = 0;
// write lines
lcd.setCursor(0, 0);
lcd.print(line1);
lcd.setCursor(0, 1);
lcd.print(line2);
}
void setup() {
DDRD |= (PD5 | PD6 | PD2);
DDRB |= (PB1);
pinMode(6, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
Serial.begin(9600);
mlx.begin();
Wire.setClock(100000);
lcd.init();
lcd.init();
lcd.backlight();
lcd.createChar(1, stage1);
lcd.createChar(2, stage2);
lcd.createChar(3, stage3);
lcd.createChar(4, stage4);
cli();
TCCR2A = 0;
TCCR2B = 0;
TCNT2 = 0;
OCR2A = 249;
TCCR2B |= (1 << WGM21);
TCCR2B |= (1 << CS22) | (0 << CS21) | (0 << CS20);
TIMSK2 |= (1 << OCIE2A);
sei();
}
void loop() {
long current = millis();
if (current - lastRead >= 100) {
writeDisplay();
lastRead = current;
}
if (readPending) {
readPending = false;
readSensorAndUpdate();
}
}