作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
更新: 本文发表后,Arduino气象站的工作仍在继续, 最终以释放 开放气象站(OWS). 查看更多更新、资源、代码和新教程.
冲浪板 是世界上最让人上瘾的运动之一吗. 它所需要的只是一个风筝板,一片水域和一些配件. 这是一个接触自然、解放思想和锻炼身体的好方法. 另外,你可以 真的很疯狂 用它.
哦,我忘了一个基本要求:风. 这就是我们的问题所在:你永远不知道是否会有风,除非你住在你最喜欢的风筝冲浪点附近.
我住在 阿根廷科尔多瓦离我风筝冲浪的湖大约130公里(约80英里). 大概两个小时的车程,我能应付. 但我无法接受天气预报不准确的事实. 在我住的地方,良好的风力条件只能持续几个小时. 你最不想做的就是清空你周一的时间表去风筝冲浪,然后发现自己在一个没有风的湖上开了两个小时的车后诅咒上帝.
我需要知道我最喜欢的风筝冲浪地点的风况——实时的. 所以我决定建立自己的气象站.
目标是将实时天气数据传送到家里的浏览器上。
在我讲细节之前, 让我们花点时间考虑一下这样一个项目中涉及的关键问题和注意事项:
You may think that the glove is t在这里 to make the station appear friendlier; but it’s actually used to test the barometric sensor (the glove pressure increases inside the inflated glove). 在右边,你可以看到车站的最终位置,坐落在附近的一座塔上.
我还设计并编写了a 关于风筝冲浪的网站,其中包括 该站的实时测量图 帮助风筝冲浪社区. 最后,我创建了 Facebook上的风筝冲浪群.
好吧,我将依次说明每一点:
这是一个关键因素,并且在许多方面推动了其余的设计过程. 大多数低于2000美元线的预制站都需要一个 USB连接到电脑. 如果小偷认出车站旁边有一台电脑, 那就是世界末日了, 因为更换电脑和工作站的费用超出了我的个人预算. 因此, 我决定测试几个硬件平台来从头实现这个站点, 成本更低.
我独自承担着这个副业项目的费用,并在业余时间做所有的工作, 所以这当然是个大问题. 我从受欢迎的开始 PIC32 以及一些预组装的微芯片以太网模块, 但成本没有我预期的那么低,而且硬件组装和扩展涉及的开销太多了. 然后,我开始研究 Arduino:一个开源的硬件和软件,用于电子原型设计,使用C语言. 这正是我想要的,我可以在上面购买模块 DealeXtreme. 我只花了15美元和两天的时间就开始玩了.
当然, Arduino 也有它的局限性:我编译的软件只有2KBytes的RAM和32kb的内存,这就没有给花哨的字符串或无用的变量留下太多的空间 1.
目前, 我的观测站可以测量风速, 风速, 风向, 温度, 湿度, 雨, 大气压力. 温度, 湿度, 压力是由几个库处理的, 这让生活轻松了很多.
测量风速和降雨量有点麻烦. 传感器通过打开和关闭开关(舌簧开关). 因此, 我需要实现硬件中断,以便在传感器触发输入时立即捕获传感器. 也就是说,我需要调用一些方法:
attachInterrupt(RAINGAUGE_PIN, countrycycles, FALLING);
此中断将中断正常的代码执行,并在交换机经历下降沿时立即调用countAnemometerCycles或countrycycles函数, 由闭合或打开电路产生的. 在开关的每个触发器上增加一些变量. (稍后,您将权衡这些变量以考虑单位转换.)
void countrycycles () {
雨CyclesCounter++; // This is easy! 它确实有效.
}
但不要太快! 由于任何硬件交换机固有的开关弹跳效应,此过程产生数百个假触发器. 幸运的是,这个问题有硬件和软件两种解决方案.
弹跳效应是开关物理打开或关闭其“触点”的结果。, 哪些与电路的其余部分建立了接触. 当触点开始分离(打开开关)或合并(关闭开关)时, 可能会产生一些小的电弧, 以及电路中的机械弹性,它将触发电路在几毫秒内开关. 当你拨动电灯开关时, this effect isn’t apparent; but when you attach an interrupt to the falling edge of a signal, 这种弹跳效果会触发大量的中断. 更多的 在这里.
我实现了一个硬件剥离电路和一个类似的软件版本. 但是,如何实现一个软件脱壳呢? 容易! 在第一个预期触发器发生之后, “等待”足够的时间让反弹安定下来,然后再开始监听新的中断. 这可以在几行C中完成:
void countrycycles () {
if (nextTimeRainIterrupt == 0 || nextTimeRainIterrupt < millis()) {
雨CyclesCounter++; // The interrupts counter
nextTimeRainIterrupt = millis() + 100; // Wait 100msecs before next trigger
}
}
millis()函数返回自Arduino打开以来的当前执行时间(以毫秒为单位). 同样值得注意的是,这些变量必须定义为volatile,以指示编译器不要优化执行,从而避免在硬件中断期间出现不准确的值.
在某种程度上, 我需要该站存储累积的数据,并定期将这些测量结果发送到MySQL数据库. 因此,我添加了一个带有SD插槽的以太网模块,用于记录这些值,并在用户(服务器)连接到站点时检索它们. 在家里用ADSL连接进行测试时,这种效果非常好,但是当我用3G互联网(使用3G调制解调器)在“现场”测试时,我几乎掉了头发。, 因为当我试图取回测量数据时空间站会随机重置! 经过显著性检验, 我最后发现,互联网上提供的描述向连接的客户机“提供”数据的示例没有考虑到连接可能非常差,以至于到客户机的连接可能在数据包传输过程中丢失, 导致输出缓冲区溢出. 但是为什么断开的连接会导致缓冲区溢出呢? 好吧, 假设传输会话开始,工作站开始用数据填充输出缓冲区. 理想情况下,客户机消耗缓冲区的速度要快于填充缓冲区的速度. 然而,当与3G调制解调器连接时,情况就不是这样了! 与客户的连接太差了, 所以缓冲区填满的速度比它被消耗的速度快, 导致缓冲区溢出和空间站突然重启的原因是什么.
要解决这个问题,我需要向 以太网图书馆 Arduino是这样运行的:
int EthernetClient::free() {
如果(_sock != MAX_SOCK_NUM)
返回W5100.getTXFreeSize (_sock);
返回0;
}
然后, 我能够检查客户端是否在缓冲区中有一些空间,然后再尝试用更多的数据填充它:
而(文件.available() > 0) {
如果(客户端.free() > 0) { // This was key to solving the issue
C =文件.read ();
客户端.打印((字符)c);
} else{//没有可用的缓冲区? 好吧,我再等几毫升...
延迟(50);
}
}
文件.close ();
顺便说一下,如果你对Arduino编程感兴趣, 这是 一个伟大的向导.
另一个有趣的任务是后进先出日志的实现. 为什么这是必要的?? 好吧, 通常, 当我将测量值保存到给定文件时, 方法很简单:打开文件, 将新样品附加到末尾, 然后关闭文件. 但假设我想获取最近的1000个测量值,按时间顺序排序. Those measurements are at the end of the 文件; so I should open the 文件, 将光标移到末尾, 输出最新的测量结果, 然后,通过寻找样例分隔符来检测开始和停止的位置,将文件游标返回到先前的测量和输出. Arduino既没有足够的RAM,也没有足够的处理器能力来快速执行这个过程, 所以我需要另一种方法. 而不是, 我决定以相反的顺序将文件输出到服务器, 然后在服务器端还原字符串字面量:
unsigned long 文件Position = 文件.大小();
文件.寻求(文件Position);
而(文件Position >= 0) {
如果(客户端.free() > 0){
文件.寻求(文件Position);
C =文件.peek ();
如果(c != -1) {
客户端.打印((字符)c);
}
if (文件Position <= 0) {
打破;
}
文件Position——;
}
}
有任何PHP开发经验, 很容易得到字符顺序正确的最新样品:
// $output具有反向的字符串度量,每个样本用;
$rows = split(";", trim($output));
array_walk_recursive(美元行,“reverseString”);
如果(strlen($rows[0]) == 0) {
array_shift($rows); // Remove the first line if empty
}
函数reverseString (&$row, $key) {
$row = trim(strrev($row));
}
// $rows现在是最新样本的数组:)
在服务器端, 然后,我设置了一个cron进程,每两分钟获取一次最新的测量结果,并将数据插入MySQL引擎. 为了显示数据,我创建了 www.kitesurfcordoba.com.ar 并使用jQuery自动更新图形(图形本身是使用 pChart v2.0,一个很棒的开源库).
还有很多其他的技巧可以让事情顺利进行, 与软件和硬件工程相关, 但我已经拖得够久了,所以我们来谈谈最小化维护.
这是一个主要的问题,因为我当然不容易到达车站——如果我愿意开两个小时的车去修理一个小故障的话, 那么我一开始就不必让她进来了(我之前没有提到过这一点), 但毕竟我们经历了这么多, 车站实际上是一个“她”。, 她的名字叫多萝西).
我们这里讨论的是什么类型的误差? 好吧, 例如:软件可能挂起, 网络可能失去连接, 能源供应可能会中断(事实也确实如此), 等.
本质上,空间站需要执行尽可能多的自我恢复. 这就是我软硬兼施的原因 监管机构. 对于那些不熟悉的人, 看门狗是一种软件或硬件,用于检查系统是否正常运行, 如果不是, 试图让它起死回生. Arduino有一个可以使用的嵌入式看门狗. 我将它设置为等待8秒:如果通话时间超过了这个时间限制, 软件看门狗会复位单板.
wdt_enable(WDTO_8S); // "wdt" stands for "watchdog timer"
我喜欢这个函数. 但是,有时单板复位而以太网模块不复位. 为什么? 好吧, 这是一个相对负担得起的原型板, 不是很贵, 防故障装置(你当然不应该用它来做心脏起搏器). 为了克服这个缺点, 我不得不通过交叉连接硬件复位输入到电路板上的数字输出来破解Arduino. 为了避免重置循环,还必须添加几行代码:
无效设置(){
digitalWrite(RESET_ARDUINO_PIN, HIGH); // Set it to HIGH immediately on boot
pinMode(RESET_ARDUINO_PIN, OUTPUT); // We declare it an output ONLY AFTER it's HIGH
digitalWrite(RESET_ARDUINO_PIN, HIGH); // Default to HIGH, set to LOW to HARD RESET
...
在那之后, 我能够通过简单的调用来对Arduino和它上面的所有模块(包括以太网模块)进行硬件重置 digitalWrite (RESET_ARDUINO_PIN,低)
几秒钟后,多萝西就活过来了.
此外,板自动重启后,能量损失. 如果网络连接失败, 我们利用SD卡的存储能力(数据可以在卡上存储一个多星期), 服务器可以提取旧数据来恢复任何丢失的样本). 综合所有这些特点,我们就有了一个高度坚固的气象站,它可以在恶劣的条件下生存下来. 这东西总共花了我300美元.
该站自2012年12月以来一直在工作. 截至目前,, 它没有失败(或者如果失败了), 车站恢复得足够快,以至于风筝冲浪社区和我都没有注意到。. 大约有500名风筝冲浪者在前往现场之前定期查看气象站. 所以除了解决一些棘手的技术挑战的奖励, 我也有机会为一群人提供更愉快的风筝冲浪体验.
1 一开始,我用的是an Arduino Uno. 后来,我换了一个 Arduino兆 因为需要增加RAM和闪存.
世界级的文章,每周发一次.
世界级的文章,每周发一次.