sobota, 24 października 2020

Raspberry Pi - Obsługa termometru DS18B20

W tym poście chciałbym opisać sposób obsługi termometru DS18B20 w oparciu o interfejs 1-Wire.

Znalezione obrazy dla zapytania raspberry pi 3

[Źródło: https://www.raspberrypi.org/products/raspberry-pi-3-model-b/]


Standardowy system:


Aby uruchomić interfejs 1-Wire należy wejść w ustawienia układu:

  1. sudo raspi-config

Następnie należy przejść do opcji numer 5 (Interfacing options) następnie P7 1-Wire gdzie nastąpi uruchomienie interfejsu. Następnie należy ponownie uruchomić system.

Po uruchomieniu interfejsu można sprawdzić poprawność wprowadzonych ustawień po wpisaniu komendy lsmod.

Linie danych interfejsu One Wire można podłączyć do każdego pinu danych dostępnego na Rapsberry Pi. Natomiast domyślnym pinem jest GPIO4.

Po podłączeniu czujnika do Raspberry odczytanie adresu termometru odbywa się przez sprawdzenie dostępnych urządzeń podłączonych do magistrali:

  1. ls /sys/bus/w1/devices


W celu odczytania temperatury z danego czujnika należy użyć następującego polecenia:

  1. cat /sys/bus/w1/devices/28-0000059373c9/w1_slave

Powyższy numer urządzenia odpowiada numerowi czujnika DS18B20 gdzie 28 to kod rodziny. Pozostały numer w ciągu odpowiada 48 bitowy unikalny numer czujnika.

Odpowiedzią na powyższą komendę będzie aktualna temperatura odczytana z czujnika (w tym przypadku będzie to 22,500 st.C)


Wartość crc=45 YES informuje czy transmisja pomiędzy czujnikiem a urządzeniem nadrzędnym została przeprowadzona poprawnie.

Jeśli czujnik miałby być podłączony do innego wyprowadzenia należy zmodyfikować plik /boot/config.txt, gdzie bez modyfikacji znajdują się następujące ustawienia:

  1. //...
  2. //...
  3. //...
  4. dtoverlay=w1-gpio

Jak wspomniałem wcześniej powoduje to uruchomienie interfejsu 1-Wire z standardowymi ustawieniami. (GPIO Pin 4). 

Aby ustawić np. pin GPIO 22 należy wprowadzić następujące informacje do pliku:

  1. //...
  2. //...
  3. //...
  4. dtoverlay=w1-gpio,pullup=1,gpiopin=22

Następnie po przeładowaniu urządzenia odczyt temperatury następuje w standardowy sposób.

Odczyt temperatury Python:


Poniżej prosty program odczytujący temperaturę z podłączonych czujników.
  1. import glob
  2.  
  3. #Convert Celsius to Fahrenheit:
  4. def CelsiusToFahrenheit(cel):
  5.     return ((cel * 9.0/5.0) + 32.0)
  6.  
  7. #Convert Fahrenheit to Celsius:
  8. def FahrenheitToCelsius(fah):
  9.     return ((fah - 32) * 5.0/9.0)
  10.  
  11. #Convert Celsius to Kelvin:
  12. def CelsiusToKelvin(cel):
  13.     return (cel + 273.15)
  14.  
  15. #Search for connected DS18B20
  16. def find_devices():
  17.     return glob.glob('/sys/bus/w1/devices/28-*')
  18.  
  19. #Read temperature for device
  20. def read_temp(path):
  21.     lines = []
  22.     with open(path + '/w1_slave') as f:
  23.         lines = f.readlines()
  24.     #Not all lines has been readed
  25.     if len(lines) != 2:
  26.         return False, 0
  27.  
  28.     #Device return CRC Error
  29.     if lines[0].find('YES') == -1:
  30.         return False, 0
  31.  
  32.     d = lines[1].strip().split('=')
  33.  
  34.     #two strings in array
  35.     if len(d) != 2:
  36.         return False, 0
  37.  
  38.     celTemp = int(d[1])
  39.     #return operation status, readed temperature
  40.     return True, celTemp
  41.  
  42. if __name__ == '__main__':
  43.     #Get connected devices
  44.     devices = find_devices()
  45.  
  46.     #Print data from all connected devices
  47.     for device in devices:
  48.         print('Device Data Loc - ' + device)
  49.         valid, raw = read_temp(device)
  50.         if valid:
  51.             cel = raw / 1000.0
  52.             far = CelsiusToFahrenheit(cel)
  53.             kel = CelsiusToKelvin(cel)
  54.             print('%i [RAW] - %0.2f [F] - %0.2f [K] - %0.3f [C]' % (raw, far, kel,  cel))

Wynik działania programu to:

Można także użyć bibliotek w1thermsensor. W tym celu należy ją najpierw zainstalować.

  1. //Dla python3 lub python2
  2. sudo apt-get install python3-w1thermsensor
  3. sudo apt-get install python-w1thermsensor

Poniżej program wykorzystujący odczyt temperatury z powyższej biblioteki:

  1. import w1thermsensor
  2.  
  3. ds18b20Sensor = w1thermsensor.W1ThermSensor()
  4.  
  5. tempInCel = ds18b20Sensor.get_temperature()
  6. print('%0.3f [C]' % (tempInCel))

Wynik działania programu:


Odczyt temperatury C:


Poniżej przykład wykorzystujący odczyt temperatury z czujnika DS18B20 w języku C. Odczyt nastąpi z wykorzystaniem plików systemowych, podobnie jak w przypadku pierwszego przykładu w języku Python.

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdint.h>
  4. #include <dirent.h>
  5. #include <string.h>
  6. #include <errno.h>
  7. #include <sys/types.h>
  8.  
  9. #define SENSORPATH    "/sys/bus/w1/devices"
  10. #define MAX_NUMBER_OF_SENSORS    2
  11. #define PROBENAMELEN 100
  12.  
  13. char probepath[MAX_NUMBER_OF_SENSORS][PROBENAMELEN];
  14. char probename[MAX_NUMBER_OF_SENSORS][PROBENAMELEN];
  15. FILE *probefd[MAX_NUMBER_OF_SENSORS];
  16.  
  17. float ConvertRawToCelsius(int raw) {
  18.         return (raw/1000.0);
  19. }
  20.  
  21. float ConvertCelsiusToFahrenheit(float cel) {
  22.         return ((cel * 9.0/5.0) + 32.0);
  23. }
  24.  
  25. float ConvertCelsiusToKelvin(float cel) {
  26.         return (cel + 273.15);
  27. }
  28.  
  29. uint8_t findprobes(void) {
  30.         struct dirent *pDirent;
  31.         DIR *pDir;
  32.  
  33.         uint8_t ds18b20SensorsNumbers = 0;
  34.  
  35.         pDir = opendir(SENSORPATH);
  36.  
  37.         if (pDir == NULL) {
  38.                 fprintf(stderr, "Cannot open directory\n");
  39.                 return 0;
  40.         }
  41.  
  42.         while ((pDirent = readdir(pDir)) != NULL)
  43.         {
  44.                 if(strstr(pDirent->d_name, "28-") != NULL)
  45.                 {
  46.                         snprintf(probepath[ds18b20SensorsNumbers], PROBENAMELEN-1, "%s/%s/w1_slave", SENSORPATH, pDirent->d_name);
  47.                         snprintf(probename[ds18b20SensorsNumbers], PROBENAMELEN-1, "%s", pDirent->d_name);
  48.                         ds18b20SensorsNumbers++;
  49.                 }
  50.         }
  51.  
  52.         closedir(pDir);
  53.         return ds18b20SensorsNumbers;
  54. }
  55. int main(int argc, char **argv)
  56. {
  57.         float temperature;
  58.         char *temp;
  59.         char buf[100];
  60.         uint8_t connectedSensors = 0;
  61.  
  62.         printf("DS18B20 test program\r\n");
  63.  
  64.         connectedSensors = FindSensors();
  65.         if (connectedSensors  == 0) {
  66.                 fprintf(stderr, "Error: No DS18B20 compatible probes located.\r\n");
  67.                 exit(1);
  68.         }
  69.         else {
  70.                 printf("Number of connected sensors: %i\r\n", connectedSensors);
  71.         }
  72.  
  73.         for(uint8_t c = 0; c < connectedSensors; c++)
  74.         {
  75.                 probefd[c] = fopen(probepath[c], "r");
  76.  
  77.                 if (probefd[c] == NULL) {
  78.                         fprintf(stderr, "Error: Can't receive data\r\n");
  79.                         exit(1);
  80.                 }
  81.                 else {
  82.                         printf("Data readed from sensor %i\r\n", (c+1));
  83.                 }
  84.  
  85.                 fgets(buf, sizeof(buf)-1, probefd[c]);
  86.                 printf(buf);
  87.  
  88.                 /* Check if CRC is correct */
  89.                 printf("CRC Checking - ");
  90.                 if(strstr(buf, "YES") != NULL) {
  91.                         printf("OK\r\n");
  92.                 } else {
  93.                         printf("Error\r\n");
  94.                         exit(1);
  95.                 }
  96.  
  97.                 memset(buf, 0, sizeof(buf));
  98.                 fgets(buf, sizeof(buf)-1, probefd[c]);
  99.                 printf(buf);
  100.  
  101.                 temp = strstr(buf, "t=");
  102.                 temp += 2;
  103.                 temperature = ConvertRawToCelsius(atoi(temp));
  104.  
  105.                 printf("---------------------------\r\n");
  106.                 printf("Sensor number %i\r\n", (c+1));
  107.                 printf("Probe %s\r\n", probename[c]);
  108.                 printf("Temp: %i [RAW]; %2.3f [C]; %2.3f [F]; %3.3f [K]\r\n", (atoi(temp)), temperature,
  109.                                                         ConvertCelsiusToFahrenheit(temperature), ConvertCelsiusToKelvin(temperature));
  110.                 fclose(probefd[c]);
  111.         }
  112.         exit(0);
  113. }

Wynik działania programu z podłączonym jednym czujnikiem jest następujący:


NodeJs:


Do odczytu temperatury można się także posłużyć dużą ilością darmowych bibliotek oraz środowiska NodeJs. Można wybierać pomiędzy kilkoma rozwiązaniami. Wszystkie od pobrania ze strony https://www.npmjs.com/. Ja skorzystałem z biblioteki ds18b20-raspi.

W celu instalacji nodejs oraz npm można skorzystać z tego opisu.

Instalacja biblioteki:

  1. npm install --save ds18b20-raspi

Odczytanie wartości wykonuje się w następujący sposób:

  1. const sensor = require('ds18b20-raspi');
  2.  
  3. const tempC = sensor.readSimpleC();
  4. console.log(`${tempC} degC`);
  5.  
  6. // async version
  7. sensor.readSimpleC((err, temp) => {
  8.     if (err) {
  9.         console.log(err);
  10.     } else {
  11.     console.log(`${temp} degC`);
  12.     }
  13. });

Dodatkowe informacje o bibliotece można przeczytać na stronie npmjs.com.