Panel logo

Kvazitec Weather Monitoring

Описание проекта

Kvazitec Weather Monitoring — автономная система мониторинга погодных условий, разрабатываемая на основе микроконтроллера Arduino. Система будет собирать данные с датчиков температуры, влажности, атмосферного давления, напряжения и тока, а затем передавать их по радиоканалу на сервер для анализа и прогнозирования погоды, контроля состояния системы.

Ключевые особенности

Технические детали

Проект реализуется на базе микроконтроллера Arduino с использованием следующих компонентов:

Схемы подключения:

📥 TX.ino (передатчик) 📥 RX.ino (приёмник) 📥 bot.py (Telegram бот) 📥 kvaziweather.zip (все файлы одним архивом)

Код передатчика (Arduino)

#include <SPI.h>          // библиотека для работы с шиной SPI
#include <Adafruit_Sensor.h>
#include "nRF24L01.h"     // библиотека радиомодуля
#include "RF24.h"         // ещё библиотека радиомодуля
#include <Adafruit_BME280.h>
#include <Adafruit_INA219.h>
#include <DHT.h>
#include <DHT_U.h>
#define DHTPIN 2
#define SEALEVELPRESSURE_HPA (1013.25)

DHT dht(DHTPIN, DHT22);

Adafruit_BME280 bme;

Adafruit_INA219 ina219;

RF24 radio(9, 10); // "создать" модуль на пинах 9 и 10 Для Уно
//RF24 radio(9,53); // для Меги

byte address[][6] = {"1Node", "2Node", "3Node", "4Node", "5Node", "6Node"}; //возможные номера труб

float tranzmit[8];

void setup() {
  Serial.begin(9600);         // открываем порт для связи с ПК
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  digitalWrite(4, 1);
  digitalWrite(5, 0);
  if (!bme.begin(0x76)) 
  {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
    if (! ina219.begin()) {       
    Serial.println("Failed to find INA219 chip");  
    while (1) { delay(10); }
  }
  dht.begin();
  radio.begin();              // активировать модуль
  radio.setAutoAck(1);        // режим подтверждения приёма, 1 вкл 0 выкл
  radio.setRetries(0, 15);    // (время между попыткой достучаться, число попыток)
  radio.enableAckPayload();   // разрешить отсылку данных в ответ на входящий сигнал
  radio.setPayloadSize(32);   // размер пакета, в байтах

  radio.openWritingPipe(address[0]);  // мы - труба 0, открываем канал для передачи данных
  radio.setChannel(0x70);             // выбираем канал (в котором нет шумов!)

  radio.setPALevel (RF24_PA_MAX);   // уровень мощности передатчика. На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
  radio.setDataRate (RF24_250KBPS); // скорость обмена. На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
  //должна быть одинакова на приёмнике и передатчике!
  //при самой низкой скорости имеем самую высокую чувствительность и дальность!!

  radio.powerUp();        // начать работу
  radio.stopListening();  // не слушаем радиоэфир, мы передатчик
}

void loop() {
  tranzmit[0] = bme.readTemperature();
  tranzmit[1] = bme.readHumidity();
  tranzmit[2] = bme.readPressure() / 100.0F;
  float alt = bme.readAltitude(SEALEVELPRESSURE_HPA);
  tranzmit[3] = dht.readHumidity(); //Измеряем влажность
  float tin = dht.readTemperature(); //Измеряем температуру
  tranzmit[4] = tin; 
  float shuntvoltage = ina219.getShuntVoltage_mV();       // Получение напряжение на шунте 
  float busvoltage = ina219.getBusVoltage_V();
  tranzmit[5] = busvoltage;            // Получение значение напряжения V
  tranzmit[6] = ina219.getCurrent_mA();              // Получение значение тока в мА
  //float power_mW = ina219.getPower_mW();                  // Получение значение мощности
  tranzmit[7] = busvoltage + (shuntvoltage / 1000); // Расчет напряжение на нагрузки
  radio.write(&tranzmit, sizeof(tranzmit));
  if (tin < 10.00) {
    digitalWrite(5, 1);
  } else {
    digitalWrite(5, 0);
  }
}

Код приёмника (Arduino)

#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

RF24 radio(4, 5);  // "создать" модуль на пинах 9 и 10 Для Уно
//RF24 radio(9,53); // для Меги

float received_data[8];
String send;
unsigned long ntime;

byte address[][6] = {"1Node", "2Node", "3Node", "4Node", "5Node", "6Node"}; //возможные номера труб

LiquidCrystal_I2C lcd(0x27,16,2);  // Устанавливаем дисплей

void setup() {
  Serial.begin(9600);         // открываем порт для связи с ПК
  radio.begin();              // активировать модуль
  radio.setAutoAck(1);        // режим подтверждения приёма, 1 вкл 0 выкл
  radio.setRetries(0, 15);    // (время между попыткой достучаться, число попыток)
  radio.enableAckPayload();   // разрешить отсылку данных в ответ на входящий сигнал
  radio.setPayloadSize(32);   // размер пакета, в байтах

  radio.openReadingPipe(1, address[1]);   // хотим слушать трубу 0
  radio.setChannel(0x70);     // выбираем канал (в котором нет шумов!)

  radio.setPALevel (RF24_PA_MAX);   // уровень мощности передатчика. На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
  radio.setDataRate (RF24_250KBPS); // скорость обмена. На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
  //должна быть одинакова на приёмнике и передатчике!
  //при самой низкой скорости имеем самую высокую чувствительность и дальность!!

  radio.powerUp();        // начать работу
  radio.startListening(); // начинаем слушать эфир, мы приёмный модуль
  lcd.init();                     
  lcd.backlight();// Включаем подсветку дисплея
  ntime = millis();
}

void loop() {
  byte pipeNo;
  while (radio.available(&pipeNo)) {        // слушаем эфир со всех труб
    radio.read( &received_data, sizeof(received_data) );  // чиатем входящий сигнал
  }
  
  float temp = round(received_data[0]*100)/100;
  float humb = round(received_data[1]*100)/100;
  float press = round(received_data[2]*100)/100;
  float hin = round(received_data[3]*100)/100;
  float tin = round(received_data[4]*100)/100;
  float busvoltage = round(received_data[5]*100)/100;
  float current = round(received_data[6]*100)/100;
  float loadvoltage = round(received_data[7]*100)/100;
  if (millis - ntime > 10000) { // задержка между отправками строки с данными в COM-порт
  send = "";
  send = send+temp+humb+press+hin+tin+busvoltage+current+loadvoltage;
  Serial.println(send);
  }

  
  if (analogRead(1) < 341) {
    // Устанавливаем курсор на вторую строку и нулевой символ.
  lcd.setCursor(0, 0);
  // Выводим на экран количество секунд с момента запуска ардуины
  lcd.print("T ");
  lcd.print(temp);
  lcd.setCursor(0, 1);
  lcd.print("P ");
  lcd.print(press);
  lcd.setCursor(10, 0);
  lcd.print("H ");
  lcd.print(humb);
  lcd.setCursor(7, 0);
  // Выводим на экран количество секунд с момента запуска ардуины
  lcd.print("  ");
  } 
  if ((analogRead(1) < 683) & (analogRead(1) > 341)) {
      lcd.setCursor(0, 0);
      lcd.print("TiN ");
      lcd.print(tin);
      lcd.setCursor(0, 1);
      lcd.print("HiN ");
      lcd.print(hin);
      lcd.setCursor(10, 0);
      lcd.print("      ");
    } 
    if ((analogRead(1) < 1024) & (analogRead(1) > 683)) {
       lcd.setCursor(6, 0);
       lcd.print("   ");
       lcd.setCursor(8, 1);
       lcd.print("   ");
       lcd.setCursor(0, 0);
       lcd.print("V ");
       lcd.print(loadvoltage);
       lcd.setCursor(0, 1);
       lcd.print("I ");
       lcd.print(current);
       lcd.setCursor(10, 0);
       lcd.print("      ");
      }
}

Python-скрипт для Telegram-бота

"""Для нормальной работы скрипта требуется наличие в директории данного скрипта следующих файлов:
1. Пустой текстовый файл userids.txt
2. Текстовый файл tokenf.txt, содержащий ТОЛЬКО токен вашего telegram-бота, который вы можете получить, создав бота
 с помощью BotFather в Telegram
 Также должны быть установлены сторонние библиотеки PyTelegramBotAPI, PySerial"""
import telebot
import serial
import time
from telebot import types
useridsf = open('userids.txt', 'r')
userids = useridsf.readlines()
useridsf.close()
for i in range(1, len(userids)):
    userids[i] = int(userids[i])
print(userids)
ntime = time.time()
status = 1
temp = 0
humb = 0
press = 0
hin = 0
tin = 0
debug =  #ввести ID чата отладки
busvoltage = 0
current = 0
loadvoltage = 0
try:
    port = "COM4"  # Replace with the appropriate COM port name
    baudrate = 9600
    ser = serial.Serial(port, baudrate=baudrate, bytesize=8)
except:
    status = 0
def datahandler():
    #Здесь данные, полученные из COM-порта должны становится
    #нормальными списками из 8 элементов каждый. В списках должны содержаться передаваемые погодные данные
    global status
    try:
            # Read a line of data from the serial port
        line_in = ser.readline().decode().strip()
        if line_in:
            print(line_in)
        global temp
        global humb
        global press
        global hin
        global tin
        global busvoltage
        global loadvoltage
        global current
        zindexs = []
        for i1 in range(0, len(line_in)):
            if line_in[i1] == '.':
                zindexs.append(i1)
        print(zindexs)
        temp = line_in[0:zindexs[0]+3]
        humb = line_in[zindexs[0]+3:zindexs[1]+3]
        press = line_in[zindexs[1]+3:zindexs[2]+3]
        hin = line_in[zindexs[2]+3:zindexs[3]+3]
        tin = line_in[zindexs[3]+3:zindexs[4]+3]
        busvoltage = line_in[zindexs[4]+3:zindexs[5]+3]
        loadvoltage = line_in[zindexs[5]+3:zindexs[6]+3]
        current = line_in[zindexs[6]+3:zindexs[7]+3]
    except:
        status = 0
def error(mess):
    global status
    bot.send_message(mess.chat.id, 'На данный момент ни одна из метеостанций Kvazitec, к сожалению, не способна предоставить данные. После получения данных хотя бы с одной из них вам будет отправлено уведомление. Приятного ожидания!')
    bot.send_message(mess.chat.id, '⌛')
    bot.send_message(debug, 'Error')
    status = 1
tokenf = open('tokenf.txt', 'r')
toke = tokenf.readline()
bot = telebot.TeleBot(toke)
@bot.message_handler(commands=['start'])
def starter(mess):
    global status
    try:
        userids.index(mess.chat.id)
    except:
        useridsf = open('userids.txt', 'a')
        useridsf.write('\n'+str(mess.chat.id))
        useridsf.close()
        userids.append(mess.chat.id)
    if status == 0:
        error(mess)
    elif status == 1:
        datahandler()
        if status == 1:
            markup = types.InlineKeyboardMarkup()
            markup.add(types.InlineKeyboardButton('KW V1 54.1', callback_data='v1.54.1'))
            bot.send_message(mess.chat.id,'Доброго дня! Вам доступны погодные данные с метеостанций Kvazitec, список которых вы видите ниже. Для получения данных с какой-либо метеостанции нажмите на кнопку с её названием', reply_markup=markup)
        elif status == 0:
            error(mess)
    print(userids)
@bot.callback_query_handler(func=lambda call: True)
def caller(call):
    global status
    if call.data == 'v1.54.1':
        datahandler()
        if status == 1:
            markup1 = types.InlineKeyboardMarkup()
            markup1.add(types.InlineKeyboardButton('Обновить данные', callback_data='v1.54.1'))
            bot.send_message(call.message.chat.id,'Данные об окружающем воздухе:' + '\n' + 'Температура: ' + temp + '°C'+'\n' + 'Влажность: ' + humb + '%' + '\n' + 'Давление: ' + press + ' кПа', reply_markup=markup1)
        elif status == 0:
            error(call.message)
bot.polling(non_stop=True)

Ссылка на GitHub

Исходный код проекта доступен по адресу: Kvazitec/KvaziWeather