Данная разработка является продолжением модификации метеостанции, описанной в статье «Метеостанция на основе Ардуино». Данная модификация позволяет мониторить показания метеостанции через браузер компьютера или мобильного устройства подключенных к домашней локальной сети.

Сразу оговорюсь, как я говорил изначально я не являюсь продвинутым программистом (профиль моей работы немного иной ;-)), поэтому конструктивная критика и предложения приветствуются.

Основой данной модификации является сервер на основе микрокомпьютера Raspberry PI4, находящийся в домашней локальной сети.

Ниже представлена блок-схема метеостанции, где указанно какое ПО и языки программирования использовались для разработки.

Имя  
Эл. почта  
Сообщение  
  
//Приёмник.
//I2C Slave 8. 
//Arduino Uno, датчик температуры терморезистор TTC 103 (аналог), 
//ЖК дисплей ST7920 , блок LoRa XL1278-SMT.

#include <Wire.h> // подключаем библиотеку для работы с шиной I2C

#include <SPI.h> //Библиотека для подключение внешних устройств

#include <RH_RF95.h> // Библиотека для работы с блоком LoRa.

#include <math.h> //Библиотека для простых мат вычислений

// Подпрограмма для вычисления температуры аналогового датчика.
  double Tehrmister(int RawADC){
  double Temp;
  Temp=log(((10240000/RawADC)-10000));
  Temp=1/(0.001129148+(0.000234125*Temp)+(0.0000000876741*Temp*Temp*Temp));
  Temp=Temp-273.15; //Кельвин в Цельсий
  return Temp;
  }

#include <U8glib.h>      // Подключаем библиотеку U8glib

 U8GLIB_ST7920_128X64_1X u8g(5); //(13, 11, 5, U8G_PIN_NONE); // настройка пинов u8g( SCK,MOSI,CS[, RST ])
 

 RH_RF95 rf95(7, 2); // Подключаем блк LoRa 7 - выбор чипа NSS (пин на ардуино)
                     // 2 - прерывание (пин на ардуино) 

 // Создаём массив данных 
 struct SEND_DATA{ 
    int Temperature;
    double Pozicion;  ///////////////////////////////////////////////////
    int Skorost;
    // int Skorost_max;
    boolean Perimetr;
    int Vlazhnost;  
    int Press;
    int Alt;
};
SEND_DATA mydata;

// Переменные

int Pozicion1;

String Azm = ("С");  // Отоброжаемое состояние направления ветра - Азимут
int TemperDom;       // Отоброжение температуры в доме
String Alarm;        // Отоброжаемое состояние периметра охраны
float switchPin = 3; // пин Вкл. охраны периметра
String Alarm_sw;     // Отоброжаемое сосояние включения сигнализации

int temperature1;
boolean znak;


// Переменные для хранения состояния кнопки
int lastButton = 0;
int currentButton = 0;

// функция для подавления дребезга
 int debounse(int last) {
 int current = analogRead(switchPin);
 if(last != current) {
 delay(5);
 current = analogRead(switchPin);
 }
 return current;
 }

 int piezoPin = 8; //Пищалка
 boolean a1 = 0;  // состояние кнопки включения пищалки

uint32_t myTimer1;

    int Sk = 0;
    int sk1 = 0;
    int sk2 = 1;
int Skorost_sr; 


void setup() {
 Wire.begin(8);// инициируем подключение к шине I2C в качестве ведомого (slave) устройства, с указанием своего адреса на шине.
  Wire.onRequest(requestEvent); // Регистрация события. После поступления запроса от Master запускаеться функция requestEvent
 
 pinMode(switchPin, INPUT);

 pinMode(10, OUTPUT);

 Serial.begin(9600); //Открывает последовательный порт, на скорости 9600 бит/с

  while (!Serial);
  if (!rf95.init()) { 
    
 //В случае не выполнения условия написать LoRa failed!
 
    u8g.firstPage();  // Всё что выводится на дисплей указывается в цикле: 
                      // u8g.firstPage(); do{ ... команды ... }while(u8g.nextPage());
    do{                                    
         u8g.setFont(u8g_font_7x13);               // Выбираем шрифт u8g_font_6x10
         u8g.setColorIndex(1);          // Выбираем цвет белый
         u8g.setPrintPos( 24, 35);u8g.print("LoRa failed!"); // Выводим текст "LoRa failed!" 
                                                             // в позиции  24х35
     
     }while(u8g.nextPage());
     delay(15000);    
     }else{
      u8g.firstPage();  // Всё что выводится на дисплей указывается в цикле: 
                      // u8g.firstPage(); do{ ... команды ... }while(u8g.nextPage());
    do{                                    
         u8g.setFont(u8g_font_7x13);               // Выбираем шрифт u8g_font_6x10
         u8g.setColorIndex(1);          // Выбираем цвет белый
         u8g.setPrintPos( 44, 35);u8g.print("LoRa!"); // Выводим текст "LoRa!" 
                                                             // в позиции  44х35
     
     }while(u8g.nextPage());
     delay(6500);
      };
   
   rf95.setTxPower(1);
   rf95.setFrequency(433.00);
   rf95.setModemConfig(RH_RF95::Bw125Cr48Sf4096 );


}

void loop() {
    
 currentButton = debounse(lastButton);
 if(lastButton <= 512 && currentButton >= 512) 
 lastButton = currentButton;


  TemperDom = double(Tehrmister(analogRead(0)));  
  


 if (rf95.available()) 
  {
    uint8_t len = sizeof(mydata);
    if (rf95.recv((uint8_t*)&mydata, &len))   
     
     
    
    if (0 <= mydata.Pozicion && 11.25 > mydata.Pozicion)
          Azm = ("C");
    if (11.25 <= mydata.Pozicion && 33.75 > mydata.Pozicion)
          Azm = ("CCB");
    if (33.75 <= mydata.Pozicion && 56.25 > mydata.Pozicion)
          Azm = ("CB");
    if (56.25 <= mydata.Pozicion && 78.75 > mydata.Pozicion)
          Azm = ("BCB");
    if (78.75 <= mydata.Pozicion && 101.25 > mydata.Pozicion)
          Azm = ("B");
    if (101.25 <= mydata.Pozicion && 123.75 > mydata.Pozicion)
          Azm = ("ВЮВ"); // ВЮВ
    if (123.75 <= mydata.Pozicion && 146.25 > mydata.Pozicion)
          Azm = ("ЮВ"); // ЮВ
    if (146.25 <= mydata.Pozicion && 168.75 > mydata.Pozicion)
          Azm = ("ЮЮB"); // ЮЮВ
    if (168.75 <= mydata.Pozicion && 191.25 > mydata.Pozicion)
          Azm = ("Ю"); // Ю
    if (191.25 <= mydata.Pozicion && 213.75 > mydata.Pozicion)
          Azm = ("ЮЮЗ"); // ЮЮЗ
    if (213.75 <= mydata.Pozicion && 236.25 > mydata.Pozicion)
          Azm = ("ЮЗ"); // ЮЗ
    if (236.25 <= mydata.Pozicion && 258.75 > mydata.Pozicion)
          Azm = ("ЗЮЗ"); // ЗЮЗ
    if (258.75 <= mydata.Pozicion && 281.25 > mydata.Pozicion)
          Azm = ("З");  // З
    if (281.25 <= mydata.Pozicion && 303.75 > mydata.Pozicion)
          Azm = ("ЗСЗ"); // 3C3
    if (303.75 <= mydata.Pozicion && 326.25 > mydata.Pozicion)
          Azm = ("СЗ");  // СЗ
    if (326.25 <= mydata.Pozicion && 348.75 > mydata.Pozicion)
          Azm = ("ССЗ");  //CC3
    if (348.75 <= mydata.Pozicion && 360.00 > mydata.Pozicion)
          Azm = ("C");

     Skorost_sr = (mydata.Skorost);
    
   //Serial.print ("Температура = ");
  // Serial.println (mydata.Temperature);
   
    if (!mydata.Perimetr==0) 
      Alarm = ("Авария");
      else Alarm = ("Норма");
      
    if(analogRead(switchPin) < 512){   // Кнопка нажата
      Alarm_sw = ("ВКЛ");a1 = 1;}      
      else {Alarm_sw = ("ВЫКЛ");a1 = 0;}

     if(a1==1 && mydata.Perimetr==1) // Если нажата кнопка 
                                      // и периметр Авария то
      {
        
        // Пищалка 
        
          tone (piezoPin,1000,600);
            delay(600);         
          tone (piezoPin,800,600);
            delay(4000);   
          
      }
    
 // Выводим данные на экран.
 
    u8g.firstPage();  // Всё что выводится на дисплей указывается в цикле: 
                      // u8g.firstPage(); do{ ... команды ... }while(u8g.nextPage());
    do{         
         u8g.setFont(rus6x12);          // Выбираем шрифт
         u8g.setColorIndex(1);          // Выбираем белый цвет 
    // Отображение температуры
         u8g.drawStr( 0, 9,"Температура ул.= ");
         u8g.setPrintPos(98, 9);
         u8g.print(mydata.Temperature);  // Вывод значения температуры на улице
         u8g.drawPixel(116,1);
         u8g.drawPixel(117,1);
         u8g.drawPixel(118,1);
         u8g.drawPixel(116,2);
         u8g.drawPixel(118,2);
         u8g.drawPixel(116,3);
         u8g.drawPixel(117,3);
         u8g.drawPixel(118,3);
         u8g.drawStr( 120, 9,"С");
      // Отображение влажности воздуха
         u8g.drawStr( 0, 20,"Влажность = ");
         u8g.setPrintPos(72, 20);
         u8g.print(mydata.Vlazhnost);        // Вывод значения влажности на улице
         u8g.drawStr( 90, 20,"%");
      // Отображение направления ветра - Азимута 
         u8g.drawStr( 0, 31,"Аз.= ");
         u8g.setPrintPos(30, 31);
         u8g.print(Azm);
      // Отображение скорости ветра
         u8g.drawStr( 54, 31,"Ск.в.=");
         u8g.setPrintPos(96, 31);
         u8g.print(Skorost_sr);        // Вывод значения скорости ветра
         u8g.drawStr( 110, 31,"м/с");
      // Отображение давления
         u8g.drawStr( 0, 42,"Давлен. =");
         u8g.setPrintPos(58, 42);
         u8g.print(mydata.Press);        // Вывод значения давления на улице
         u8g.drawStr( 80, 42,"мм.рт.ст");
      // Отображение температуры в доме
         u8g.drawStr( 0, 53,"Температура дом =");
         u8g.setPrintPos(105, 53);
         u8g.print(TemperDom);  // Вывод значения температуры в доме
         u8g.drawPixel(118,44);
         u8g.drawPixel(119,44);
         u8g.drawPixel(120,44);
         u8g.drawPixel(118,45);
         u8g.drawPixel(120,45);
         u8g.drawPixel(118,46);
         u8g.drawPixel(119,46);
         u8g.drawPixel(120,46);
         u8g.drawStr( 122, 53,"С");
      // Отоброжение состояния периметра
         u8g.drawStr( 0, 63,"Перим.=");
         u8g.setPrintPos(48, 63);
         u8g.print(Alarm);
         u8g.setPrintPos(100, 63);
         u8g.print(Alarm_sw);
         }while(u8g.nextPage()); 
      delay(100); 
}
} 

// функция, которая выполняется всякий раз, когда данные запрашиваются мастером
// эта функция регистрируется как событие, см. setup () 

void requestEvent() {
  
  Pozicion1 = static_cast<int>(mydata.Pozicion); // Преобразование данных в целочисленное

  if (mydata.Temperature < 0){  
    temperature1 = mydata.Temperature * -1;
    znak = false;
    Wire.write (highByte (temperature1));   
    Wire.write (lowByte (temperature1));
       
    } 
    else {
      znak = true;
      Wire.write (highByte (mydata.Temperature));   
      Wire.write (lowByte (mydata.Temperature));} 
  Wire.write (znak);
  Wire.write (highByte (Pozicion1));   
  Wire.write (lowByte (Pozicion1));

  Wire.write (highByte (mydata.Skorost));   
  Wire.write (lowByte (mydata.Skorost)); 

  Wire.write(mydata.Perimetr);

  Wire.write (highByte (mydata.Vlazhnost));   
  Wire.write (lowByte (mydata.Vlazhnost)); 

  Wire.write (highByte (mydata.Press));   
  Wire.write (lowByte (mydata.Press)); 

  Wire.write (highByte (mydata.Alt));   
  Wire.write (lowByte (mydata.Alt)); 
  // как и ожидалось мастером 
}

Файлы проекта можно скачать с яндекс диска.

Напишите отзыв.

Сервер на Raspberry PI4 для метеостанции.

     Блок метеостанции описан в статье «Метеостанция на основе Ардуино», поэтому здесь не рассматривается.

Блок приёмной части метеостанции (в схеме Монитор метеостанции) также описан в указанной выше статье, но в нем произведена небольшая модернизация, а именно подключение шины I2C. По данной шине передаются данные от метеостанции к серверу, по запросу от сервера. То есть блок «монитор метеостанции» является ведомым на шине I2C, соответственно мастером является сервер.

     Ниже представлен измененный код на С++ для блока Arduino UNO («монитор метеостанции»).

     В данном скетче добавлена дополнительная функция, которая включается после поступления запроса от ведущего устройства. В данном случае ведомое устройство имеет адрес 8.

     По шине I2C в одном пакете можно передать 1 байт информации, однако метеостанция передаёт данные в словах (по 2 байта). Поэтому слова разделяются и побайтно (сначала старший байт, а затем младший байт) передаются по шине I2C.

     Далее немного расскажем о совместимости шин I2C. Дело в том, что интерфейс I2C платы Ардуино имеет напряжение 5В, в то время как шина I2C Raspberry имеет напряжение 3.3В. Поэтому между портами ардуино и raspberry устанавливается блок преобразователя уровней, ниже на схеме указано подключение такого блока. 

Сервер.

     Вот и добрались до основного. Как уже упоминалось сервер собран на основе микрокомпьютера Rasberry PI 4. Операционная система, установленная на микрокомпьютере - Linux Ubuntu Server 20.04. Как установить систему рассказывалось в статье «Создаём файловый сервер на Raspberry Pi». В этой же статье рассказано как установить и настроить FTP и как подключиться к серверу через терминальную программу.

     Далее нужно активировать порт I2C. Для этого нам понадобится утилита raspi-config. Вводим команду:

 

               sudo raspi-config

 

     Переходим в пункт Interfacing options / Advanced Options и включаем I2C.

     Перезагружаем

 

              sudo reboot

 

     Дальнейшие действия проводим удалённо с помощью терминальной программы.

Далее устанавливаем пакет «i2c-tools» этот пакет позволяет подавать команды через терминал для работы c I2C.

 

             sudo apt-get install -y i2c-tools

 

Для проверки работоспособности введём команду вывода доступных шин I2C.

 

             sudo i2cdetect –l

Удостоверимся, что сервер видит ведомое устройство.

 

             sudo i2cdetect -y 1

Итак, шина I2C задействована. Далее в проекте используется программа, написанная на Python. Данная программа взаимодействует с шиной I2C (передаёт запрос в ведомое устройство, а затем принимает данные), поле этого данные преобразуются и сохраняются в переменных, после этого создаётся/переписывается файл JavaScript с внесёнными данными принятыми по I2C.

Запускаем обновление репозиториев и пакетов операционной системы.

 

            sudo apt update && apt upgrade –y

 

Посмотрим какой пакет Python3 установлен.

 

            python3 -V

Но Python3 пока не может работать с I2C, поэтому поставим пакет «python3-smbus».

sudo apt-get -y install python3-smbus

Также в проекте на сервере присутствует HTML страничка, для того чтобы можно было с ней работать (загружать в браузер) нужно установить пакет «HTTP-сервер Apache».

 

           sudo apt install apache2

 

Необходимо изменить настройки брандмауэра, чтобы разрешить доступ к веб-портам по умолчанию. Установка брандмауэра UFW описана в статье «Создаём файловый сервер на Raspberry Pi».

Выведем список профилей.

 

           sudo ufw app list

Есть три профиля, доступных для Apache:

     •   Apache: этот профиль открывает только порт 80 (нормальный веб-трафик без шифрования)

     •   Apache Full: этот профиль открывает порт 80 (нормальный веб-трафик без шифрования) и порт 443 (трафик с шифрованием           TLS/SSL)

     •   Apache Secure: этот профиль открывает только порт 443 (трафик с шифрованием TLS/SSL)

Рекомендуется применять самый ограничивающий профиль, который будет разрешать заданный трафик.

 

          sudo ufw allow 'Apache'

 

Посмотрим список разрешенного трафика HTTP.

 

          sudo ufw status

     Проверим работу службы

 

          sudo systemctl status apache2

     Для проверки запустим страницу Apache. Для этого в браузере удалённого компьютера надо ввести IP адрес сервера (настройка IP адреса сервера рассматривалась в статье «Создаём файловый сервер на Raspberry Pi») или ввести команду.

  

          hostname –I

Продолжим настройку Apache. Произведём настройку виртуального хоста

Создадим домен meteost (вы должны заменить это имя собственным доменным именем)

 

          sudo mkdir /var/www/ html/meteost.ru/public_html

 

Назначим владение директорией

 

          sudo chown -R $USER:$USER /var/www/ html/meteost.ru/public_html

 

Чтобы убедиться, что разрешения корректны, позволить владельцу читать, писать и запускать файлы, а группам и другим пользователям разрешить только читать и запускать файлы, вы можете ввести следующую команду

 

          sudo chmod -R 755 /var/www/ html/meteost.ru/public_html

 

Далее создадим файл index.html по адресу: /var/www/html/meteost.ru/public_html

Файлы html, js и py удобнее переносить с удалённого компьютера с помощью программы FileZilla.

Можно также создать файл штатным способом.

 

          sudo vi /var/www/html/meteost.ru/public_html/ index.html

 

          sudo vi /var/www/html/meteost.ru/public_html/js/znach.js

 

Содержимое файлов рассмотрим немного позже. Пока их можно создать пустыми.

Далее создадим файл виртуального хоста. Создадим копию конфигурационного файла по умолчанию (не обязательно).

 

          sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf.orig

 

Создадим новый конфигурационный файл

 

          sudo vi /etc/apache2/sites-available/ meteost.ru.conf

 

Заполним файл следующим содержимым

 

          <VirtualHost *:80>

          ServerName meteost.com

          ServerAlias www.meteost.com

          ServerAdmin webmaster@localhost

          DocumentRoot /var/www/html/meteost.ru/public_html

          ErrorLog ${APACHE_LOG_DIR}/error.log

          CustomLog ${APACHE_LOG_DIR}/access.log combined

          </VirtualHost>

 

Сохраним файл (wq).

Активируем файл.

 

          sudo a2ensite meteost.ru.conf

 

Отключим сайт по умолчанию.

 

          sudo a2dissite 000-default.conf

 

Проверим ошибки конфигурации.

 

           sudo apache2ctl configtest

 

Перезапустим Apache.

 

           sudo systemctl restart apache2

 

Теперь с удалённого компьютера локальной сети html страница доступна по IP адресу сервера.

Ну вот и закончили со встроенным ПО, далее рассмотрим файлы созданные собственно в рамках проекта.

Файл написанный на Python.

     Как уже говорилось выше файл (i2c_Data.py), написанный на Python, считывает информацию с I2C и преобразует их. Вот здесь нужно кое-что пояснить. Дело в том, что данные по шине I2C передаются по 1 байту. Поэтому первый принятый байт подвергаем побитному смещению влево на 8 бит, затем добавляем второй принятый байт посредствам логического ИЛИ. Полученное Слово сохраняем в переменную. Так последовательно принимаем все данные от метеостанции и сохраняем в переменных.

 

     Далее файл (i2c_Data.py) создаёт файл znach.js (написанный на JavaScript) по адресу /var/www/html/meteost.ru/public_html/js, в созданный файл прописываются данные от метеостанции.

 

     После этого шина I2C закрывается и включается пауза на 900 сек (15 мин).

Файл i2c_Data.py выполняется в бесконечном цикле.

 

     Ниже приведено содержимое файла i2c_Data.py (его можно создать в блокноте с разрешением .py).

   # Server. Master.  
   # Получаем данные по I2C, преобразуем, расчитываем значения, записываем в файл. 

filename = '/var/www/html/meteost.ru/public_html/js/znach.js' # Путь к создаваемому файлу znach.js

while True:

  from smbus import SMBus       # Импортируем модуль SMBus для работы с I2C.
  bus = SMBus(1)                # Для raspberry pi4 значение SMBus должно быть 1.

  from time import *            # Импортируем модуль таймера.
  from datetime import *        # Импортируем модуль системного времени

  i = 1
  Skorost_1,Skorost_m = 0,0

  while i < 50 :          # Цикл для расчёта среднего и максимального значения скорости ветра.
 
        # Запрашиваем блок из устройства 8 в колличестве 14 значений данных. 
      # Полученные данные сохраняем в переменные data(1-13).

    data1,data2,data2_1,data3,data4,data5,data6,data7,data8,data9,data10,data11,data12,data13 = bus.read_i2c_block_data(0x8, 0x0C, 14)

        # В переменных храняться данные из полуслова, в 2 переменных одно слово. 
      # Поэтому значение 1 переменной (11111111) смещаем 
        # на 8 бит влево (1111111100000000) и добавляем знакение 2 переменной
      # с помощью логического ИЛИ (1111111111111111).
    Temperature = data1 << 8
    Temperature = Temperature | data2
    znak = data2_1
    
    Pozicion = data3 << 8
    Pozicion = Pozicion | data4
    Skorost = data5 << 8
    Skorost = Skorost | data6
    Perimetr = data7
    Vlazhnost = data8 << 8
    Vlazhnost = Vlazhnost | data9
    Press = data10 << 8
    Press = Press | data11

     # Вычисляем среднюю и максимальную скорость ветра.
  
    Skorost_1 = Skorost_1 + Skorost    
  
    if Skorost_m < Skorost :
      Skorost_m = Skorost 
    sleep(1)                           # Пауза 1 сек.
    i+=1
  Skorost_sr = int(Skorost_1 / 50)
  
  today = datetime.today()
  today1 = today.strftime('%X')
  today2 = today.strftime('%x')
  
  
   # Определение направления ветра по азимуту (0-360)

  if 0 <= Pozicion and 11 > Pozicion :
    Azm = 'С'
  if 11<= Pozicion and 34> Pozicion :
    Azm = 'ССВ'
  if 34 <= Pozicion and 56 > Pozicion :
    Azm = 'СВ'
  if 56 <= Pozicion and 79 > Pozicion :
    Azm = 'ВСВ'
  if 79 <= Pozicion and 101 > Pozicion :
    Azm = 'В'
  if 101 <= Pozicion and 124 > Pozicion :
    Azm = 'ВЮВ'
  if 124 <= Pozicion and 146 > Pozicion :
    Azm = 'ЮВ'
  if 146 <= Pozicion and 169 > Pozicion :
    Azm = 'ЮЮВ'
  if 169 <= Pozicion and 191 > Pozicion :
    Azm = 'Ю'
  if 191 <= Pozicion and 214 > Pozicion :
    Azm = 'ЮЮЗ'
  if 214 <= Pozicion and 236 > Pozicion :
    Azm = 'ЮЗ'
  if 236 <= Pozicion and 259 > Pozicion :
    Azm = 'ЗЮЗ'
  if 259 <= Pozicion and 281 > Pozicion :
    Azm = 'З'
  if 281 <= Pozicion and 304 > Pozicion :
    Azm = 'ЗСЗ'
  if 304 <= Pozicion and 326 > Pozicion :
    Azm = 'СЗ'
  if 326 <= Pozicion and 349 > Pozicion :
    Azm = 'ССЗ'
  if 349 <= Pozicion and 360 > Pozicion :
    Azm = 'С'

   # Выводим на экран параметры метеомтанции.

  #print('\n')
  #print('\tПараметры метеостанции.')
  #print('\n')
  #print('\n\tТемпература = ',Temperature,' C')
  #print('\n\tНаправление ветра = ',Azm)
  #print('\n\tСкорость ветра максимальная = ',Skorost_m,' м/с')
  #print('\n\tСкорость ветра средняя = ',Skorost_sr,' м/с')
  #print('\n\tСостояние охранного периметра (0-норма,1-авария) = ',Perimetr)
  #print('\n\tВлажность воздуха = ',Vlazhnost,' %')
  #print('\n\tДавление воздуха = ',Press,' мм.рт.ст')
  #print('\n\tДата и время измерения показаний метеостанции: ',today1,'   ',today2)
  #print('\n')
  
  # Запись в файл.
  if znak == 0:                     # Определяем какой знак передан, если передано значение 0
    Temperature = Temperature * -1  # знаит число отрицательное. Умножаем значение температуры на -1
  Temperature1 = str(Temperature)
  Skorost_m1 = str(Skorost_m)
  Skorost_sr1 = str(Skorost_sr)
  Perimetr1 = str(Perimetr)
  Vlazhnost1 = str(Vlazhnost)
  Press1 = str(Press) 
  
  with open(filename, 'w') as f:
    f.write("var temper = '" + Temperature1 + "';")
    f.write("\nvar napr = '" + Azm + "';")
    f.write("\nvar skor_m = " + Skorost_m1 + "; ")
    f.write("\nvar skor_s = " + Skorost_sr1 + "; ")
    f.write("\nvar vlash = '" + Vlazhnost1 + "'; ")
    f.write("\nvar davl = '" + Press1 + "'; ")
    f.write("\nvar vrm = '" + today1 + "'; ")
    f.write("\nvar vrm1 = '" + today2 + "'; ")
    f.write("\nvar elt = document.getElementById('temper'); ")
    f.write("\nelt.textContent = temper;")
    f.write("\nvar elv = document.getElementById('vlash');")
    f.write("\nelv.textContent = vlash;")
    f.write("\nvar eln = document.getElementById('napr'); ")
    f.write("\neln.textContent = napr;")
    f.write("\nvar els_s = document.getElementById('skor_s'); ")
    f.write("\nels_s.textContent = skor_s;")
    f.write("\nvar els_m = document.getElementById('skor_m'); ")
    f.write("\nels_m.textContent = skor_m;")
    f.write("\nvar eld = document.getElementById('davl'); ")
    f.write("\neld.textContent = davl; ")
    f.write("\nvar eld_t = document.getElementById('vrm'); ")
    f.write("\neld_t.textContent = vrm; ")
    f.write("\nvar eld_t1 = document.getElementById('vrm1'); ")
    f.write("\neld_t1.textContent = vrm1; ")
    

  bus.close()
  
  sleep(900)
  


     Далее, помощью программы FileZilla (FTP менеджер), скопируем файл «i2c_Data.py» в сервер по адресу: /home/ubuntu.

 

Осталось запустить данный файл в фоновом режиме.

 

Для этого создадим сервис который будет запускать файл i2c_Data.py в фоновом режиме, а также запускаться после перезагрузки сервера. Используем для этого systemd.service.

 

          sudo vi /etc/systemd/system/meteost.service

 

Содержимое файла заполним следующим:

 

          [Unit]

          Description=My meteost service

          After=multi-user.target

 

          [Service]

          User=root

          Group=root

          Type=simple

          Restart=always

          ExecStart=/usr/bin/python3 /home/ubuntu/i2c_Data.py

 

          [Install]

          WantedBy=multi-user.target

 

     ExecStart - в основном первый аргумент — это путь к python (в моем случае это python3), а второй аргумент — это путь к скрипту, который необходимо выполнить. Флаг перезапуска всегда установлен, чтобы перезапустить свою службу, если сервер будет перезапущен.

 

     Перезагрузим демон. 

 

          sudo systemctl daemon-reload

 

Включим сервис, чтобы он не отключался при перезагрузке сервера.

 

          sudo systemctl enable meteost.service

 

Запустим сервис.

 

          sudo systemctl start meteost.service

 

Чтобы проверить статус. 

 

          sudo systemctl status meteost.service

Алгоритм файла i2c_Data.py выполняется примерно 1 мин, значит файл znach.js будет обновляться каждые 16 мин.

 

На этом с файлом i2c_Data.py закончим.

Web страница с данными Метеостанции.

      При вводе в браузере (внешнего компьютера) IP адреса сервера будет вызываться html страница с данными метеостанции.

 

     Html код страницы (index.html) приведён ниже. Html код взаимодействует с JavaScript (созданный файлом i2c_Data.py), поэтому папка (js) c JavaScript (znach.js) располагается в одной папке с файлом index.html по адресу: /var/www/html/meteost.ru/public_html

      Вот, пожалуй и всё, перезагружаем сервер и наслаждаемся данными метеостанции в браузере.

P.S. Кстати если кто желает наблюдать свою страничку через сеть интернет, а не только в локалке. То для этого нужно у своего интернет провайдера получить белый IP адрес (данная услуга бывает платной), затем настроить роутер и сделать проброс портов в роутере.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Метеостанция</title>
  <style type="text/css">
    body {
      width: 500px;
      font-family: Arial, Verdana, sans-serif;
      font-size: 90%;
      color: #666;
      background-color: #f8f8f8;
      margin: 10px auto auto auto;}
    h1 {margin: 0px auto 0px 130px;}
    table {
      border-spacing: 0px;
      margin: 0px auto 0px 18px;}
    th, td {
      padding: 5px 30px 5px 10px;
      border-spacing: 0px;
      font-size: 90%;
      margin: 0px;}
    th, td {
      text-align: left;
      background-color: #e0e9f0;
      border-top: 1px solid #cbd2d8;
      border-right: 1px solid #cbd2d8;}   
    tr.head th {
      color: fff;
      background-color: #90b4d6;
      border-bottom: 2px solid #547ca0;
      border-right: 1px solid #749abe;
      border-top: 1px solid #90b4d6;
      text-align: center;
      text-shadow: -1px -1px 1px #666;
      letter-spacing: 0.15em;}
    td {
      text-shadow: 1px 1px 1px #fff;
      text-align: center;}  
    tr.even td, tr.even th {
      background-color: #e8eff5;}
    tr.head th:first-child {
      border-top-left-radius: 5px;}
    tr.head th:last-child {
      border-top-right-radius: 5px;}
    fieldset {
      width: 310px;
      margin-top: 20px;
      border: 1px solid #d6d6d6;
      background-color: #ffffff;
      line-height: 1.6em;}
    legend {
      font-style: italic;
      color: #666666;}
    input[type="text"] {
      width: 120px;
      border: 1px solid #d6d6d6;
      padding: 2px;
      outline: none;}
    input[type="text"]:focus, input[type="text"]:hover {
      background-color: #d0e2f0;
      border: 1px solid #999;}
    input[type="submit"] {
      border: 1px solid #006633;
      background-color: #009966;
      color: #ffffff;
      border-radius: 5px;
      padding: 5px;
      margin-top: 10px;}
    input[type="submit"]:hover {
      border: 1px solid #006633;
      background-color: #00cc33;
      color: #ffffff;
      cursor: pointer;}
    .title {
      float: left;
      width: 160px;
      clear: left;}
    .submit {
      width: 310px;
      text-align: right;}   
    td.lef {text-align: left;}
  </style>
</head>
<body>
  <h1>Метеостанция</h1>
  <p>В таблице отображаются параметры переданные с внешнего (уличного) блока метеостанции.</p>
  <table>
    <tr class="head">
      <th>Параметр</th>
      <th>Значение</th>
      <th>Ед. изм.</th>
    </tr>
    <tr>
      <th>Температура на улице</th>
      <td id="temper"></td>
      <td class="lef">&#176С</td>   
    </tr>
    <tr class="even">
      <th>Влажность на улице</th>
      <td id="vlash"></td>
      <td class="lef">%</td>    
    </tr>
    <tr>
      <th>Направление ветра</th>
      <td id="napr"></td>
      <td></td>   
    </tr>
    <tr class="even">
      <th>Скорость ветра (сред.)</th>
      <td id="skor_s"></td>
      <td class="lef">м/с</td>    
    </tr>
    <tr class="even">
      <th>Скорость ветра (макс.)</th>
      <td id="skor_m"></td>
      <td class="lef">м/с</td>    
    </tr>
    <tr>
      <th>Атм. Давление</th>
      <td id="davl"></td>
      <td class="lef">мм.рт.ст</td>   
    </tr>
    <tr>
      <th></th>
      <td></td>
      <td></td>
          
    </tr>
    <tr>
      <th>Дата и время измерения показаний метеостанции</th>
      <td id="vrm"></td>
      <td id="vrm1"></td>
          
    </tr>
  </table>
  
  <script src="js/znach.js"></script> 
</body>
</html>