Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Умная теплица с автоматическим климат-контролем на Arduino

Компоненты и материалы

Arduino MKR1000×1
Arduino UNO×1
Датчик температуры и влажности DHT22×1
Arduino WiFi Shield 101×1
Android-устройство×1
Шилд управления двигателем DC Motor Control Shield с BTN8982 (Infineon)×1

Программное обеспечение и онлайн-сервисы

О проекте

Обзор

Условия окружающей среды в теплице постоянно меняются, поэтому необходим эффективный мониторинг. Автоматизированная система приносит множество преимуществ: сокращение ручного труда, возможность удалённого контроля инвестиций со смартфона или ПК, хранение данных в базе для аналитики и принятия решений, а также мгновенное реагирование на проблемы в реальном времени.

Система состоит из датчиков (считывающих параметры среды) и исполнительных механизмов — «Slave»-устройств. Они обмениваются данными по беспроводной сети с центральным устройством — «Master». Тот, в свою очередь, передаёт настройки порогов на Slave и отправляет данные на веб-сервер через WiFi.

1. Принцип работы

Проект разделён на три части:

Master общается с веб-сервером по WiFi (встроенный модуль WINC1500). Slave считывает температуру и влажность через DHT22 и передаёт данные Master по WiFi (WiFi Shield WINC1500).

2. Master

Master работает в режиме точки доступа (Access Point) и ожидает подключения Slave для приёма данных. Получив пакет, он вычисляет CRC-8 (по алгоритму Dallas/Maxim) для валидации настроек порогов, которые затем отправляются Slave. После выхода из режима AP Master подключается к WiFi-сети и передаёт данные на веб-сервер для построения графиков.

3. Slave

Slave каждые 5 минут считывает показания DHT22, формирует UDP-пакет и отправляет его Master. Затем ожидает возможные новые пороговые значения. Получив их, Slave пересчитывает CRC и сравнивает с полученным. При совпадении новые настройки сохраняются в EEPROM.

4. Веб-сервер

Сервер сохраняет все поступающие данные для истории. Используются PHP-скрипты: один для записи в БД, другой — для отображения графиков (библиотека JpGraph), третий — для выдачи JSON-данных Android-приложению.

5. Мобильное приложение

Приложение позволяет выбирать временной диапазон и запрашивать график. Оно обращается к PHP-скрипту getdati, получает JSON и строит график с помощью библиотеки GraphView.

Код

Генерация графика (PHP + JpGraph)

<?php
define('__ROOT__', dirname(dirname(__FILE__)));
require_once ('../jpgraph.php');
require_once ('../jpgraph_line.php');
require_once ('../jpgraph_error.php');

$parameter1 = $parameter2 = $time_axis = array();
$i = 0;
$con = mysqli_connect("localhost","user","pass","my_db");
if (mysqli_connect_errno()) die("Failed to connect: " . mysqli_connect_error());

$result = mysqli_query($con, "SELECT parameter1, parameter2, date FROM your_raw");
while($row = mysqli_fetch_array($result)) {
    $parameter1[$i] = $row["parameter1"];
    $parameter2[$i] = $row["parameter2"];
    $time_axis[$i] = $row["date"];
    $i++;
}
mysqli_close($con);

$graph = new Graph(300,250);
$graph->SetScale("textlin");
$graph->SetTheme(new UniversalTheme());
$graph->img->SetAntiAliasing(true);
$graph->title->Set('Greenhouse Climate');
$graph->SetBox(false);
$graph->yaxis->HideZeroLabel();
$graph->xgrid->Show();
$graph->xgrid->SetLineStyle("solid");
$graph->xaxis->SetTickLabels($time_axis);
$graph->xgrid->SetColor('#E3E3E3');
$graph->xaxis->SetLabelAngle(90);
$graph->legend->SetPos(0.5,0.08,'center','top');

$p1 = new LinePlot($parameter1);
$p1->SetColor("#6495ED");
$p1->SetLegend('Temperature');
$graph->Add($p1);

$p2 = new LinePlot($parameter2);
$p2->SetColor("#B22222");
$p2->SetLegend('Humidity');
$graph->Add($p2);

$graph->Stroke();
?>

Скрипт приёма данных (PHP)

<?php
$servername = "localhost"; $username = "user"; $password = "pass"; $dbname = "my_db";
$id = $_GET["name"]; $temp = $_GET["parameter1"]; $umid = $_GET["parameter2"];

$conn = mysqli_connect($servername, $username, $password, $dbname);
if (!$conn) die("Connection failed: " . mysqli_connect_error());

$sql = "INSERT INTO greenhouse (name, parameter1, parameter2) VALUES ('$id', '$temp', '$umid')";
if (mysqli_query($conn, $sql)) echo "New record created";
else echo "Error: " . $sql . "
" . mysqli_error($conn); mysqli_close($conn); ?>

Создание таблицы (SQL)

CREATE TABLE IF NOT EXISTS `greenhouse` (
  `id` int(11) NOT NULL,
  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `temp` float NOT NULL,
  `umid` float NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Код Master (C/C++ для Arduino MKR1000)

/* Automation greenhouse project */
/* Authors: Antonio La Mura, Umberto Festa */
/* Date: 03/03/2016 */
#include 
#include 
#include 
#include 

char ssid[] = "SSID"; char pass[] = "pass"; int keyIndex = 0;
char ssid_ap[] = "Arduino"; char pass_ap[] = "";
int status = WL_IDLE_STATUS;
unsigned int localPort = 2390;
char server[] = "www.example.com";

WiFiServer server_ap(80); WiFiClient client_ap; WiFiUDP Udp; RTCZero rtc; WiFiClient client;
char packetBuffer[255]; char ReplyBuffer[255];

float humax=0, humin=0, tumax=0, tumin=0;
boolean flag = false; boolean threshold_available = false;

void setup() { Serial.begin(9600); while(!Serial); }

void loop() {
  int packetSize; double temp, hum; int id; byte crc; String strURL;
  if (WiFi.status() == WL_NO_SHIELD) { while(true); }

  while (status != WL_CONNECTED) {
    status = WiFi.beginAP(ssid_ap); delay(10000); server_ap.begin();
  }
  Udp.begin(localPort);
  client_ap = server_ap.available();
  if (client_ap) {
    packetSize = Udp.parsePacket();
    if (packetSize) {
      int len = Udp.read(packetBuffer, 255);
      if (len > 0) packetBuffer[len] = 0;
      char* cmd = strtok((char*)packetBuffer, ";");
      int count = 0;
      while (cmd != 0) {
        switch(count) {
          case 0: id = atoi(cmd); break;
          case 1: temp = atof(cmd)/10; break;
          case 2: hum = atof(cmd)/10; break;
        }
        cmd = strtok(0, ";"); count++;
      }
      // CRC calculation for thresholds
      byte bhmax=(byte)humax, bhmin=(byte)humin, btmax=(byte)tumax, btmin=(byte)tumin;
      byte crc_arr[4] = {bhmax, bhmin, btmax, btmin};
      crc = CRC8(crc_arr);
      if (threshold_available) {
        snprintf(ReplyBuffer, sizeof(ReplyBuffer), "%d;%d;%d;%d;%d;%d", id, (int)humax, (int)humin, (int)tumax, (int)tumin, (int)crc);
        Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
        Udp.write(ReplyBuffer); Udp.endPacket();
      }
    }
    client_ap.stop(); flag = true;
  }

  if (flag) {
    while (status != WL_CONNECTED) { status = WiFi.begin(ssid, pass); delay(10000); }
    strURL = "GET /YourAddress.php?id=" + String(id) + "¶meter1=" + String(temp) + "¶meter2=" + String(hum) + " HTTP/1.1";
    if (client.connect(server, 80)) {
      client.println(strURL);
      client.println("Host: www.example.com");
      client.println("Connection: close"); client.println();
      client.stop();
    }
    flag = false;
  }
}

byte CRC8(const byte *data) {
  byte crc = 0x00;
  while (*data) {
    byte extract = *data++;
    for (byte i=8; i; i--) {
      byte sum = (crc ^ extract) & 0x01;
      crc >>= 1; if (sum) crc ^= 0x8C;
      extract >>= 1;
    }
  } return crc;
}

Код Slave (C/C++ для Arduino UNO)

/* Slave code */
#include  #include  #include  #include  #include 

#define HUMIDIFIER A4 #define HEATER A5 #define DHT22_PIN 4
#define IN_1 3 #define IN_2 11 #define INH_1 12 #define INH_2 13
#define TCONST 100

extern unsigned long timer0_millis;
int status = WL_IDLE_STATUS;
char ssid[] = "Arduino"; char pass[] = "";
unsigned int localPort = 2390; char packetBuffer[255];
WiFiUDP Udp; DHT22 myDHT22(DHT22_PIN);

float hmin=0, hmax=0, tmin=0, tmax=0; int duty_motor=0;
float humidity, temperature; unsigned long interval = 600000;

void setup() {
  Serial.begin(9600); while(!Serial);
  pinMode(HEATER, OUTPUT); digitalWrite(HEATER, LOW);
  pinMode(HUMIDIFIER, OUTPUT); digitalWrite(HUMIDIFIER, LOW);
  pinMode(IN_1, OUTPUT); pinMode(IN_2, OUTPUT);
  pinMode(INH_1, OUTPUT); pinMode(INH_2, OUTPUT);
  reset_ports(); digitalWrite(INH_1, 1); digitalWrite(INH_2, 1);
  delay(2000);
}

void loop() {
  int packetSize = Udp.parsePacket();
  humidity = myDHT22.getHumidity()*10; temperature = myDHT22.getTemperatureC()*10;
  char toSend[32]; DHT22_ERROR_t errorCode = myDHT22.readData();
  byte crc; int crc_ric;

  if (millis() > interval) {
    // Connect to Master AP
    while (status != WL_CONNECTED) { status = WiFi.begin(ssid); delay(10000); }
    Udp.begin(localPort);
    snprintf(toSend, sizeof(toSend), "clie1;%d;%d", (int)humidity, (int)temperature);
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); Udp.write(toSend); Udp.endPacket();
    resetMillis();
    while (millis() < 10000) {
      packetSize = Udp.parsePacket();
      if (packetSize) {
        int len = Udp.read(packetBuffer, 255); if (len>0) packetBuffer[len]=0;
        char* cmd = strtok((char*)packetBuffer, ";"); int count=0;
        while (cmd != 0) {
          switch(count) {
            case 1: hmax = atof(cmd)/10; break;
            case 2: hmin = atof(cmd)/10; break;
            case 3: tmax = atof(cmd)/10; break;
            case 4: tmin = atof(cmd)/10; break;
            case 5: crc_ric = atoi(cmd); break;
          } cmd = strtok(0, ";"); count++;
        }
        // Verify CRC
        byte bhmax=(byte)hmax, bhmin=(byte)hmin, btmax=(byte)tmax, btmin=(byte)tmin;
        byte crc_arr[4] = {bhmax, bhmin, btmax, btmin};
        crc = CRC8(crc_arr);
        if (crc_ric == (int)crc) {
          EEPROM_writeDouble(0, tmax); EEPROM_writeDouble(4, tmin);
          EEPROM_writeInt(8, hmax); EEPROM_writeInt(10, hmin);
        }
      }
    }
    WiFi.disconnect();
  }

  // Control actuators
  if (myDHT22.getTemperatureC() >= tmax) digitalWrite(HEATER, HIGH);
  if (myDHT22.getTemperatureC() <= tmin+1) digitalWrite(HEATER, LOW);
  if ((int)myDHT22.getHumidity() >= hmax) digitalWrite(HUMIDIFIER, HIGH);
  if ((int)myDHT22.getHumidity() <= hmin+1) digitalWrite(HUMIDIFIER, LOW);
  // Fan control logic...
  delay(1000);
}

// EEPROM helpers, CRC8, resetMillis() — аналогичны Master

Получение данных для приложения (PHP)

<?php
$username="root"; $password=""; $hostname="localhost";
$dbhandle = mysql_connect($hostname, $username, $password) or die("Unable to connect");
mysql_select_db("serra",$dbhandle) or die("Could not select DB");
$datada = $_GET["datada"]; $dataa = $_GET["dataa"];
$result = mysql_query("SELECT hum,temp,date FROM record WHERE date >= '$datada' and date <= '$dataa'");
$records=array();
while ($row = mysql_fetch_array($result)) {
  $tmp = array("hum"=>$row{'hum'}, "temp"=>$row{'temp'}, "date"=>$row{'date'});
  array_push($records,$tmp);
}
echo json_encode($records); mysql_close($dbhandle);
?>

Производственный процесс

  1. Профессиональное руководство по анимации битмапов на TFT-экране ILI9341 2.4" с Visuino
  2. Веб-управляемая DMX-система освещения: Arduino Mega как мастер-контроллер
  3. Сборка Bluetooth-робота-шпиона на Arduino с управлением с телефона
  4. FlickMote: жестовое IR-пульт для умного дома на Arduino 101 и ультразвуковом датчике
  5. DIY Arduino TV-B-Gone: универсальный пульт для выключения любого телевизора
  6. Создание пользовательских LED-часов с будильником — видно с 12 метров
  7. HSV Clock: Яркие, настраиваемые часы для энтузиастов Arduino
  8. Android-управляемая робот-платформа на Raspberry Pi и Motor Shield
  9. Find Me: Умный поисковик вещей на Arduino и Bluetooth
  10. Оптимизированные решения питания для проектов на Arduino