然后使用您的水培控制系统收集数据,并使用它来监控和做出更明智的决策。查看趋势并了解实际情况!
在读取温度、湿度、土壤湿度或任何水培数据后,我们需要首先使用 WiFi 连接将数据发布到数据库。这是通过使用简单的 REST API 完成的。REST 或具象状态传输是使用带有 JSON(JavaScript 对象表示法)格式的有效负载的 HTTP 方法 (POST)。
void HttpPost(const char *url, String &post_data)
{
HTTPClient http;
http.begin(url);
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
int http_code = http.POST(post_data); // Send the request
String payload = http.getString(); // Get the response payload
SerialDebug.println(http_code); // Print HTTP return code
SerialDebug.println(payload); // Print request response payload
if (payload.length() > 0) {
int index = 0;
do
{
if (index > 0) index++;
int next = payload.indexOf('\n', index);
if (next == -1) break;
String request = payload.substring(index, next);
if (request.substring(0, 9).equals(")) break;
SerialDebug.println(request);
StaticJsonDocument<100> doc;
DeserializationError error = deserializeJson(doc, request);
if (!error) {
if (doc["OVERRIDE_LIGHTS_TIME"]) OVERRIDE_LIGHTS_TIME = doc["OVERRIDE_LIGHTS_TIME"];
if (doc["OVERRIDE_LIGHTS"]) OVERRIDE_LIGHTS = doc["OVERRIDE_LIGHTS"];
if (doc["OVERRIDE_VENT_FAN_TIME"]) OVERRIDE_VENT_FAN_TIME = doc["OVERRIDE_VENT_FAN_TIME"];
if (doc["OVERRIDE_VENT_FAN"]) OVERRIDE_VENT_FAN = doc["OVERRIDE_VENT_FAN"];
}
index = next;
} while (index >= 0);
}
http.end(); // Close connection
}
...
void loop() {
...
char buffer[80];
strftime(buffer, sizeof(buffer), "%m/%d/%Y %H:%M:%S", &rtc);
// Allocate JsonDocument
// Use arduinojson.org/assistant to compute the capacity
StaticJsonDocument<500> doc;
// Create the root object
doc["ReadingTime"] = buffer;
doc["InsideTemp"] = (inside.error) ? ERROR_READ : inside.temp;
doc["InsideRelative"] = (inside.error) ? ERROR_READ : inside.relative;
doc["InsideAbsolute"] = (inside.error) ? ERROR_READ : inside.absolute;
doc["OutsideTemp"] = (outside.error) ? ERROR_READ : outside.temp;
doc["OutsideRelative"] = (outside.error) ? ERROR_READ : outside.relative;
doc["OutsideAbsolute"] = (outside.error) ? ERROR_READ : outside.absolute;
doc["VentFan"] = vent_fan;
doc["Lights"] = lights;
doc["Power"] = power;
doc["DailyCost"] = cost;
doc["ColorTemp"] = color_temp;
doc["Lux"] = lux;
doc["CO2"] = co2;
doc["CO2Temp"] = co2_temp;
doc["CO2Relative"] = co2_relative;
doc["GerminationTemp"] = germination_temp;
doc["ChillerTemp"] = chiller_temp;
doc["pH"] = pH;
doc["DO"] = DO;
JsonArray array = doc.createNestedArray("GrowBed");
for (i = 0; i < sizeof(grow_bed_table) / sizeof(GROWBED_t); i++) {
JsonObject object = array.createNestedObject();
object["WaterTemp"] = (grow_bed_table[i].water_temp_error) ? ERROR_READ : grow_bed_table[i].water_temp;
object["WaterTDS"] = grow_bed_table[i].water_tds;
object["WaterLevel"] = grow_bed_table[i].water_level;
}
String json_data;
serializeJson(doc, json_data);
post_data = "data=" json_data;
SerialDebug.println(post_data);
#ifdef MySQL
HttpPost(mysql_url, post_data);
#endif
#ifdef MSSQL
HttpPost(mssql_url, post_data);
#endif
...
}
您可以使用两种不同的流行的用户控制的数据库平台。
您还可以使用 MQTT 客户端使用任何 IoT 服务,但此处不解释该方法。
对于 Windows 虚拟主机,我一直在使用WinHost。他们提供每月只需 3.95 美元的基本计划,应该适合包括我自己在内的大多数家庭爱好者的预算,但您可以使用任何您喜欢的 Windows 虚拟主机。我已经使用了多年,对他们的产品和服务非常满意。
优点
缺点
使用SQL Server Management Studio连接到您的 Microsoft SQL Server 并使用以下 SQL 脚本创建数据库和表。
CREATE TABLE [dbo].[Hydroponics](
[ReadingTime] [datetime] NOT NULL,
[InsideTemp] [DECIMAL](9, 2) NULL,
[InsideRelative] [DECIMAL](9, 2) NULL,
[InsideAbsolute] [DECIMAL](9, 2) NULL,
[OutsideTemp] [DECIMAL](9, 2) NULL,
[OutsideRelative] [DECIMAL](9, 2) NULL,
[OutsideAbsolute] [DECIMAL](9, 2) NULL,
[VentFan] [bit] NULL,
[Lights] [bit] NULL,
[POWER] [SMALLINT] NULL,
[GrowBed1WaterTemp] [DECIMAL](9, 2) NULL,
[GrowBed1WaterTDS] [SMALLINT] NULL,
[GrowBed1WaterLevel] [bit] NULL,
[GrowBed2WaterTemp] [DECIMAL](9, 2) NULL,
[GrowBed2WaterTDS] [SMALLINT] NULL,
[GrowBed2WaterLevel] [bit] NULL,
[DailyCost] [DECIMAL](9, 2) NULL,
[ColorTemp] [INT] NULL,
[Lux] [INT] NULL,
[CO2] [DECIMAL](9, 2) NULL,
[CO2Temp] [DECIMAL](9, 2) NULL,
[CO2Relative] [DECIMAL](9, 2) NULL,
[GerminationTemp] [DECIMAL](9,2) NULL,
[ChillerTemp] [DECIMAL](9,2) NULL,
[pH] [DECIMAL](9,2) NULL,
[DO] [DECIMAL](9,2) NULL,
PRIMARY KEY CLUSTERED
(
[ReadingTime] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Request](
[RequestTime] [DATETIME] NOT NULL,
[JsonData] [VARCHAR](MAX) NULL,
[Processed] [BIT] NULL,
PRIMARY KEY CLUSTERED
(
[RequestTime] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
使用Microsoft IIS 管理器设置和管理您的网站。使用以下 Active Server Page Extended 'adddata.aspx' 连接和传输数据负载。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Script;
using System.Web.Script.Serialization;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
namespace Hydroponics
{
public partial class adddata : System.Web.UI.Page
{
class GrowBed
{
public decimal WaterTemp { get; set; }
public int WaterTDS { get; set; }
public bool WaterLevel { get; set; }
}
class HydroponicsData
{
public DateTime ReadingTime { get; set; }
public decimal InsideTemp { get; set; }
public decimal InsideRelative { get; set; }
public decimal InsideAbsolute { get; set; }
public decimal OutsideTemp { get; set; }
public decimal OutsideRelative { get; set; }
public decimal OutsideAbsolute { get; set; }
public bool VentFan { get; set; }
public bool Lights { get; set; }
public int Power { get; set; }
public decimal DailyCost { get; set; }
public int ColorTemp { get; set; }
public int Lux { get; set; }
public decimal CO2 { get; set; }
public decimal CO2Temp { get; set; }
public decimal CO2Relative { get; set; }
public decimal GerminationTemp { get; set; }
public decimal ChillerTemp { get; set; }
public List GrowBed { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
var data = new JavaScriptSerializer().Deserialize(Request["data"].ToString());
String strSQL = "INSERT INTO Hydroponics (ReadingTime,"
"InsideTemp,InsideRelative,InsideAbsolute,"
"OutsideTemp,OutsideRelative,OutsideAbsolute,"
"VentFan,Lights,Power,DailyCost,"
"ColorTemp,Lux,"
"CO2,CO2Temp,CO2Relative,"
"GerminationTemp,ChillerTemp,"
"GrowBed1WaterTemp,GrowBed1WaterTDS,GrowBed1WaterLevel,"
"GrowBed2WaterTemp,GrowBed2WaterTDS,GrowBed2WaterLevel)"
" VALUES ("
"'" data.ReadingTime.ToString() "',";
if (data.InsideTemp >= 0) strSQL += data.InsideTemp.ToString() "," data.InsideRelative.ToString() "," data.InsideAbsolute.ToString() ",";
else strSQL += "NULL,NULL,NULL,";
if (data.OutsideTemp >= 0) strSQL += data.OutsideTemp.ToString() "," data.OutsideRelative.ToString() "," data.OutsideAbsolute.ToString() ",";
else strSQL += "NULL,NULL,NULL,";
strSQL += ((data.VentFan) ? "1" : "0") "," ((data.Lights) ? "1" : "0") "," data.Power.ToString() "," data.DailyCost.ToString() ",";
if (data.ColorTemp >= 0) strSQL += data.ColorTemp.ToString() "," data.Lux.ToString() ",";
else strSQL += "NULL,NULL,";
if (data.CO2 >= 0) strSQL += data.CO2.ToString() "," data.CO2Temp.ToString() "," data.CO2Relative.ToString() ",";
else strSQL += "NULL,NULL,NULL,";
if (data.GerminationTemp >= 0) strSQL += data.GerminationTemp.ToString() ",";
else strSQL += "NULL,";
if (data.ChillerTemp >= 0) strSQL += data.ChillerTemp.ToString() ",";
else strSQL += "NULL,";
if (data.GrowBed[0].WaterTemp >= 0) strSQL += data.GrowBed[0].WaterTemp.ToString() ",";
else strSQL += "NULL,";
if (data.GrowBed[0].WaterTDS >= 0) strSQL += data.GrowBed[0].WaterTDS.ToString() ",";
else strSQL += "NULL,";
strSQL += ((data.GrowBed[0].WaterLevel) ? "1" : "0") ",";
if (data.GrowBed[1].WaterTemp >= 0) strSQL += data.GrowBed[1].WaterTemp.ToString() ",";
else strSQL += "NULL,";
if (data.GrowBed[1].WaterTDS >= 0) strSQL += data.GrowBed[1].WaterTDS.ToString() ",";
else strSQL += "NULL,";
strSQL += ((data.GrowBed[1].WaterLevel) ? "1" : "0") ")";
try
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ServerConnectionString"].ConnectionString);
con.Open();
SqlCommand cmd = new SqlCommand(strSQL, con);
cmd.ExecuteNonQuery();
strSQL = "SELECT * FROM Request WHERE Processed IS NULL ORDER BY RequestTime ASC";
SqlCommand req = new SqlCommand(strSQL, con);
SqlDataAdapter sda = new SqlDataAdapter(req);
DataTable dtRequest = new DataTable();
sda.Fill(dtRequest);
if (dtRequest.Rows.Count > 0)
{
for (int rows = 0; rows < dtRequest.Rows.Count; rows++)
{
if (dtRequest.Rows[0]["JsonData"] != DBNull.Value)
{
Response.Write(dtRequest.Rows[0]["JsonData"].ToString());
Response.Write("\n");
}
}
DateTime dt = (DateTime)dtRequest.Rows[dtRequest.Rows.Count-1]["RequestTime"];
strSQL = "UPDATE Request SET Processed=1 WHERE Processed IS NULL AND RequestTime <='" dt.ToString("yyyy-MM-dd HH:mm:ss.fff") "'";
SqlCommand upd = new SqlCommand(strSQL,con);
upd.ExecuteNonQuery();
}
con.Close();
}
catch (SqlException sqlex)
{
Response.Write(sqlex.Message.ToString() "\r\n");
}
}
}
}
使用 IIS 管理器添加一个连接字符串“ServerConnectionString”,它将允许 .aspx 网页连接到您的数据库。
到目前为止,我们应该每分钟都在捕获数据。为了动态查看数据库数据,我们将使用Grafana。 这个动态图形工具将允许您修改和添加图表,帮助您查看时间序列数据以管理您的水培系统。
在您的计算机上下载并安装 Grafana或获取免费的托管 Grafana 实例并创建 localhost 服务器。
登录到 Grafana 后,使用 MSSQL 创建数据库连接,然后导入以下脚本以创建 Garage Hydroponics 仪表板。
要允许来自 grafana 的请求,例如打开灯或风扇,请添加以下“addrequest.aspx”,以便在下次发送有效负载时做出响应。这意味着一旦选择了该选项,可能需要一分钟的时间才能处理该操作。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
namespace Hydroponics
{
public partial class addrequest : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Request["data"] != null)
{
String strSQL = "INSERT INTO Request (RequestTime, JsonData) VALUES (GETUTCDATE(),'" + Request["data"].ToString() + "')";
try
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ServerConnectionString"].ConnectionString);
con.Open();
SqlCommand cmd = new SqlCommand(strSQL, con);
cmd.ExecuteNonQuery();
con.Close();
}
catch (SqlException sqlex)
{
Response.Write(sqlex.Message.ToString() + "\r\n");
}
}
ClientScript.RegisterStartupScript(typeof(Page), "closePage", "window.close();", true);
}
}
}
使用新的 Raspberry Pi 4 作为服务器。随着 4GB RAM、USB 3.0 和 1Gb 以太网的加入,Raspberry Pi 4 终于成为低成本家庭服务器的真正竞争者。
优点
缺点
x1 CanaKit Raspberry Pi2 4GB 入门套件 - 4GB RAM。
x1东芝 HDTB410EK3AA 1TB 2.5" USB 3.0 黑色。
下载并安装最新版本的 Raspberry Pi OS。
确保操作系统从 USB 3.0 硬盘启动。
使用 PHP 下载并安装 Apache Web 服务器。
下载并安装 MySQL 数据库服务器。
下载并安装 PHPMyAdmin 以轻松管理 MySQL。
下载并安装 Grafana 服务器。
将以太网设置为您的家庭静态 IP 地址。
下载并安装用于远程访问的 VNC 查看器。
将您的 Raspberry Pi 4 设置为无头服务器(无键盘/鼠标/显示器)。
使用 phpMyAdmin 创建一个名为“mydata”的新数据库,并使用 SQL 脚本创建下表。
CREATE TABLE Hydroponics(
ReadingTime DATETIME NOT NULL,
InsideTemp DECIMAL(9, 2) NULL,
InsideRelative DECIMAL(9, 2) NULL,
InsideAbsolute DECIMAL(9, 2) NULL,
OutsideTemp DECIMAL(9, 2) NULL,
OutsideRelative DECIMAL(9, 2) NULL,
OutsideAbsolute DECIMAL(9, 2) NULL,
VentFan BIT NULL,
Lights BIT NULL,
Power SMALLINT NULL,
GrowBed1WaterTemp DECIMAL(9, 2) NULL,
GrowBed1WaterTDS SMALLINT NULL,
GrowBed1WaterLevel BIT NULL,
GrowBed2WaterTemp DECIMAL(9, 2) NULL,
GrowBed2WaterTDS SMALLINT NULL,
GrowBed2WaterLevel BIT NULL,
DailyCost INT NULL,
ColorTemp INT NULL,
Lux INT NULL,
CO2 DECIMAL(9, 2) NULL,
CO2Temp DECIMAL(9, 2) NULL,
CO2Relative DECIMAL(9, 2) NULL,
GerminationTemp DECIMAL(9,2) NULL,
ChillerTemp DECIMAL(9,2) NULL,
pH DECIMAL(9,2) NULL,
DO DECIMAL(9,2) NULL,
PRIMARY KEY(ReadingTime)
);
CREATE TABLE Request(
RequestTime DATETIME NOT NULL,
JsonData VARCHAR(4096) NULL,
Processed BIT NULL,
PRIMARY KEY(RequestTime)
);
在 Apache Web 服务器中使用以下 PHP 文件“adddata.php”连接到 MySQL 并注入 JSON 数据负载。
$servername = "localhost";
$dbname = "mydata";
$username = "admin";
$password = "password";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$data = json_decode($_POST["data"]);
// Create database connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
ini_set("date.timezone", "UTC");
$sql = "INSERT INTO Hydroponics (ReadingTime," .
"InsideTemp,InsideRelative,InsideAbsolute," .
"OutsideTemp,OutsideRelative,OutsideAbsolute," .
"VentFan,Lights,Power,DailyCost," .
"ColorTemp,Lux," .
"CO2,CO2Temp,CO2Relative," .
"GerminationTemp,ChillerTemp," .
"pH,DO," .
"GrowBed1WaterTemp,GrowBed1WaterTDS,GrowBed1WaterLevel," .
"GrowBed2WaterTemp,GrowBed2WaterTDS,GrowBed2WaterLevel) " .
"VALUES (" .
"STR_TO_DATE('" . $data->ReadingTime . "','%m/%d/%Y %H:%i:%s'),";
if ($data->InsideTemp >= 0) $sql .= $data->InsideTemp . "," . $data->InsideRelative . "," . $data->InsideAbsolute . ",";
else $sql .= "NULL,NULL,NULL,";
if ($data->OutsideTemp >= 0) $sql .= $data->OutsideTemp . "," . $data->OutsideRelative . "," . $data->OutsideAbsolute . ",";
else $sql .= "NULL,NULL,NULL,";
$sql .= ($data->VentFan ? "1":"0") . "," . ($data->Lights ? "1":"0") . "," . $data->Power . "," . $data->DailyCost . ",";
if ($data->ColorTemp >= 0) $sql .= $data->ColorTemp . "," . $data->Lux . ",";
else $sql .= "NULL,NULL,";
if ($data->CO2 >= 0) $sql .= $data->CO2 . "," . $data->CO2Temp . "," . $data->CO2Relative . ",";
else $sql .= "NULL,NULL,NULL,";
if ($data->GerminationTemp >= 0) $sql .= $data->GerminationTemp . ",";
else $sql .= "NULL,";
if ($data->ChillerTemp >= 0) $sql .= $data->ChillerTemp . ",";
else $sql .= "NULL,";
if ($data->pH >= 0) &sql .= $data->pH . ",";
else $sql .= "NULL,";
if ($data->DO >= 0) &sql .= $data->DO . ",";
else $sql .= "NULL,";
if ($data->GrowBed[0]->WaterTemp >= 0) $sql .= $data->GrowBed[0]->WaterTemp . ",";
else $sql .= "NULL,";
if ($data->GrowBed[0]->WaterTDS >= 0) $sql .= $data->GrowBed[0]->WaterTDS . ",";
else $sql .= "NULL,";
$sql .= ($data->GrowBed[0]->WaterLevel ? "1":"0") . ",";
if ($data->GrowBed[1]->WaterTemp >= 0) $sql .= $data->GrowBed[1]->WaterTemp . ",";
else $sql .= "NULL,";
if ($data->GrowBed[1]->WaterTDS >= 0) $sql .= $data->GrowBed[1]->WaterTDS . ",";
else $sql .= "NULL,";
$sql .= ($data->GrowBed[1]->WaterLevel ? "1":"0") . ")";
if ($conn->query($sql) == FALSE) {
echo "Error: " . $sql . "\r\n" . $conn->error . "\r\n";
}
$sql = "SELECT * FROM Request WHERE Processed IS NULL ORDER BY RequestTime ASC";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$lasttime = "";
while($row = $result->fetch_assoc()) {
echo $row["JsonData"] . "\n";
$lasttime = $row["RequestTime"];
}
$sql = "UPDATE Request SET Processed=1 WHERE Processed IS NULL AND RequestTime <= '" . $lasttime . "'";
$conn->query($sql);
}
$conn->close();
}
else {
echo "No data posted with HTTP POST.";
}
在 Pi 4 上的端口 3000 登录 Grafana 后,创建与 MySQL 的数据库连接,然后导入以下脚本以创建 Garage Hydroponics 仪表板。
注意:为了让 Grafana 检索正确的时间序列数据,请确保将 MySQL 中的默认时区设置为 UTC。
要允许来自 grafana 的请求,例如打开灯或风扇,请添加以下 PHP 文件“addrequest.php”,该文件将允许在下次发送有效负载时做出响应。这意味着一旦选择了该选项,可能需要一分钟的时间才能处理该操作。
$servername = "localhost";
$dbname = "mydata";
$username = "admin";
$password = "mysql";
if ($_SERVER["REQUEST_METHOD"] == "GET") {
// Create database connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
ini_set("date.timezone", "UTC");
$sql = "INSERT INTO Request (RequestTime,JsonData) " .
"VALUES (" .
"UTC_TIMESTAMP(),'" . $_GET["data"] . "')";
if ($conn->query($sql) == FALSE) {
echo "Error: " . $sql . "\r\n" . $conn->error;
}
$conn->close();
echo "";
}
else {
echo "No data posted with HTTP POST.";
}
echo "\r\n";
车库水培 水
培 深水培养 斗系统
水培 种植传感器/显示模块
水培 冷水机
水培 水/养分控制
水培 数据库管理
水培 发芽控制
水培 CO2 监测
水培 光照监测
水培 pH 和 DO 监测
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !