Skocz do zawartości

Podłaczenie czujników TMP36GT9Z


Pomocna odpowiedź

Też pomyślałem o programowym wygładzeniu tego wykresu. 

Z innej beczki, co to za czujnik? Był w komplecie jako zewnętrzny temperatury do mojego pieca... :

IMG_20190518_162442.thumb.jpg.f5c20da20b1f2da7f8fc48d39d40e4c9.jpg

Na 99% to półprzewodnikowy czujnik rezystancyjny czyli popularny termistor, np. z serii KTY:

https://www.tme.eu/Document/1807f69966f297e1da1dafa767320e88/KTY81.pdf

Zmierz go omomierzem a potem chuchnij albo przyłóż palec. Większość (oprócz pewnych specjalnych) termistorów ma ujemny współczynnik temperaturowy (tzw. tempco) więc rezystancja powinna spadać. Możesz z tego i z jakiegoś opornika zrobić zwykły dzielnik i mierzyć napięcie. Niestety termistory nie są zbyt liniowe (jak np. sygnał z wyjścia TMP36) - znając dokładnie typ można używać wzoru z podanymi przez producenta współczynnikami, zajrzeć do tabelki w datasheet albo zwyczajnie zdjąć ch-kę w kilku punktach (np. 0, +25 i +100 stopni), dziwną krzywą aproksymować prostymi a potem interpolować. Czasem nawet spora nieliniowość w ogóle nie ma znaczenia, bo robisz np. termostat. Wtedy bierzesz potencjometr i komparator napiecia, ustawiasz jakiś próg przełączania i ważne jest jedynie to, by jutro i za miesiąc termistor w tej samej temperaturze miał taka samą rezystancję, nie jest ważne jaką konkretnie.

  • Lubię! 2

Czy z termistorami też występują podobne problemy jak mam z TMP36, czy to "łatwiejsze" w implementacji czujniki temperatury? 

Hej, co spróbowałem zrobić to programowe uśrednienie pomiarów. 10 próbek, każda co 10ms. Chodzi tak od paru godzin. Odczyty zdecydowanie stabilniejsze. Skoki o 1 stopień. W tym tygodniu spróbuję podłączyć te fizyczne filtrowanie... 

Screenshot_20190519-173223.jpg Screenshot_20190519-172950.jpg

  • Lubię! 1

Tak mi przyszło do głowy, że jeśli masz zasilanie 5V to Twój ADC ma LSB ważący ok. 5mV (5V/1024). A z kolei 1 stopień Celsjusza to na wyjściu TMP36 zmiana napięcia o 10mV. Przetwornik w AVR jest taki sobie i plus/minus 2LSB to zupełnie normalny rozrzut w środowisku gdzie nie przyłożyłeś się do filtrowania zasilań i referencji przetwornika. Tak więc może pokaż kod, bo z wykresu widzę, że zaczynasz opierać się rozdzielczość jakiejś arytmetyki - nie ma wyników pośrednich tylko całkowite liczby stopni. A jeśli w swoich obliczeniach masz na wyjściu 1 LSB = 1 stopień C, to z definicji pomiarów będziesz miał skoki o 1. Jeśli chcesz mieć wykresy gładsze w tej skali, to musisz zacząć liczyć więcej miejsc po przecinku i nie gubić ich w arytmetyce, by nie tracić rozdzielczości.

  • Lubię! 1
(edytowany)

Masz rację, używam floata tylko do przeliczenia napięcia, potem już same int-y. Więc mówisz, że powinienem liczyć wszystko na floatach, a tylko wynikową zmienną mieć w pełnych stopniach(dokładniej mi nie potrzebne). Takie przeliczanie zrobi różnicę? Zresztą sprawdzę, to tylko zmiana rodzaju zmiennych... 

Dotychczasowy kod:

void term_grzejniki_maxmin_reset(){ 
  int sumaGrzejniki = 0;
  int tempGrzejniki [9];
  for(int i=0; i<10; i++){
  int odczyt = analogRead(grzejniki);      //odczytanie wartości z czujnika
  float VOLT = (odczyt * 5.0) / 1024.0;        //przeliczenie odczytanej wartości na napięcie w woltach (dla podłączenia pod 5 V)
  tempGrzejniki[i] = (VOLT - 0.5) * 100;   
  sumaGrzejniki += tempGrzejniki[i];
  delay(10);
  }  
  temp_grzejniki = sumaGrzejniki/10; 
  sumaGrzejniki = 0;
  DateTime now = czas.now();    
   if (temp_grzejniki > temp_grzejniki_max) {
    temp_grzejniki_max = temp_grzejniki;
    dzien_temp_grzejniki_max = now.day();
    miesiac_temp_grzejniki_max = now.month();
    rok_temp_grzejniki_max = now.year();
   }
   if (temp_grzejniki < temp_grzejniki_min) { 
    temp_grzejniki_min = temp_grzejniki;
    dzien_temp_grzejniki_min = now.day();
    miesiac_temp_grzejniki_min = now.month();
    rok_temp_grzejniki_min = now.year();
   }
}

 

Edytowano przez SOYER
21 minut temu, SOYER napisał:

Więc mówisz, że powinienem liczyć wszystko na floatach,

Więcej miejsc po przecinku to niekoniecznie float. Możesz za jednostkę przyjąć np. 1/10 stopnia. Pamiętaj, że każde działanie arytmetyczne na liczbach ograniczonej precyzji może powodować błąd, a błędy mogą się skumulować. Jeśli precyzja będzie wystarczająca, kumulacja będzie niedostrzegalna.

To tak, jak w przetwarzaniu dźwięku - wewnętrznie maszyneria chodzi co najmniej w 24 bitach (te tańsze), a na wyjściu dostajesz teoretycznie 16 (teoretycznie, bo np. przetworniki w CD maja nieco mniej). Czy jak programy księgowe, dla których jednostką jest 1/10 grosza, a wyniki przedstawiają po zaokrągleniu.

  • Lubię! 1
(edytowany)

Chodzi o taką zmianę:

float VOLT = (odczyt * 5.0) / 2048.0;         tempGrzejniki[i] = (VOLT - 0.5) * 200;   

Choć chyba raczej

int sumaGrzejniki = 0;  
int tempGrzejniki [9];   
   for(int i=0; i<10; i++){   
                   int odczyt = analogRead(grzejniki);      
                   float VOLT = (odczyt * 5.0) / 1024.0;       
                   tempGrzejniki = (VOLT - 0.5) * 10;   
                   sumaGrzejniki += tempGrzejniki;   
                   delay(10);
   }     
      temp_grzejniki = sumaGrzejniki;
      sumaGrzejniki = 0;



Edytowano przez SOYER
(edytowany)

Hm... a jeszcze takie dziwne pytanie...

Tablica "tempGrzejniki" została zadeklarowana jako int[9]; Pomijając fakt, że w ostatniej wersji chyba o tym zapomniałeś... co się stanie, jeśli zmienna "i" przyjmie wartość 9? A w ogóle po kiego grzyba Ci tabela, której nie wykorzystujesz?

Zamiast przeliczać za każdym razem zrób sobie po prostu średnią odczytu wartości z konwertera a przelicz na samym końcu, coś w stylu:

int i, odczyt;
for (i=odczyt=0; i<10;i++) {
  odczyt += analogRead(cośtam);
  delay(chwilkę);
}
float VOLT = (odczyt * 5.0 / 10240.0); // czy jakoś tak

Na samych intach też można (trochę większy błąd, ale i tak mniejszy niż rozdzielczość termometru):

int centiVolt = (odczyt * 2) / 41;
int temperatura_czegośtam = centiVolt - 50;

Jeśli mi się przecinek gdzieś nie przesunął to powinieneś dostać rozsądny wynik.

Aha - zmienna centiVolt wcale nie jest nikomu do szczęścia potrzebna, wystarczy przecież:

temperatura = (odczyt * 2) / 41 - 50;

 

Edytowano przez ethanak
  • Lubię! 1
(edytowany)
int sumaGrzejniki = 0;   
int tempGrzejniki [9];   
   for(int i=0; i<10; i++){   
                   int odczyt = analogRead(grzejniki);      
                   float VOLT = (odczyt * 5.0) / 1024.0;       
                   tempGrzejniki[i] = (VOLT - 0.5) * 10;    
                   sumaGrzejniki += tempGrzejniki[i];   
                   delay(10); 
                   }     
temp_grzejniki = sumaGrzejniki; 
sumaGrzejniki = 0;

Oto mi chodziło, tak to jest jak telefonem piszę program...

Rozumiem, że lepiej, prościej i szybciej dla procka, tablica zbędna i połowa linijek kodu odpadła:):  
 

   for(int i=0; i<10; i++){   
                   float odczyt += analogRead(grzejniki);      
                   delay(10); 
                   }     

float VOLT = (odczyt * 5.0) / 10240.0;       
temp_grzejniki = (VOLT - 0.5) * 100;    
odczyt = 0;

Jednak skoro najpierw dzielimy przez 10240 a potem mnożymy razy 100 to po skróceniu faktycznie może być:

for(int i=0; i<10; i++){   
                   float odczyt += analogRead(grzejniki);      
                   delay(10); 
                   }     

temp_grzejniki = (odczyt *2)/41-50
odczyt = 0;

Dzięki.

Edytowano przez SOYER

to nie ma prawa działać.

deklarujesz zmienną "odczyt" w bloku - poza blokiem już jej nie będzie. Ale rozumiem, że to przeoczenie i "float odczyt" jest nieco wyżej. Bo jeśli są tu i tu to są wtedy dwie różne zmienne i podstawienie czegoś do zmiennej "odczyt" w bloku spowoduje, że nic nie spowoduje :).

Po co odczyt to float jeśli wynik w najgorszym razie to 14 bitów inta (czyli zmieści się z zapasem w int16_t)? Żeby procek się nie nudził?

Dlaczego zmienna odczyt zerowana jest po podsumowaniu a nie przed?

Tak przy okazji - sprawdziłeś jaka będzie różnica między Twoimi (no, powiedzmy że znalezionymi na stronie traktującej o termometrze) dwiema linijkami na floatach a moją jedną na intach?

Tak przy okazji w pętli powinno być trochę inaczej (chociaż tak jak jest też zadziała) - niestety od rana jakieś telefony i w sumie nie zdążyłem klepnąć "wyślij" po edycji:

for (i=odczyt=0; i<10;i++) {
  if (i) delay(chwilkę);
  odczyt += analogRead(cośtam);
}

Drobiazg - ale chwilkę krócej się wykonuje 🙂

(edytowany)

 

int odczyt;

for(int i=0; i<10; i++){                       
                    odczyt += analogRead(grzejniki);      
                    delay(10);                    
                    }    
 temp_grzejniki = (odczyt *2)/41-50;
 odczyt = 0;

Tego ostatniego Twojego drobiazgu nie rozumiem:

 

24 minuty temu, ethanak napisał:

for (i=odczyt=0; i<10;i++) {
  if (i) delay(chwilkę);
  odczyt += analogRead(cośtam);
}

Drobiazg - ale chwilkę krócej się wykonuje 🙂

 

Edytowano przez SOYER

Ponawiam pytanie: dlaczego zmienna "odczyt" jest float a nie int? I dlaczego nie jest zerowana przed wejściem do pętli?

Co do drobiazgu: w Twojej pętli niepotrzebnie wykonywał się delay(10) po ostatnim odczycie (tzn. pomijając czas konwersji A/D czas wykonania pętli to 100msec). W mojej wykonuje się tylko pomiędzy pomiarami - konkretniej przed każdym pomiarem z wyjątkiem pierwszego (czyli czas wykonania to 90 msec).

 

  • Lubię! 1
  • Pomogłeś! 1
(edytowany)
int odczyt; 

odczyt = 0;

for(int i=0; i<10; i++){                       

if (i) delay(10);

odczyt += analogRead(grzejniki);               

  }     

temp_grzejniki = (odczyt *2)/41-50;

Drobiazg do mnie przemawia teraz, racja.

Jednak co za róznica czy odczyt=0 jest przed pętlą czy po zakończeniu obliczeń? Chodziło ci tylko o ZERO  przy pierwszej pętli?

int odczyt = 0; 

for(int i=0; i<10; i++){                       

if (i) delay(10); odczyt += analogRead(grzejniki);                

  }     

temp_grzejniki = (odczyt *2)/41-50;

odczyt = 0; 

 

Edytowano przez SOYER

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...