Skocz do zawartości
SOYER

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

Udostępnij ten post


Link to post
Share on other sites

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

Udostępnij ten post


Link to post
Share on other sites

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

Udostępnij ten post


Link to post
Share on other sites

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

Udostępnij ten post


Link to post
Share on other sites

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

Udostępnij ten post


Link to post
Share on other sites
(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

Udostępnij ten post


Link to post
Share on other sites
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

Udostępnij ten post


Link to post
Share on other sites
(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

Udostępnij ten post


Link to post
Share on other sites
(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

Udostępnij ten post


Link to post
Share on other sites
(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

Udostępnij ten post


Link to post
Share on other sites

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 🙂

Udostępnij ten post


Link to post
Share on other sites
(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

Udostępnij ten post


Link to post
Share on other sites

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

Udostępnij ten post


Link to post
Share on other sites
(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

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij, aby zacząć pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...