第1步:需要的零件,耗材和工具
電子產品:
粒子光子
US-100超聲波測距儀
低壓差可調電壓調節器:MIC29302WT; 1.25-26V; 3A
6V 4.5Ah密封鉛酸電池“凝膠電池”
2 Maxim Integrated DS18B20數字溫度傳感器
2太陽能電池板,每個5V 500mA 2.5W
1歐姆10W電阻(掃氣)
瞬間開關(清除)
3 10k Trim電位器(掃氣)
保護二極管(掃氣)
短microUSB電纜(掃氣)
工具和用品:
焊臺
剝線鉗
臺式電源
熱縮管和熱風棒
熱膠槍
手鉆或Dremel工具
原型Photon Shield PCB(在Maker Faire免于Bluz)這是Adafruit的一個不錯的選擇。)
PVC管和鋸切割到你的ta的高度nk
包含電子設備的聚丙烯塑料食品儲存盒
連接線
金屬支架
用于固定系統的帶子,但可以拆下調試
太陽能電池板支架(掃描電腦顯示器支架)
透明5分鐘環氧樹脂
PC-7環氧樹脂漿 - 非常堅固和厚實!
丁腈手套
硅橡膠膠
步驟2:超聲波測距儀 - US-100 Wins!
這種坦克級別監控方法的操作理論非常好直截了當:從流體表面反射聲音脈沖,并通過測量回聲的延遲,計算水平在水箱中的下降程度。在實踐中,它根本不是直截了當的。值得慶幸的是,有很多便宜的超聲波測距儀用于愛好機器人,可以做得很好。我最初使用的是HC-SR04。該器件輸出一個長度與距離相對應的TTL脈沖,Arduino(或者我的情況下,Photon)必須精確測量其長度。事實證明這非常棘手并且充滿了噪音問題。它的準確性很差(重復性結果差異大約2厘米),精度差(使用NewPing庫最好可能是1厘米),可靠性差(有些數據完全錯誤)。
感謝Andreas Spiess,我發現了US-100超聲波測距儀,其發現頻率低于€并具有數字模式,以毫米為單位響應目標距離(不是厘米,與其他數字相同)超聲波測距儀)。與大多數模擬超聲波測距儀不同,它在內部進行從回波延遲到距離的轉換,而不是Arduino。我發現HC-SR04在它和Photon之間的線路上容易受到噪聲的影響。數字信號不易出現噪聲問題。 US-100還包括一個溫度傳感器,用于校正聲速隨溫度的變化。另一個好處是它可以在3.3V下使用,不像大多數需要5V工作良好的超聲波測距儀。為了讓它能夠存活在像這樣的全天候應用的元件中,我焊接連接線并用5分鐘的環氧樹脂封裝電路板。圖為我的坦克通風帽,傳感器用熱膠固定。煤油(加熱油)幾乎不像汽油一樣易揮發或易燃,因此這類裝置幾乎沒有爆炸危險。 (顯然,如果你測量的是更易燃的液體,你需要小心地將任何電子設備與蒸汽隔離開來)
香蕉機器人以5美元的價格銷售US-100,并且在數字(串行數據)和模擬(脈沖寬度)模式下可以找到最佳描述,您可以通過跳線選擇它。如果您在本Instructable的末尾掃描我的代碼,您將看到只需要通過Photon的TX線向US-100發送一個命令(0x55或0x50)來查詢傳感器,它將在幾毫秒內通過RX回復以mm為單位的距離或以攝氏度為單位的溫度。
第3步:錯誤的回聲問題 - 錯誤修復!
正如你在照片中看到的那樣,我的坦克有很多內部結構形狀,這給我帶來了許多問題,這些錯誤的回聲并非來自油面。即使鍋爐正在燃燒燃油,油耗也會在某些時候停止。在油位下降,暴露內部油箱結構之前,不會發現虛假回波問題。我成功地將超聲波束與10cm的熱縮管聚焦在發送和接收傳感器上(見圖)。但是這種方法對我如何將蓋子放回油箱非常敏感。比起小型熱縮管好,我想出了一個想法,事實證明,這個想法很久以前就被發明了,被稱為“靜止管”。我的是PVC管,從罐的最底部延伸到頂部,超聲波應答器放置在其頂端內。在其底端有兩個凹口,允許油從下方輕松地從管道中流入和流出管道,因此管道內的水平始終與水箱的其余部分相同。它被稱為靜止管,因為即使在填充過程中,內部的水平也會相對平坦和平靜。 (這在校準期間很重要。)并且因為管道的內表面非常光滑,所以不需要擔心錯誤的回聲。 除了earwigs!我很少隨機獲得奇怪的讀數。當我拿下帽子進行調查時,我看到大約5個蠕蟲漏掉了,一個人朝著PVC管道走去!我認為我的數據中的錯誤不是由于我的代碼中的錯誤,而是我的坦克中的實際錯誤,這很滑稽!現在,我已經用丁腈手套和一些硅橡膠更加安全地關閉了通風帽,以“調試”它,并阻止了穗和蜘蛛在那里開店。它不是一個完美的密封,允許坦克中的空氣膨脹和收縮。
步驟4:粒子光子是和不是Arduino
此IoT項目需要一個帶Wifi的微處理器。我選擇了粒子光子,因為這是Ventz用于他的坦克監視器的。
如果我現在開始這個項目,我可能會使用Arduino.cc提供的一個支持WiFi的Arduinos,例如Arduino MKR WiFi 1010。我對粒子光子并不滿意,但他們基本上創建了自己的并行Arduino類宇宙,而不僅僅是讓他們的產品可以與Arduino IDE一起使用。他們的Web IDE非常類似于Arduino IDE,但您可能想要使用的每個庫都必須由他們導入和驗證才能輕松地將其添加到草圖中。像通過USB重新編程Photon或甚至使用串行監視器進行調試一樣簡單,因為它需要安裝軟件層(Homebrew,dfu-util,NPM,Xcode等)并學習命令行接口(CLI)。他們希望您通過無線方式重新編程(OTA),但這并不總是可行的,因為設備在大多數時間處于脫機狀態并且處于休眠狀態。由于MacOS X High Sierra的過度安全性變化,很難安裝Particle的CLI。盡管Particle的在線文檔通常非常好,但導航和查找內容卻過于復雜。我通常只是放棄并使用谷歌搜索。從好的方面來說,Particle的客戶服務優秀,并且問題會在一天或更短的時間內得到技術支持的詳細回復。另一種選擇是使用ESP32,它現在似乎在Arduino IDE中具有相當不錯的功能,并且整體功耗更低。
Photon是一款3.3V器件,由microUSB(5V)或3.6供電其Vin引腳為-5.5V。它的數字輸入均為5V耐壓(“FT”),但其模擬引腳不是。確保不要對它們施加超過3.3V的電壓。
步驟5:電池:密封的鉛酸凝膠電池
我第一次嘗試使用LiPo電池,但很快發現當溫度低于冰點時,它幾乎毫無價值。這就是為什么電動汽車需要電池加熱器才能在冬季獲得合理的范圍。據我所知,在寒冷的冬天早晨開始使用老式汽油/汽油車時,鉛酸電池在寒冷中運作良好。通過在冰凍條件下充電,可以迅速破壞LiPo。
我使用的電子設備在激活時吸收大約80 mA,可能用于WiFi連接的很多。
所以從理論上說,這個電池應該可以為系統供電幾天沒有太陽。由于愛爾蘭可能有超過幾天的陰天,我通常會在每次循環結束時讓我的Photon睡5-10分鐘,如下所示:
System.sleep(WAKEPIN, RISING, SleepSecs);
WAKEPIN連接到瞬時開關(將線路從0上拉至3.3V),我可以使用它來幫助重新編程Photon,而無需打開盒子,如果它正在睡覺。我還在循環中添加了大約10秒的延遲,因此如果我正確計時,我可以通過WiFi重新閃爍Photon OTA(無線),然后再進入睡眠狀態。
這些鉛酸電池是“密封的”,但有一個通風口,當快速充電時會散發出易燃的氫氣,因此最好確保外殼上有某種防雨通風口。
第6步:Oilwatcher原理圖
這里我已經勾畫了整個項目的接線圖。它經歷了許多版本和升級,都散落在我的制造商筆記本電腦中。所以我把它拿下來,打開盒子,然后回溯現在的一切。它并不完美,但它有效!。..有關設置裝飾罐的信息,請參閱校準步驟。
我歡迎您提出改進建議和想法。如果您為自己制作版本,請在評論中告訴我們,或者更好,編寫自己的Instructable!
步驟7:太陽能和安裝
我通過低壓差可調電壓調節器焊接兩個串聯的太陽能電池板,為“6V”鉛酸電池充電(MIC29302)。這些面板的額定電壓為5V 500mA 2.5W,每個面板為13 x 15cm。它們絕對沒有電路,只有太陽能電池,所以我將可調電壓調節器設置為7.3V,電池前面說的是一個很好的充電電壓。我發現太陽能電池板在昏暗的燈光下成為放電的好方法。我發明了太陽能電池板除霜系統!所以我將一個二極管串聯起來,以確保電流僅從它們流入電池而不是電池。確保它是一個堅固的二極管,因為充電電流可能高達1A。務必使用環氧樹脂或硅橡膠防風雨,以免腐蝕。與我的老鼠窩不同,將所有暴露的PCB引線,焊料,電線等絕緣,以防止釋放出使其全部工作的神奇煙霧。對于我的外部DS18B20溫度傳感器,我選擇了更昂貴的防水版本。
為了給Photon供電,我將電池連接到Adafruit的Verter Buck/boost轉換器。這可以采用各種直流電壓(3-12VDC)并將其轉換為Photon通過其微型USB端口所需的5VDC。因此,我的Oilwatcher一直在黑暗中工作,直到鉛酸電池真的死了。與LiPo電池不同,鉛酸凝膠電池不會因完全放電而損壞。
我在太陽能電池板上安裝了幾個金屬支架和一些強力防風雨環氧樹脂膏(PC-7)電腦顯示器支架。我在Artificial Window Instructable項目中有幾個看臺。您應該仔細定位并指向您的太陽能電池板,以避免陰影和收集最多的陽光,特別是在較短的冬季。出于這個原因,我把它們瞄準在正午的太陽高度,在冬日,并有一個面板傾斜,以捕捉更多的早晨的陽光和一個更多的午后陽光。通過監控充電電流,您可以優化此功能。根據您的自由度,有些應用和網站也可以提供幫助。您應該牢固地安裝面板以抵御風和其他外部危險。我的是一個4x4的木柱子,用來擋住我們坦克周圍的柵欄。
步驟8:校準:體積和電壓
油箱mm到L校準
制作一個有用的設備來測量你剩下多少升(或加侖)在您的坦克中,您需要將測距儀的信號(毫米(或厘米))轉換為升。如果坦克是垂直圓柱體或完美的矩形棱柱,這將是一個簡單的問題,測量坦克的尺寸和做一些數學。但是如你所見,我的坦克是一個凸出的橢圓形,中間有兩個空洞,各種山脊和諸如此類的東西。這向我表明,1厘米的回波距離變化將代表不同水平的不同數量的升。為了直接測量體積隨油位變化的變化,我寫了一個草圖,以便在油箱加注過程中盡快收集數據(考慮到Particle Photon云服務的限制,大約每2秒鐘)。我假設裝滿水箱的卡車有一個泵,它以恒定的流速泵送,駕駛員用大杠桿設置。卡車正在仔細計量交付的量(總共1000升)。我很驚訝地看到在填充過程中收集的應答器數據的附圖中,曲線在整個范圍內非常線性!也許坦克設計師調整了坦克中心的空隙,以補償其中心增加的寬度。我將時間軸轉換為升,并使用曲線的線性擬合測量轉換因子:1.10 L/mm。我還注意到當油箱空了時轉發器讀數,這意味著當龍頭吸入空氣并且鍋爐停止時。事實證明我的坦克是1304毫米。這對于設置有用的警報很重要;你需要知道鍋爐什么時候退出,而不是在水箱干燥的時候。 (套管不夠低,無法將油箱排干。)
電壓和電流校準
我想要校準的另一件事是,預測如果連續陰天太多,電池何時會死亡,是整個系統吸收的電流和電池電壓。為此,我使用1歐姆分流電阻與電池的(+)電源串聯制作了一個粗略的“高端”雙向電流傳感器。我測量該電阻相對于地的每一側(“抽頭”)的電壓。兩個測量電壓之間的差值,或該電阻上的電壓降與通過它的電流成正比(歐姆定律,I = V/R)。當R = 1歐姆時,I = V,所以80mV是80mA。我將每個抽頭電壓除以一個已知量(約7/3因子,使7.2V降至~3.3V)和分壓器(10k微調電位器,參見本Instructable后面的原理圖),以防止超過3.3V最大值Photon的模擬輸入。所以我使用了兩個Photon的模擬輸入線來測量這個電壓降(另一個用來測量Vbatt)。為了校準這種設置,我使用具有良好電流讀數的數字臺式電源為整個設備供電,并在各種條件下獲得電流測量:WiFi連接和光子激活(~90 mA)和休眠(~1-4mA)。因為1歐姆電阻上的電壓降,特別是在分壓器之后,實際上很小(并且光子的A/D線只有12位= 4096級),我需要平均100次測量以獲得合理的無噪聲當前值。這些可以很快收集。理想情況下,我應該制作一個差分放大器來增強電流信號,但這種簡單的平均方法非常適合測量雙向電流(充電和放電)。我嘗試使用10歐姆分流器來增加信號,但光子無法通過它獲得足夠的汁液來工作。它陷入了掉電模式。我可以使用小于10k的微調電池來獲得更好的抗噪性,但是會浪費電池加熱微調電位器。 1k將是每個裝配罐浪費7mA。
因此,模擬線路為我提供0-4095的數字單位,并且通過臺式電源進行電流和電壓測量,我能夠將轉換因子放入我的草圖(見最后一步的代碼)。顯然,您需要自己校準設置以獲得良好的數據。
我意識到我不需要使用單獨的模擬輸入線和自己的分壓器來測量Vbatt,但我保留了因為它在概念上更清晰,并且因為電流監視器抽頭必須對稱地位于1歐姆電阻旁邊。 (在添加電流測量系統之前,我已經在單獨的電路板上安裝了電壓監控器。)
步驟9:使用Pushbullet發送警報
Pushbullet是一種跨平臺的警報和消息服務。您的Photon草圖將包含一些“警報”消息,這些消息將“發布”到粒子云,例如,當油位低于某個閾值時。在手機上安裝Pushbullet應用程序,它將回顯您希望所有其他設備發送的任何警報,通知和消息。因此,一旦你正確設置,就很難錯過重要的“油低!”警告您的Oilwatcher設備將啟動。
在pushbullet.com上注冊一個帳戶并閱讀其API(應用程序編程接口)上的文檔。您還可以閱讀粒子控制臺 - 集成部分的關鍵背景:https://docs.particle.io/guide/tools-and-features/。..
當您開始不堪重負時,請閱讀Ventz Petkov的博客是關于如何設置的一步一步。
https://blog.vpetkov.net/2017/11/12/diy-monitor-heating-oil-tank-gallons-with-pushbullet-sms-and-email-alerting/
他還介紹了如何開始使用粒子光子。這個過程的很多截圖都在他的Instructable中。他提到他轉移到Pushover而不是Pushbullet,所以有一些選項可以確切地實現如何在手機上獲取警報。
步驟10:使用ThingSpeak記錄和共享您的數據
如果您只是想生活并忘記加熱油,直到它耗盡,你可以跳過這一部分。
對于那些對我們在一年中不同時間使用多少油的科學和令人討厭的細節感興趣的人,或者發生了多少陽光或寒冷,ThingSpeak是給你的!太陽能電池板作為太陽傳感器,以及我項目中包含的溫度傳感器,我正在慢慢建立一個合適的氣象站! Thingspeak是一個收集和分析任何類型數據的地方,使用大量圖表,如果您愿意,可以使用復雜的數學。它由The MathWorks創建,他也創建了Matlab,這是像我這樣處理大數據的科學家的最愛。 “使用MATLAB分析的開放式物聯網平臺”是他們的標語。您可以將其全部保密,或與全世界分享您的數據,就像我在這里所做的那樣。您可以讓Photon草圖每隔幾秒在其云端的通道中將最多8個不同的變量寫入“字段”。在ThingSpeak創建帳戶后,創建一個頻道作為收集和處理數據的地方。請注意其頻道編號。使用字段名稱設置頻道并記下其字段編號。然后,您可以從API密鑰選項卡中獲取API密鑰。在Oilwatcher草圖設置中使用它:
unsigned long ThingSpeakChannelNumber = 123456;
const char * ThingSpeakWriteAPIKey = “THIS1ISAFAKEKEY45678”;
在主循環中,每當感興趣的變量更新為新值時,調用ThingSpeak.setField(5, US100tempData); // Field 5 is for the US-100‘s onboard temperature sensor
ThingSpeak.setField(6, litres_of_oil); // Field 6 is for my Litres Remaining data
并接近每個循環的結束草圖,您可以一次性發送所有數據字段:ThingSpeak.writeFields(ThingSpeakChannelNumber, ThingSpeakWriteAPIKey);
然后在您的頻道的私人和/或公共視圖選項卡中,創建數據的可視化(圖表),每次新的一組時,它們都會自動更新數據由草圖廣播。
步驟11:Oilwatcher草圖
附件是.ino文件,它是我的Oilwatcher代碼AKA Photon的固件。 .zip文件還包含Rob Tillaart名為RunningMedian的庫,尚未在Particle的Web IDE上使用。我在這里得到了該庫的最新版本:
https://github.com/RobTillaart/Arduino/tree/master 。..
頂部包含的其他庫位于Particle的庫中,但不要忘記在項目中實際#包含以使代碼正確編譯。
如果你決定使用我所包含的所有鈴聲和口哨聲,比如當前的監聽,ThingSpeak等,你必須個性化的草圖標題中會有一些常量。當然,必須仔細設置代碼發出警報的油位。
快樂黑客!
請在下面添加您的評論和問題。..
這是我的Oilwatcher草圖(也包含在此步驟中):
/*
OilWatcherus100 sketch by Steve M. Potter steve.potter at gmail.com
To monitor heating oil tank level remotely, and send Pushbullet alerts when fuel is getting low.
Uses the Particle Photon board, Adafruit’s Verter voltage buck-boost converter, an additional adjustable voltage regulator, and PushBullet
Verter is available in EU from Pimoroni https://shop.pimoroni.com/products/verter-5v-usb-buck-boost-500ma-from-3v-5v-1000ma-from-5v-12v
Uses US-100 ultrasonic rangefinder.
Inspired by code and design by Ventz: https://blog.vpetkov.net/2017/11/12/diy-monitor-heating-oil-tank-gallons-with-pushbullet-sms-and-email-alerting/
and also from his Instructable: https://www.instructables.com/id/Monitor-Heating-Oil-Tank-Gallons-With-Email-SMS-an/
Last update 2018-10-03
Code is kept on http://build.particle.io called “OilWatcherUS100”
*/
// Libraries to include:
#include “RunningMedian.h” // by Rob.Tillaart at gmail.com
#include // for data logging and analysis, e.g. see https://thingspeak.com/channels/377827
#include // For the DS18B20 digital temperature sensors
#include // For the DS18B20 digital temperature sensors
//#include // Improved communication to the HC-SR04 ultrasonic rangefinder, by Tim Eckel
// Only for use in analog pulse mode.
// CONSTANTS:
#define VBATT A1 // blue wire. Monitors the voltage of the battery, through a 1/10 voltage divider.
#define POSCURPIN A4 // white Wire attached to one side of a 1-ohm shunt resistor in series with the battery, then a voltage divider.
#define NEGCURPIN A5 // blue Wire attached to the other side of a 1-ohm shunt resistor in series with the battery, then a voltage divider.
#define TEMPPIN D5 // yellow Onewire data line for temperature probe.
#define MAX_DISTANCE 1400 // Maximum distance we want to ping for (in mm)。
#define ONBOARDLED D7
#define WAKEPIN A2 // white wire, to wake from sleep with button.
#define SENSORS 2 // number of DS18B20 temperature sensors
// Variables:
TCPClient client; // for ThingSpeak
byte mac[6]; // the MAC address of the Photon
int battThreshold = 30; // Percent (from Dead to Full) below which a LOW BATTERY! warning is triggered (should be 》 2.5V)。
double FullChargeVolts = 6.5; // set for battery being used.
double DeadBattVolts = 5.5; // Lead Acid batt voltage that the system fails.
float voltage = 0.0; // Variable to keep track of battery voltage
double BattVolts = 0.0;
double battCalib = 2.16; //correction factor. TO RECALIBRATE THIS, measure batt at its terminals with o-scope.
double soc = 0.0; // Variable to keep track of battery‘s state-of-charge (SOC)
bool battalert = LOW; // Variable to keep track of whether battery alert has been triggered
double currentConversion = 2.27; //To convert A/D counts to mA of current in and out of the battery.
float BattCurrent = 0.0;
int negCurCounts;
int posCurCounts;
int negCurOffset = 0;
int posCurOffset = -27; // to calibrate the current-measuring lines, jumper the 1ohm resistor and make current zero.
int CurCountsDiff = 0;
int cm = 0; // Distance between sensor and oil surface. NewPing returns it in whole cm.
bool LowOilAlert = LOW; // Goes high when oil level gets below threshold (50mm presently)
int cm_of_oil = 200; // This is zero when it gets down to the spigot, not the bottom of the tank.
int mm_of_oil = 2000; //This is zero when it gets down to the spigot, not the bottom of the tank.
int litres_of_oil = 0;
int ZeroOilmm = 1304; // When my tank begins to suck air, the mm reading is this.
int difference = 0; // variable to hold temporary info during calculations
int q; // for loop counters.
int SleepSecs = 60; // seconds for device to sleep each loop.
unsigned long lastUpdate = 0;
float BoxCelsius = NAN;
float OutsideCelsius = NAN;
String BoxTempStr;
String OutsideTempStr;
DS18B20 TempSensors(TEMPPIN); //Sets name for calls to DS18B20 library, Pin D5 for 1-wire Temp Sensors.
retained uint8_t sensorAddresses[SENSORS][8]; // DS18B20 addresses retained across deep sleep in backup RAM as long as power is on VBAT or VIN of Photon
unsigned long ThingSpeakChannelNumber = ; //Enter your Channel number
const char * ThingSpeakWriteAPIKey = “”; // Enter your API key
// US-100 ultrasonic rangefinder:
unsigned int MSByteDistance = 0;
unsigned int LSByteDistance = 0;
unsigned int mmDistance = 0;
int Median_mm = 0; // Some of these I tried to use unsigned int and got an “ambiguous” compile error.
int TempmmDistance = 0;
int US100tempData = 0;
int junk;
unsigned long beginmillis = 0;
unsigned long nowmillis = 0;
unsigned long elapsedmillis = 0;
RunningMedian US100reading = RunningMedian(9);
// SETUP ______________________________________________________________________________________________
void setup() {
pinMode(ONBOARDLED, OUTPUT);
pinMode(VBATT, INPUT);
pinMode(POSCURPIN, INPUT);
pinMode(NEGCURPIN, INPUT);
pinMode(WAKEPIN, INPUT_PULLDOWN);
ThingSpeak.begin(client);
Particle.variable(“Box °C”, BoxTempStr); // temp sensor in box.
Particle.variable(“mm_of_oil”, mm_of_oil); // mm of oil remaining in tank (to spigot)。
Particle.variable(“L of Oil”, litres_of_oil); // litres of oil remaining in tank (to spigot)。
Particle.variable(“mmDistance”, mm_of_oil); // Average mm measured by rangefinder.
Particle.variable(“BattVolts”, BattVolts); // battery voltage.
Particle.variable(“BattPercent”, soc); // Li-ion battery State of Charge
Particle.variable(“Tank °C”, US100tempData); // Thermometer in ultrasonic sensor
Particle.variable(“Median_mm”, Median_mm); // Median mm measured by rangefinder.
Particle.variable(“Outside °C”, OutsideTempStr); // DS18B20 temperature outside.
Particle.variable(“Raw mm data”, TempmmDistance);
// To read the values from a browser, go to:
// http://api.particle.io/v1/devices/{DEVICE_ID}/{VARIABLE}?access_token={ACCESS_TOKEN}
STARTUP(WiFi.selectAntenna(ANT_EXTERNAL)); // selects the external antenna connector, not chip antenna.
Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results.
Serial.println(“OilWatcher by Steve M. Potter. Last update 2018-10-03”);
Serial1.begin(9600, SERIAL_8N1); // for comm to/from the US-100 via RX and TX pins.
WiFi.macAddress(mac);
Serial.print(“MAC address of Photon: ”);
Serial.print(mac[0]&0x0f,HEX); // This may be in reverse order.
Serial.print(“:”);
Serial.print(mac[1]&0x0f,HEX);
Serial.print(“:”);
Serial.print(mac[2]&0x0f,HEX);
Serial.print(“:”);
Serial.print(mac[3]&0x0f,HEX);
Serial.print(“:”);
Serial.print(mac[4]&0x0f,HEX);
Serial.print(“:”);
Serial.println(mac[5]&0x0f,HEX);
TempSensors.resetsearch(); // initialise for DS18B20 temperature sensor search
for (int i = 0; i 《 SENSORS; i++)
{
TempSensors.search(sensorAddresses[i]); // try to read the 1-wire sensor addresses and if available, store them.
}
} // end setup()
// MAIN LOOP _________________________________________________________________________________________________
void loop() {
mmDistance = 0;
int DataToAvg = 9;
for (int avgloop = 1; avgloop 《 (DataToAvg + 1); avgloop++)
{
Serial1.flush(); // Clear the serial1 buffer.
Serial1.write(0x55); // Send a “distance measure” command to US-100
delay(200); // US100 response time depends on distance.
if (Serial1.available() 》= 2) // at least 2 bytes are in buffer
{
MSByteDistance = Serial1.read(); // Read both bytes
LSByteDistance = Serial1.read();//
TempmmDistance = (MSByteDistance * 256 + LSByteDistance);
Particle.publish(“Raw mm data”, String(TempmmDistance), 60, PRIVATE);
delay(200);
mmDistance = mmDistance + TempmmDistance; // calculate distance in mm. Add to running sum.
if ((TempmmDistance 》 50) && (TempmmDistance 《 MAX_DISTANCE)) // Test that the distance is in range.
{
US100reading.add(TempmmDistance); // put another datum into the buffer for a running median.
} else avgloop--; // discard this datum.
}
}
mmDistance = mmDistance/DataToAvg; // calculate the mean of N measurements.
Serial.print(“Raw mm data: ”);
Serial.print(TempmmDistance);
Median_mm = US100reading.getMedian(); // get the current running median value of mm from sensor to surface of oil.
Particle.publish(“Median_mm”, String(Median_mm), 60, PRIVATE);
Serial.print(“ Median mm data: ”);
Serial.println(Median_mm);
mm_of_oil = ZeroOilmm - Median_mm; // I determined that my boiler quits when mm = 1304.
Particle.publish(“mm_of_oil”, String(mm_of_oil), 60, PRIVATE);
delay(1000); // wait for last publish to finish.
if ((mm_of_oil 》 0) && (mm_of_oil 《 MAX_DISTANCE))
{
ThingSpeak.setField(1, mm_of_oil);
ThingSpeak.setField(7, TempmmDistance);
delay(1000); // wait for last Publish to finish.
// Calculate litres.。..Our tank is 1.10 mm/L
litres_of_oil = 0.909 * mm_of_oil; // Calibrated 2018-02-24 during tank filling.
Particle.publish(“Oil level (L)”, String(litres_of_oil), 60, PRIVATE);
ThingSpeak.setField(6, litres_of_oil);
}
// Monitor the battery voltage
voltage = 0;
for (int z = 1; z 《 101; z++) // It is pretty noisy so we take an average of 100 values.
{
voltage += battCalib * ((analogRead(VBATT) / 4096.0) * 3.3); // battery monitor is via a voltage divider.
}
voltage = (voltage/100);
BattVolts = voltage;
soc = ((voltage - DeadBattVolts) / (FullChargeVolts - DeadBattVolts)) * 100.0;
Particle.publish(“BattVolts”, String(BattVolts, 2), 60, PRIVATE);
ThingSpeak.setField(2,voltage);
delay(1000); // Can only publish 4 variables per second.
Particle.publish(“BattPercent”, String(soc, 1), 60, PRIVATE);
// Monitor the charge/discharge current. Negative values mean the solar panel is effectively charging the battery.
CurCountsDiff = 0;
for (q = 1; q 《 101; q++) // The readings are so noisy, need to average 100 of them.
{
negCurCounts = analogRead(NEGCURPIN);
posCurCounts = (analogRead(POSCURPIN) + posCurOffset);
difference = (posCurCounts - negCurCounts);
CurCountsDiff = CurCountsDiff + difference;
}
CurCountsDiff = (CurCountsDiff/100);
BattCurrent = (currentConversion * (CurCountsDiff));
Particle.publish(“BattCurrent mA”, String(BattCurrent, 0), 60, PRIVATE);
ThingSpeak.setField(4, BattCurrent);
// Send Alerts via Pushbullet:
if ((mm_of_oil 《 50) && (mm_of_oil 》 45)) // Only want Pushbullet to send a few warnings
{
Particle.publish(“Alert”, “Heating Oil Level is 5cm - Refill SOON!”, PRIVATE);
LowOilAlert = HIGH;
}
if (soc 《 battThreshold)
{
Particle.publish(“Alert”, “Oilwatcher battery is very low. Recharge it.”, PRIVATE);
battalert = HIGH;
}
// Read temperature from the US-100 ultrasonic rangefinder’s temp sensor at the top of the tank. The tank air heats up in the sun.
Serial1.flush();
while (Serial1.available() 》= 1) // seemed like flush() was not working so I added this.
{
junk = Serial1.read();
}
Serial1.write(0x50); // send command to request temperature byte.
delay(50); // temp response takes about 2ms after command ends.
if (Serial1.available() 》= 1)
{
US100tempData = Serial1.read();
if ((US100tempData 》 1) && (US100tempData 《 130))
{
US100tempData -= 45; // Correct by the 45 degree offset of the US100.
Particle.publish(“Tank °C”, String(US100tempData), 60, PRIVATE);
ThingSpeak.setField(5, US100tempData);
}
}
// Read temperature from the DS18B20 sensors in electronics box and outside.
BoxCelsius = TempSensors.getTemperature(sensorAddresses[1]); // Will it always find the 1-wire sensor addresses in the same order?
BoxTempStr = String(BoxCelsius, 1);
Particle.publish(“Box °C”, BoxTempStr, PRIVATE);
ThingSpeak.setField(3, BoxTempStr);
OutsideCelsius = TempSensors.getTemperature(sensorAddresses[0]);
OutsideTempStr = String(OutsideCelsius, 1);
Particle.publish(“Outside °C”, OutsideTempStr, PRIVATE);
ThingSpeak.setField(8, OutsideTempStr);
// Send all data to ThingSpeak for graphing and calculations.
ThingSpeak.writeFields(ThingSpeakChannelNumber, ThingSpeakWriteAPIKey);
// give time for temperature to publish before going to sleep.
delay(10000); // In case I need to reprogram it, here is a chance when it is not sleeping.
System.sleep(WAKEPIN, RISING, SleepSecs); // Turn off WiFi and pause execution, preserving variables. Wakes after set # of seconds
// A momentary button on the box allows waking for easier re-programming.
// Hit Flash very soon after hitting the wakeup button. Or just before.
//System.reset(); // This was one way to allow it to not lose its ability connect to wifi when the router‘s IP address changed during sleep.
// Disadvantage is it only loops once thru the code. No variables remembered.
} // end void loop()
-
監控系統
+關注
關注
21文章
3920瀏覽量
175035
發布評論請先 登錄
相關推薦
評論