Введение

Привет, читатель, меня зовут Морозов Алексей, и в этой статье я наглядно покажу, как сделать метеостанцию! За работу.

Идея и ответы на почему

Я хочу сделать простую и модульную метеостанцию с консольным выводом данных

  • Сделать плату-опросчик, которая читает данные с датчиков и отправляет их в порт.

  • Сделать скрипт на питоне, который будет обрабатывать и выводить значения в консоль

Почему модульность?

Разделение метеостанции на два уровня, низкий(опрос датчиков) и высокий(обработка и вывод данных) позволяет быстро вносить изменения в систему, добавлять новые датчики или даже менять сами устройства.

Почему именно консольный вывод?

TUI - отличная отправная точка для вывода данных, можно быстро менять формат вывода, добавлять новые диаграммы и даже окна.

Подбор компонентов

  1. В качестве платы опросчика, подойдёт arduino nano, у неё достаточно мощности и портов, чтобы развивать метеостанцию до предела

  2. В качестве датчика я решил взять htu21d, который умеет определять температуру и влажность воздуха. Отличная плата для начала, потом заменю на bmp280

Подключение

Приступим к сборке метеостанции! Вот схема подключения:

схема подключения
схема подключения
Пример сборки тут.
Ардуино нано
Ардуино нано
Ардуино нано в корпусе
Ардуино нано в корпусе
dht21d в корпусе
dht21d в корпусе
dht21d вид внутри корпуса
dht21d вид внутри корпуса

Код для ардуино

Вот код для считывания данных с датчика и отправки их в порт:

#include <GyverHTU21D.h>

GyverHTU21D htu;

void getTemp(){  
  Serial.print("Temp:");
  Serial.print(htu.getTemperature());
  Serial.print(",");
  Serial.print("Humd:");
  Serial.println(htu.getHumidity());
}

void setup() {
  Serial.begin(115200);
  htu.begin();
}

void loop() {
  if (htu.readTick()) {
    getTemp();
  }
}
вывод данных
вывод данных

Если открыть вывод в ардуино иде, то мы увидим вот такую картину.

Код на питон

Вот код скрипта на питон, для парсинга и вывода данных в виде графика:

import serial
import plotext as plt
import time
ser = serial.Serial('/dev/ttyUSB0', 115200)

def read_and_parse():
    
    response = ser.readline()

    if not response:
        return None, None
    
    try:
        decoded_response = response.decode('utf-8')
    
        input_string = decoded_response.rstrip('\n')

    
        # Разбиваем строку на пары ключ:значение
        pairs = input_string.split(',')
        if len(pairs) != 2:
            print("Ошибка: строка должна содержать ровно две пары ключ:значение")
            return None, None

        
        expected_key1 = "Temp"  
        expected_key2 = "Humd"  

        value1 = None
        value2 = None

        for pair in pairs:
            parts = pair.split(':', 1)  # Разбиваем только по первому ':'
            if len(parts) != 2:
                print(f"Ошибка: некорректный формат пары: {pair}")
                return None, None

            key, value = parts[0].strip(), parts[1].strip()

            if key == expected_key1:
                value1 = value
            elif key == expected_key2:
                value2 = value
            else:
                print(f"Предупреждение: неизвестный ключ '{key}' найден в строке")

        # Проверяем, нашли ли оба ожидаемых значения
        if value1 is None or value2 is None:
            print("Ошибка: не найдены значения для одного или обоих ожидаемых ключей")
            return None, None

        return value1, value2

    except Exception as e:
        print(f"Произошла ошибка при парсинге: {e}")
        return None, None
    
def plot_data(temp, humd):
    x = ["Температура", "Влажность"]
    # Значения
    y = (float(temp), float(humd))
    colors = ["cyan", "red"] 
    plt.clear_data()
    
    plt.bar(x, y, color=colors, orientation="horizontal", width=3/5)
    plt.title("Данные с датчика")
    plt.ylabel("Значение")
    plt.clt()
    plt.show() 

def main():
    print("Начало работы! Нажмите Ctrl+C для завершения.")
    try:
        while True:
            temp, humd = read_and_parse()
            
            # Рисуем только если данные успешно получены
            if temp is not None and humd is not None:
                plot_data(temp, humd)
                
            # Небольшая пауза, чтобы не нагружать процессор и дать время Arduino
            time.sleep(0.1) 
            
    except KeyboardInterrupt:
        print("\nЗавершение работы...")
    finally:
        ser.close()

if __name__ == "__main__":
    main()

Обратите внимание!

На 4 строчке, если вы работаете на windows, то нужно поменять с /dev/ttyUSB0 на COMX(заменить на номер порта куда подключена ваша плата, можно посмотреть в ардуино иде)

Тесты

Всё исправно работает!

вывод данных
вывод данных

Завершение

Всё заработало - это успех.

Идеи по доработке:

  1. Поставить датчик давления(bmp280)

  2. сделать GUI, например на PyQt6 + Qt creator

Спасибо за внимание!