亲,“电路城”已合并升级到更全、更大、更强的「新与非网」。点击查看「新与非网」

本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页

一个永远待机的太阳能水监测系统

发布时间:2022-11-07
分享到:

一个永远待机的太阳能水监测系统

发布时间:2022-11-07
分享到:

背景
水是我们的健康和环境最重要的单一元素。

我们能做些什么来保护它?监控其质量是一个良好的开始。无论是在游泳池、农作物、市政供水设施还是在非常偏远的井里,保存干净的水既有助于减少不必要的浪费,也有利于我们的健康。

所面临的挑战
水质测量既昂贵又耗时。这是因为它的参数不仅是物理的,而且还涉及到了化学和生物方面,需要在实验室中取样和分析其中一些参数。

尽管水中有超过30个参数,我们的挑战是在这些条件下开发一个在线监测系统:

  • 简单:可以由非技术用户构建
  • 稳定:可以无人看管,通过太阳能自己充电
  • 低成本(低于200美元):我们集成了商业硬件和软件工具来测量一组基本参数,通过分析环境,可以了解水质,即pH值,ORP(氧化还原电位)和温度。

您可以很容易地扩展这个项目,以测量其他参数,如电导率或溶解氧,只需购买各自的探头,并将它们连接到Seeeduino板的模拟输入。

在完成这个项目后,您应该有您的所有变量在一个漂亮的仪表板,就像这样,甚至能够创建您自己的触发器来接收短信或电子邮件时,一个变量超出阈值。

设置你的ubidots账户
首先,我们将使用Ubidots服务来存储和分析传感器数据,因此让我们首先在这里创建一个Ubidots帐户。

登录到您的帐户,创建一个数据源称为“池监控”,并添加4个新变量称为“温度”,“电池”,“PH”和“ORP”:

请注意,每个变量都有一个ID。稍后我们的设备代码将需要这些ID。

此外,你需要在“我的个人资料”选项卡下创建一个令牌。令牌类似于设备的密码,用于对Ubidots API进行身份验证并向其发送数据。注意这个值,因为我们也需要它的设备代码。

布线

这里有很多电缆,所以密切注意连接是很重要的!

确保Adafruit Fona模块有天线,否则代码可能返回通信错误。你也应该检查每个pH/ORP适配器的开关在正确的位置;应分别设置pH值和ORP值。

在将所有内容连接起来之后,将所有元素尽可能有序地放入一个Tight Box中。

编码

因为我们的项目涉及不同类型的外设,所以我们将分别测试每个外设。如果您想获得完整的代码,您可以在本节的末尾找到它。

读取温度数据

我们使用DS18B20传感器,因为它简单。下面的代码将从引脚#2读取温度,并在串行监视器中显示它。在使用它之前,你需要包含以下库:

  • DallasTemperature
  • OneWire

#include <DallasTemperature.h>
#include <OneWire.h>
#define Pin 2 //Pin for DATA
 
OneWire ourWire(Pin); //Pin declared like a bus for OneWire communication
 
DallasTemperature sensors(&ourWire); //instance of DallasTemperature library
 
void setup() {
delay(1000);
Serial.begin(9600);
sensors.begin(); //Init the sensors
}
 
void loop() {
sensors.requestTemperatures(); //Prepare the sensor for reading
 
Serial.print(sensors.getTempCByIndex(0)); //Read and print the temperature in Celsius degree.
Serial.println(" °C");
 
delay(1000); // 1 second for delay
 
}

读取pH/ORP原始数据
在感知pH值或ORP数据之前,用下面的Arduino代码测试phidget是一个好主意。它基本上读取模拟输入并在串行显示器上显示它们。通常,需要一个公式将这些读数转换为有意义的pH/ORP值,但我们稍后将使用Ubidots的数学引擎进行这些计算。

/*
 Basic sketch for pH/ORP Adapter.

 This is a basic example to test the analog values from pH/ORP Adapter.

 You'll need:
 * An Arduino Uno
 * pH/ORP Adapter 1130
 
 Pinout:
 

 created 20 Feb. 2015
 by Alejandro D.J Gomez Florez for Ubidots Inc.

 This example code is in the public domain.

*/

void setup() {
  // Initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}


void loop() {
  // read the input on analog pin 0:
  int ph = analogRead(A0);
  // read the input on analog pin 1:
  int orp = analogRead(A1);
  // print out the value you read:
  Serial.println(ph);
  Serial.println(orp);
  // delay in between reads for stability
  delay(500);
}

读取电池电量
这个测试代码将读取电池的电压,这将帮助您熟悉它的太阳能电路的充放电曲线。

/*
 Basic sketch for Adafruit FONA module. Based on Adafruit FONAtest Example

 This is a basic example to read voltage value.

 You'll need:
 * An Arduino
 * A FONA Module
 
 Pinout:
 

 created 20 Feb. 2015
 by Alejandro D.J Gomez Florez for Ubidots Inc.

 This example code is in the public domain.
*/

void setup() {  
  //For Serial comunnication
  Serial.begin(115200);
  analogReference(INTERNAL);
  
  delay(2000);
}

void loop(){
  //Read the Analog Batery voltage raw data
  int battery = analogRead(A7);
  Serial.println(battery);
}

最终代码


该代码集成了传感器读数和FONA函数,将数据发送到Ubidots:

/*

 Basic sketch for Adafruit FONA module. Based on Adafruit FONAtest Example

 This is a basic example to post a value on Ubidots with a simple
 function "sendata2Ubidots".

 You'll need:
 * An Arduino
 * An Adafruit FONA Module

 Pinout:


 created 20 Feb. 2015
 by Alejandro D.J Gomez Florez for Ubidots Inc.

 This example code is in the public domain.
*/


#include <SoftwareSerial.h>
#include "Adafruit_FONA.h"
#include <stdlib.h>
#include <DallasTemperature.h>
#include <OneWire.h>

#define FONA_RX 2
#define FONA_TX 3
#define FONA_RST 4
#define FONA_KEY 7
#define FONA_PS 8
#define Pin 10          //Pin for temperature data


char token[] = "kA8XWNQRHFrXVQakaJVY3Zg2qRU1lq";
char id1[] = "54eb894d7625423fe93f0f3c";
char id2[] = "54eb896d762542419938b415";
char id3[] = "54eb89537625423f923e0501";
char id4[] = "54eb89597625423f78057de8";

// this is a large buffer for replies

char replybuffer[255];

SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);

Adafruit_FONA fona = Adafruit_FONA(FONA_RST);

//For Temperature Sensor
OneWire ourWire(Pin);
DallasTemperature sensors(&ourWire);

uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
int get_int_len(int);
void sendDataUbidots(void);

int value1, value2, value3, value4;
char buffer [33];

void setup() {  
  //For FONA MODULE
  Serial.begin(115200);
  fonaSS.begin(4800);
  pinMode(FONA_KEY, OUTPUT);
  pinMode(FONA_PS, INPUT);
  analogReference(INTERNAL);

  //Init the temperature sensor
  sensors.begin();

  delay(2000);
  TurnOffFona();

}

void loop() {
  flushSerial();
  TurnOnFona();
  checkFona();
  senseValues();

  while (fona.available()) {
    Serial.write(fona.read());
  }

  TurnOffFona();
  delay(600000);

}

void test(int value, char* myid){

  //Make the url string
  char url1[] = "things.ubidots.com/api/v1.6/variables/";
  char* url2 = myid;
  char url3[] = "/values?token=";
  char* url4 = token;
  int lurl = strlen(url1) + strlen(url2) + strlen(url3) + strlen(url4);

  char url[lurl];
  sprintf(url,"%s%s%s%s",url1,url2,url3,url4);
  Serial.println(url);
   
  char data1[] = "{\"value\":";
  char data2[get_int_len(value)];
  char data3[] = "}";
  itoa(value,data2,10);
  int ldata = strlen(data1) + strlen(data2) + strlen(data3);

  char data[ldata];
  sprintf(data,"%s%s%s",data1,data2,data3);
  Serial.println(data);

}

void senseValues(){
  //Prepare for the temperature sensor for lecture
  sensors.requestTemperatures();
  //Read the temperature in Celsius degree.
  value1 = sensors.getTempCByIndex(0)*100;
  //Read the Analog Batery voltage data
  value2 = analogRead(A7);
  value3 = analogRead(A0);
  value4 = analogRead(A1);

  gprsOnFona();
  sendDataUbidots(value1, id1);
  gprsOffFona();

  gprsOnFona();
  sendDataUbidots(value2, id2);
  gprsOffFona();

  gprsOnFona();
  sendDataUbidots(value3, id3);
  gprsOffFona();

  gprsOnFona();
  sendDataUbidots(value4, id4);
  gprsOffFona();  

}

void sendDataUbidots(int value, char* myid){

  uint16_t statuscode;
  int16_t length;

  char url1[] = "things.ubidots.com/api/v1.6/variables/";
  char* url2 = myid;
  char url3[] = "/values?token=";
  char* url4 = token;
  int lurl = strlen(url1) + strlen(url2) + strlen(url3) + strlen(url4);

  char url[lurl];
  sprintf(url,"%s%s%s%s",url1,url2,url3,url4);
//  Serial.println(url);

  char data1[] = "{\"value\":";
  char data2[get_int_len(value)+1];
  char data3[] = "}";
  itoa(value,data2,10);
  int ldata = strlen(data1) + strlen(data2) + strlen(data3);

  char data[ldata];
  sprintf(data,"%s%s%s",data1,data2,data3);
  Serial.print(F("http://"));
  Serial.println(url);
  Serial.println(data);
  Serial.println(F("****"));

  if(!fona.HTTP_POST_start(url, F("application/json"), (uint8_t *) data, strlen(data), &statuscode, (uint16_t *)&length)) {

    Serial.println("Failed!");

   }

   while (length > 0) {

     while (fona.available()) {
       char c = fona.read();
       
  #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
       loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
       UDR0 = c;
  #else
       Serial.write(c);
  #endif
       
       length--;
       if (! length) break;
     }
   }
   Serial.println(F("\n****"));
   fona.HTTP_POST_end();
    
  // flush input
  flushSerial();
  
}

void flushSerial() {
    while (Serial.available()) 
    Serial.read();
}

char readBlocking() {
  while (!Serial.available());
  return Serial.read();
}

uint16_t readnumber() {
  uint16_t x = 0;
  char c;
  while (! isdigit(c = readBlocking())) {
  }

  Serial.print(c);
  x = c - '0';
  while (isdigit(c = readBlocking())) {
    Serial.print(c);
    x *= 10;
    x += c - '0';
  }
  return x;

}

  

uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout) {

  uint16_t buffidx = 0;

  boolean timeoutvalid = true;

  if (timeout == 0) timeoutvalid = false;

  

  while (true) {

    if (buffidx > maxbuff) {

      //Serial.println(F("SPACE"));

      break;

    }

    while(Serial.available()) {

      char c =  Serial.read();

      //Serial.print(c, HEX); Serial.print("#"); Serial.println(c);

      if (c == '\r') continue;

      if (c == 0xA) {

        if (buffidx == 0)   // the first 0x0A is ignored

          continue;

        

        timeout = 0;         // the second 0x0A is the end of the line

        timeoutvalid = true;

        break;

      }

      buff[buffidx] = c;

      buffidx++;

    }

    

    if (timeoutvalid && timeout == 0) {

      //Serial.println(F("TIMEOUT"));

      break;

    }

    delay(1);

  }

  buff[buffidx] = 0;  // null term

  return buffidx;

}

int get_int_len (int value){

  int l=1;

  while(value>9){ l++; value/=10; }

  return l;

}

//Check if FONA works

void checkFona(){

  // See if the FONA is responding

  if (! fona.begin(fonaSS)) {           // can also try fona.begin(Serial1) 

    Serial.println(F("Couldn't find FONA"));

    while (1);

  }

  Serial.println(F("FONA is OK"));

  //configure a GPRS APN

  fona.setGPRSNetworkSettings(F("web.vmc.net.co"), F(""), F(""));

}

//Turn on FONA

void TurnOnFona(){  

  Serial.println("Turning on Fona: ");

  while(digitalRead(FONA_PS)==LOW)

    {

    digitalWrite(FONA_KEY, LOW);

    }

    digitalWrite(FONA_KEY, HIGH);

    delay(4000);

}

//Turn off FONA

void TurnOffFona(){

  Serial.println("Turning off Fona ");

  while(digitalRead(FONA_PS)==HIGH)

  {

    digitalWrite(FONA_KEY, LOW);

    }

    digitalWrite(FONA_KEY, HIGH); 

    delay(4000);

}

//Turn on GPRS on FONA

void gprsOnFona(){

  while(!fona.enableGPRS(true));

  Serial.println(F("Turn on"));

}

//Turn off on FONA

void gprsOffFona(){

  while (!fona.enableGPRS(false));

  Serial.println(F("Turn off"));

}

pH / ORP校准
为了得到正确的pH值测量,有必要使用“缓冲溶液”。这是一种已知pH值的液体,可作为参考。

我们可以将pH值测量近似为线性曲线,因此我们将记录pH值为4和pH值为7时的传感器读数,温度为25°C。

让我们以“y = mx + b”的形式构建线性方程,其中:

M = (y2-y1) / (x2-x1)

而且

B = (y1) - m*(x1)

其中y1和y2分别为ph4和ph7,而x1和x2分别为ph4和ph7缓冲溶液中获得的模拟传感器原始值。

在我们的例子中,ph4对应的模拟值为“595”,ph7对应的模拟值为“744”。然后计算转换系数:

M = ((7-4)/(744 - 595)) = 0.020134;

最后,我们计算定值“b”得到最终方程:

B = 4 - 0.020134*595

最后一个方程:

pH(校准)= 0.02013(RawPHvalue) - 7.9798

关于ORP校准,制造商已经在他们的网站上提供了一个公式:

ORP(校准)= (2.5 - (RawORPvalue/200))/1.37

Ubidots很酷的一点是,你不需要在设备中编写这些公式;你可以在云端完成。只需在数据源中创建一个“派生变量”,并输入相应的数学表达式:

太阳能充电注意事项
这个系统试图尽可能地保持自治。

由于通过GPRS发送值是消耗的主要来源,我们将每10分钟发送一次数据,并在此完成后关闭FONA。这将给电池足够的时间通过太阳能充电,并确保它在晚上不会放电。

为了确定FONA在夜间可以工作多长时间,我们给电池充满电,断开太阳能充电器,并向Ubidots发送尽可能多的值。我们能够发送1378个值,这意味着,在10分钟的分离,设备可以发送数据230小时,或9.5天!有足够的时间给电池充电。

另一个测试是看在阳光充足的情况下电池需要多少电量。我们观察到,20分钟内电荷增加了12个单位,范围在530到650之间(模拟转换得到的值)。因此,通过太阳能电池板100%充电需要200分钟,即3小时15分钟。

通过每10分钟发送一次数据,我们可以确保我们有足够的时间给它充电,即使是在冬天!

考虑到这些因素,我们确定了10分钟的更新周期,尽管它应该能够支持更短的更新周期。

结论

该项目允许您设置一个自动的水监测系统,用于游泳池或其他设置的水质量是重要的。将这些数据存入Ubidots之后,您可以创建文本消息或电子邮件警报,甚至可以激活远程执行器,如打开阀门或打开水泵。

如果您对此项目有任何想法、意见或问题,请在下方留言。

以上内容翻译自网络,原作者:Alejandro Gómez,如涉及侵权,可联系删除。

加入微信技术交流群

技术交流,职业进阶

关注与非网服务号

获取电子工程师福利

加入电路城 QQ 交流群

与技术大牛交朋友

讨论