6. týden: Senzory a vstupní zařízení Week 6: Sensors and Input Devices

Během šestého týdne bylo úkolem použít nějaký senzor pro měření fyzikální veličiny pomocí Arduina. During the sixth week, the task was to use a sensor to measure a physical quantity using an Arduino.

Měření teploty a hloubky Temperature and depth measurement

Doma jsem měl nevyužitý senzor teploty a senzor absolutního tlaku, tak jsem se rozhodl postavit přístroj na měření teploty kapaliny a hloubky ponoření sondy. Mozkem zařízení je Arduino Nano napájené z počítače přes USB. Naměřená data se zobrazují na OLED displeji a zároveň se ukládají na SD kartu pro pozdější zpracování a tvorbu grafů. Měření a zápis dat lze bezpečně ukončit stisknutím tlačítka. I had an unused temperature sensor and an absolute pressure sensor at home, so I decided to build a device for measuring liquid temperature and the immersion depth of the probe. The brain of the device is an Arduino Nano powered from a computer via USB. The measured data is displayed on an OLED display and simultaneously saved to an SD card for later processing and graph creation. Measurement and data recording can be safely terminated by pressing a button.

Zapojení Wiring

Seznam použitých součástek: List of used components:

Fotka použitých součástek SDkarta a její adaptéry

Teplotní sonda využívá jednovodičovou sběrnici OneWire. Pro její správné fungování je nutné zapojit 4,7kΩ pull-up rezistor mezi napájecí a datový pin. Senzor tlaku MPRLS a OLED displej komunikují přes protokol I2C (dva vodiče - datový SDA a hodinový SCL). Modul čtečky SD karet je připojen přes rozhraní SPI (čtyři vodiče - data do karty MOSI, data z karty MISO, hodinový signál SCK a Chip Select CS). The temperature probe uses a OneWire single-wire bus. For its proper functioning, it is necessary to connect a 4.7kΩ pull-up resistor between the power and data pins. The MPRLS pressure sensor and the OLED display communicate via the I2C protocol (two wires - data SDA and clock SCL). The SD card reader module is connected via the SPI interface (four wires - data to card MOSI, data from card MISO, clock signal SCK, and Chip Select CS).

Zapojení jsem navrhl v programu KiCAD. Součástky jako Arduino Nano (Arduino_Nano_Every z knihovny MCU_Module), teplotní senzor DS18B20 (DS18B20), tlačítko (SW_Push) a rezistor (R) jsou již součástí základních knihoven programu. Symboly pro ostatní moduly lze stáhnout z internetu, případně je ve schématu nahradit standardními kolíkovými lištami (např. Conn_01x04_Socket pro OLED displej). I designed the wiring in the KiCAD program. Components like the Arduino Nano (Arduino_Nano_Every from the MCU_Module library), the DS18B20 temperature sensor (DS18B20), the button (SW_Push), and the resistor (R) are already part of the program's basic libraries. Symbols for other modules can be downloaded from the internet, or replaced in the schematic with standard pin headers (e.g., Conn_01x04_Socket for the OLED display).

Schema projektu v programu KiCAD

Teplotní sonda se standardně prodává s několikametrovým kabelem. Pro snadnější zapojení do nepájivého pole (breadboardu) jsem kabel zkrátil a na jeho konce připájel kolíkové piny. The temperature probe is normally sold with a several-meter-long cable. For easier connection to the breadboard, I shortened the cable and soldered header pins to its ends.

Zkrácený teplotní senzor a drátky Teplotní senzor s připájenými drátky

Poté jsem obvod zapojil podle připraveného schématu. Then I wired the circuit according to the prepared schematic.

Projekt zapojený na breadboardu

Kód Code

Kód je napsán v prostředí Arduino IDE. Program v pravidelných 800ms intervalech vyčítá hodnoty teploty z teplotní sondy a absolutního tlaku z I2C senzoru. Z naměřeného tlaku a referenční hodnoty pak vypočítává hloubku ponoru podle vzorce: The code is written in the Arduino IDE environment. The program reads temperature values from the temperature probe and absolute pressure from the I2C sensor at regular 800ms intervals. From the measured pressure and the reference value, it then calculates the immersion depth according to the formula:

Vzorec pro výpočet hloubky z tlaku

Získaná data se průběžně zobrazují na OLED displeji a ukládají do CSV souboru na SD kartu. Stiskem tlačítka lze měření a zápis na kartu bezpečně ukončit. The acquired data is continuously displayed on the OLED display and saved to a CSV file on the SD card. By pressing the button, measurement and saving to the card can be safely terminated.

Použité knihovny: Used libraries:

Kód v Arduinu: Code in Arduino:

C++ measure_temperature_depth.ino
#include <Wire.h>
#include <Adafruit_MPRLS.h>
#include <OneWire.h>
#include <SPI.h>
#include <SD.h>
#include <U8x8lib.h>

#define ONE_WIRE_BUS 2
#define BUTTON_PIN 3
#define CHIP_SELECT_SD 10

// inicializace periferii
OneWire oneWire(ONE_WIRE_BUS);
Adafruit_MPRLS mprls = Adafruit_MPRLS(-1, -1);
U8X8_SSD1306_128X64_NONAME_HW_I2C display(/* reset=*/ U8X8_PIN_NONE);

// merene hodnoty
float referencePressure = 0.0;
float currentTempC = 0.0;
float currentDepth_m = 0.0;

// rizeni behu
bool isRunning = true;
bool lastButtonState = HIGH;
unsigned long lastSensorReadTime = 0;

// sd karta a data
bool sdReady = false;
unsigned long sampleCount = 0;

void updateDisplay();

void setup() {
    pinMode(BUTTON_PIN, INPUT_PULLUP);

    // oled displej
    display.begin();
    display.setPowerSave(0);
    display.setFont(u8x8_font_courB18_2x3_r);
    display.clear();

    // senzor tlaku
    if (!mprls.begin()) {
    display.print("err tlak");
    delay(2000);
    display.clear();
    }

    // sd karta
    if (SD.begin(CHIP_SELECT_SD)) {
    sdReady = true;
    if (SD.exists("data.csv")) SD.remove("data.csv");
    
    // hlavicka csv
    File dataFile = SD.open("data.csv", FILE_WRITE);
    if (dataFile) {
        dataFile.println(F("vzorec,teplota,hloubka"));
        dataFile.close();
    }
    } else {
    display.print("err sd");
    delay(2000);
    display.clear();
    }

    // kalibrace tlaku
    delay(500);
    referencePressure = mprls.readPressure();

    // start prevodu teploty
    oneWire.reset();
    oneWire.skip();
    oneWire.write(0x44);

    updateDisplay();
}

void loop() {
    // cteni tlacitka
    bool currentButtonState = digitalRead(BUTTON_PIN);
    
    // detekce stisku pro zastaveni
    if (currentButtonState == LOW && lastButtonState == HIGH) {
    isRunning = false;
    display.clear();
    display.setCursor(0, 2);
    display.print("STOP");
    delay(50);
    }
    lastButtonState = currentButtonState;

    // periodicke mereni
    if (isRunning && (millis() - lastSensorReadTime >= 800)) {
    lastSensorReadTime = millis();
    sampleCount++;

    // vycteni teploty
    oneWire.reset();
    oneWire.skip();
    oneWire.write(0xBE);
    int16_t raw = oneWire.read() | (oneWire.read() << 8);
    currentTempC = (float)raw / 16.0;

    // novy prevod
    oneWire.reset();
    oneWire.skip();
    oneWire.write(0x44);

    // vypocet hloubky
    float currentPressure = mprls.readPressure();
    currentDepth_m = ((currentPressure - referencePressure) * 100.0) / 9810.0;
    if (currentDepth_m < 0) currentDepth_m = 0;

    // ulozeni na sd
    if (sdReady) {
        File dataFile = SD.open("data.csv", FILE_WRITE);
        if (dataFile) {
        dataFile.print(sampleCount);
        dataFile.print(',');
        dataFile.print(currentTempC, 1);
        dataFile.print(',');
        dataFile.println(currentDepth_m, 3);
        dataFile.close();
        }
    }

    updateDisplay();
    }
}

void updateDisplay() {
    display.clear();
    
    // radek 1: teplota
    display.setCursor(0, 0);
    display.print(currentTempC, 1);
    display.print(" C");

    // radek 2: hloubka
    display.setCursor(0, 4);
    int displayDepth_mm = currentDepth_m * 1000;
    display.print(displayDepth_mm);
    display.print(" mm");
}

Pro následné zpracování dat na počítači jsem vytvořil Python skript. Ten načte soubor DATA.csv, převede hodnoty hloubky z metrů na milimetry a na data aplikuje klouzavý průměr (s oknem o velikosti 5 vzorků), čímž se vyhladí šum měření. Nakonec pomocí knihovny Matplotlib vykreslí grafy pro teplotu a hloubku. For subsequent data processing on a computer, I created a Python script. It reads the DATA.csv file, converts depth values from meters to millimeters, and applies a moving average (with a window size of 5 samples) to the data, which smooths out the measurement noise. Finally, using the Matplotlib library, it plots graphs for temperature and depth.

Python process_data.py
import matplotlib.pyplot as plt
import pandas as pd

window_size = 5

# nacteni dat z DATA.csv
try:
    df = pd.read_csv('DATA.csv')
except FileNotFoundError:
    print("DATA.csv nebyl nalezen.")
    exit()

# prevod hloubky z metru na milimetry
df['hloubka_mm'] = df['hloubka'] * 1000

# vypocet klouzaveho prumeru pro teplotu i převedenou hloubku
df['temp_avg'] = df['teplota'].rolling(window=window_size, center=True).mean()
df['depth_avg_mm'] = df['hloubka_mm'].rolling(window=window_size, center=True).mean()

# vytvoreni platna pro grafy
fig, (ax_temp, ax_depth) = plt.subplots(1, 2, figsize=(14, 6))

# vykresleni teploty
ax_temp.plot(df['vzorec'], df['teplota'], 'b-', alpha=0.3, label='Původní data')
ax_temp.plot(df['vzorec'], df['temp_avg'], 'b-', linewidth=2, label=f'Průměr (n = {window_size})')
ax_temp.set_title('Teplota')
ax_temp.set_xlabel('Číslo vzorku')
ax_temp.set_ylabel('Teplota (°C)')
ax_temp.grid(True, linestyle='--', alpha=0.7)
ax_temp.legend()

# vykresleni hloubky
ax_depth.plot(df['vzorec'], df['hloubka_mm'], 'r-', alpha=0.3, label='Původní data')
ax_depth.plot(df['vzorec'], df['depth_avg_mm'], 'r-', linewidth=2, label=f'Průměr (n = {window_size})')
ax_depth.set_title('Hloubka')
ax_depth.set_xlabel('Číslo vzorku')
ax_depth.set_ylabel('Hloubka (mm)')
ax_depth.grid(True, linestyle='--', alpha=0.7)
ax_depth.legend()

# finalni uprava a zobrazeni
plt.tight_layout()
plt.show()

Měření hloubky a teploty vody ve sklenici Measuring the depth and temperature of water in a glass

Funkčnost přístroje jsem otestoval ve sklenici s teplou vodou. Měření hloubky je velmi přesné (s odchylkou zhruba do ±2 mm). Zjistil jsem však, že po vytažení z vody zůstává konec měřicí trubičky zaplněn několika milimetry vody. Pokud se trubička před dalším měřením nevyfoukne, dojde ke špatnému stanovení referenčního atmosférického tlaku při kalibraci a všechny následně měřené hodnoty jsou pak posunuté. I tested the device's functionality in a glass of warm water. The depth measurement is very accurate (with a deviation of roughly up to ±2 mm). However, I found that after pulling it out of the water, the end of the measuring tube remains filled with a few millimeters of water. If the tube is not blown out before the next measurement, the reference atmospheric pressure is incorrectly determined during calibration, and all subsequently measured values are then shifted.

Displej zobrazující měřené hodnoty Sondy ve skleničce vody Grafy hloubky a teploty