2015/03/30

Arduino SNMP溫度監控: Cacti CDEF轉換數值 與 自定Graph Template

CDEF可以把數值做前處理,譬如轉回小數點
還有其他很強的功能的樣子,這次就沒摸了。

Graph Management > CDEFs > Add


CDEF Items > Add

Item這邊要新增三個,資料來源、常數、運算元


首先新增資料來源

常數是要還原回浮點數,Arduino是設定100,這邊相同

運算元是除法

儲存即可,目前這個CDEF還不能套用在資料來源上(即使選了還是會跳回none)
需要新增Graph Template來使用

新增Graph Template

Graph Templates > Add

Use Per-Graph Value (Ignore this Value) 打勾表示之後新增可以蓋掉這些設定


Create後,上方會出現兩個區塊可以新增


先加入 Graph Template Items

這個是要在圖上顯示數值的線,Graph Item Type可以自己選Line*或AREA

(選用) 加入最小值、最大值、目前值 在圖底下的文字



GPRINT是顯示在圖底的文字格式
Consolidation Function數值的形式 分別選擇LAST, MIN, MAX
Text Format 是文字的標籤,不打會不知道那個數值是啥

全部設定完成會長這樣


記得存檔!

新增Graph


Template選擇剛剛製作好的


剛剛有打勾 Use Per-Graph Value 可以在這裡覆蓋


Graph應該就會顯示出來了


看Y軸的刻度就小了100,底下數值也顯示正確的溫度了
樣板匯出檔在此 https://github.com/Aspertw/ArduinoSNMPTemp/blob/master/cacti_graph_template_arduinotempgraphtpl.xml

Arduino SNMP溫度監控: Cacti設定

Cacti安裝請參考 CentOS 6.5 快速安裝cacti 0.8.8b

這篇主要是介紹怎麼將 *100的偽浮點數 轉換為真正的浮點數
(SNMP沒有傳送浮點數的資料型態,所以將溫度值*100後保留小數兩位)

基本測試(不做轉換)

建議先測試連線跟資料是不是可以正確的傳回Cacti,之後再處理數值轉換。

打開Cacti後,選擇Devices -> Add(右上角)


新增後過一下子(手動重新整理)就會看到裝置上線資訊,
如果很久都沒出現,請檢查網路連線是否正常。


新增資料來源與圖

New Graphs 選擇 SNMP - Generic OID Template



修改欄位:
  • Title: 圖的名稱
  • Legend Color: 圖的數值顏色
  • Name: 資料來源名稱
  • Maximum Value 改成 U,不改的話超過100 Cacti會略過
  • OID: 自訂的 1.3.6.1.4.1.36582.X.Y (X:A2~A5, Y:0~7)

到Graph是看不到這個裝置跟圖的,還要在Tree新增節點


新增Graph Tree

Graph Tree -> Default Tree -> Add

在Graph應該可以看到這台機器,點他就會出現圖了


如果過程遇到問題,可以到 System Utilities -> View Cacti Log File 看有沒有錯誤訊息
然後拜 Google 大神


調整更新時間

Cacti預設是每5分鐘更新一次資料,正式環境是夠用,
但是測試環境要等5分鐘才知道設定正不正確有點浪費時間
最小的更新時間是1分鐘(受限Crontab)
調整需動到四個地方:
  • CentOS Crontab
  • Cacti > Setting > Poller
  • Cacti > Data Sources
  • Cacti > Graph (改動Data Source後圖要重新產生,不然還是維持原來的時間間隔)
[ CentOS Crontab ]
vi /etc/cron.d/cacti
*/1 * * * *     root   php    /var/www/cacti/poller.php &>/dev/null
5改成1


[ Cacti > Setting > Poller ]
Poller Interval, Cron Interval 改成 "Every Minute"


[ Cacti > Data Sources ]
Step: 300 改 60 (單位是秒)

最後把受影響的圖刪掉重新Add即可


下一篇介紹CDEF轉回小數點格式

2015/03/28

Arduino SNMP溫度監控: 分析oid

上一篇講到了可以接受自訂oid後,
這篇要來說要怎麼分析。

格式定義

首先,我定義的格式為:
1.3.6.1.4.1.36582.X.Y
X為Arduino 的 Analog Input接頭(A2~A5),所以X範圍是2~5
Y是多工器(MUX)的接腳,範圍是0~8

X.Y的長度最多是3,所以可以抓取oid的後3字串,在用"."切割得到X跟Y。

取得後3的字串

使用函數:strncpy(結果字串 , 來源字串, 字串長度);
char oidLastTwo[4] = "";
strncpy(oidLastTwo, oid + locTempPrefix, 3);

應該要特別解釋的是 oid + locTempPrefix,來源字串為什麼加上一個整數?
因為oid是一個字串指標,值是記憶體位址,如果加上數字代表是移動字串的位置

例如(以下是為了舉例,真實不能這樣寫):
oid = "ABCDEF";  oid + 2 = "CDEF";

所以 1.3.6.1.4.1.36582.X.Y + 18("1.3.6.1.4.1.36582."的長度),
所取得的字串就是X.Y...,取三個得到 X.Y。

可以用終端機測試一下
char oidLastTwo[4] = "";
Serial.print("Last Two Segment: ");
Serial.print(oidLastTwo);
Serial.print(" oid: ");
Serial.println(oid);

切割字串

使用函數:sscanf(來源, 格式, 切割結果1 [, 切割結果2...] );

int analogPin;
int muxPin;
sscanf(oidLastTwo, "%d.%d", &analogPin, &muxPin);

切割結果會根據你的格式改變,我這邊只取得兩個%d(數字),所以也只給兩個變數;&是傳記憶體位址進去。

最後就可以得到 analogPin(X) 跟 muxPin(Y)。

可以用終端機測試一下
Serial.print("Selected AnalogPin: ");
Serial.print(analogPin);
Serial.print(" Selected muxPin: ");
Serial.println(muxPin);

整合抓取溫度程式

宣告區增加

//Thermistor
#include <math.h>
static float pad = 9900;
static float thermr = 10000; 
#define muxPINA 2
#define muxPINB 3
#define muxPINC 4
int muxA = 0;
int muxB = 0;
int muxC = 0;

Setup() 增加

pinMode(muxPINA, OUTPUT);
pinMode(muxPINB, OUTPUT);
pinMode(muxPINC, OUTPUT);

複製 Thermistor 函式到喜歡的地方 


為了方便呼叫,我另外寫了一個函數來抓溫度;大部分的Code都跟之前的範例一樣。
int32_t fetchTemp(int analogPin, int muxCount){
 float temp = 0;

 if( analogPin<2 data-blogger-escaped-analogpin="">5 || muxCount > 7 ){
  // analogPin 0, 1 used for ethernet shield SD Card Control
  // Multiplexer only 0~7
  return temp;
 }

 //get temp from Thermistor
 muxA = bitRead(muxCount,0);
 muxB = bitRead(muxCount,1);
 muxC = bitRead(muxCount,2);

 //select mux pint
 digitalWrite(muxPINA, muxA);
 digitalWrite(muxPINB, muxB);
 digitalWrite(muxPINC, muxC);

 temp = Thermistor(analogRead(analogPin));

 if(true ){
  unsigned long time = millis();
  Serial.print(time);  
  Serial.print(": analogPin ");
  Serial.print(analogPin);
  Serial.print(" MUX Port");
  Serial.print(muxCount);
  Serial.print("=> ");
  Serial.print("Celsius: "); 
  Serial.print(temp,2);               
  Serial.println("");
  Serial.println("");
 }

 return (int) (temp*100.0);
}

第一個if用來檢查輸入的值是否正確analogPin(2~5), muxPin(0~7)
第二個if用來輸出除錯訊息,true改false就不會印出來了

比較要注意的是最後一行回傳了 temp*100.0
由於SNMP協定沒有"浮點數"的型態,只能傳送整數;

所以這邊*100後轉成整數(int)取得小數點後第二位,到了 Cacti 再轉回來。

最後把
temp = fetchTemp(analogPin, muxPin);
加到 pduReceived() 判斷式內即可。


結果畫面




[Source Code]
https://github.com/Aspertw/ArduinoSNMPTemp/blob/master/SnmpThermal.ino

2015/03/25

Arduino SNMP溫度監控: 自訂SNMP oid

我的SNMP也是一知半解,
只知道每個訊息都需要一組oid,
類似身分證這樣唯一的識別碼;
這篇文章有詳細的說明  簡單網絡管理協議 (SNMP)

了解oid

在AgentPlus.ino的32~44行就是該訊息的oid,
這幾項的資訊都是標準格式,oid都是以1.3.6.1.2.1起頭
如果要傳自定義的資料,需要使用1.3.6.1.4.1的Private

Agentuino作者已經在49行留了arduino的oid
  .iso.org.dod.internet.private.enterprises.arduino (.1.3.6.1.4.1.36582)

這次我會使用 1.3.6.1.4.1.36582.X.Y
X: 代表Arduino的Analog Pin(A2~A5, A0~A1因為Ethernet Shield要控制SD卡不能用)
Y: 多工器的訊號來源

EX:
1.3.6.1.4.1.36582.2.0  => A2 的 MUX 0
1.3.6.1.4.1.36582.3.7  => A3 的 MUX 7
當然要怎麼編排可以自己決定,改程式就好。

在程式中只要判斷傳來的oid是不是前面18個字是"1.3.6.1.4.1.36582."
是的話在取得後方的X.Y做分析就可以知道要取得哪個溫度值

pduReceived()

這是主要處理SNMP的回應函式,如果有收到SNMP封包就會執行這個函式。
在212行多增加一個if判斷式,可以執行自己的程式碼
將原本的199~212行的程式碼由原本的
...
} else if ( strcmp_P(oid, sysServices) == 0 ) {

   ...

} else {

...

改成
...
} else if ( strcmp_P(oid, sysServices) == 0 ) {
   ...
} else if ( strncmp_P(oid, sysTempPrefix, locTempPrefix) == 0 ) {
   //code here 
} else {
...


其中 sysTempPrefix, locTempPrefix 請在變數宣告區新增
const static char sysTempPrefix[] PROGMEM   = "1.3.6.1.4.1.36582.";
static int32_t locTempPrefix          = 18;



之後程式碼會寫在這個區塊內,可以先加入
  Serial.println(oid);
來測試有沒有正確,正確的話終端機就會印出oid
請取消 setup() 第一行 Serial.begin(9600); 的注解

下圖是部分的程式碼截圖

(Serial後四行是回傳一個整數)


測試結果


下一篇 處理分析oid

[Source Code]
https://github.com/Aspertw/ArduinoSNMPTemp/blob/master/SnmpCustomOid.ino

Arduino SNMP溫度監控: SNMP

我是採用Arduino IDE 1.6.1版本,
Flash Library需修改才可以編譯成功,
舊版本1.0.5可能可以順利通過,
但是新版IDE好用很多,所以還是寫的複雜點好。


事前準備

以下Library,請下載後解壓縮到IDE目錄內的 libraries 資料夾,把後面的"-master"刪除。
EX: <IDE PATH>\libraries\Agentuino\(*.cpp, *.h)

放完之後,重開IDE
  • 檔案 > 開起 > 範例 > Agentuino > AgentPlus
  • 按下驗證
  • 然後一堆錯誤訊息XDD


error: 'prog_char' does not name a type
新版IDE不支援這個型態了,請開啟有這個錯誤訊息的檔案(Flash.h, Flash.cpp)
把"prog_char" 改成 "char",存檔。
error: variable 'XXX' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
 在 AgentPlus.ino 內,static char 要加上 const,存檔。

理論上可以編譯完畢了,燒進版子來測試吧!


測試工具

我使用Agentuino作者介紹的Net-Snmp工具來測試(https://code.google.com/p/agentuino/),以下以Windows為例。
網路與Arduino在同網段(192.168.20.X),我網路環境不同,截圖IP會有差異。

請下載安裝Net-Snmp後,開啟命令提示字元(cmd),切換目錄到C:\usr\bin



輸入 snmpget -v 1 -r 1 -c public 192.168.20.6 sysUpTime.0
 

有出現類似上圖就表示 SNMP 可以Work了

下一篇要加上自己的命令讓Arduino讀值回傳

2015/03/24

Arduino SNMP溫度監控: 多工器讀值

多工器簡單的說就是同一時間內從八個來源選擇一個
要控制多工器需要三隻訊號線(2^3 = 8)

我買到的是CD4051BE,Google DataSheet後可以找到接腳圖



Pin16: VDD接5V
Pin6,7,8: 接地GND
Pin9~11: 控制線,接到Arduino 2(A), 3(B), 4(C)
Pin 1,2,4,5,12~15就是訊號輸入的地方(0~7)

C B A  Channel
0 0 0  0
0 0 1  1
0 1 0  2
0 1 1  3
1 0 0  4
1 0 1  5
1 1 0  6
1 1 1  7


新版本的IDE多了個好用的指令:
bitRead( 數字 , 哪個位元 );

舉例來說,想要取得控制線訊號,只要這樣寫
A =  bitRead( 數字 , 0 );   // 0是最低位元
B =  bitRead( 數字 , 1 ); 
C =  bitRead( 數字 , 2 );

可以來寫程式了

  1. 在程式前端加上多工器的操作變數宣告
    #define muxPINA 2
    #define muxPINB 3
    #define muxPINC 4
    int muxA = 0;
    int muxB = 0;
    int muxC = 0;
    int muxCount = 0;
    分別對應控制線腳位跟數值
  2. setup() 內加入 PinMode定義輸出腳
    pinMode(muxPINA, OUTPUT);
    pinMode(muxPINB, OUTPUT);
    pinMode(muxPINC, OUTPUT);
  3. loop() 內修改為這樣
    for( muxCount=0; muxCount<=7; muxCount++){
     muxA = bitRead(muxCount,0);
     muxB = bitRead(muxCount,1);
     muxC = bitRead(muxCount,2);
    
     //select mux pints
     digitalWrite(muxPINA, muxA);
     digitalWrite(muxPINB, muxB);
     digitalWrite(muxPINC, muxC);
    
     float temp;
     temp = Thermistor(analogRead(ThermistorPIN));
     Serial.print("MUX Port");
     Serial.print(muxCount);
     Serial.print("=> ");
     Serial.print("Celsius: "); 
     Serial.print(temp,3);
     Serial.println("");
    }
    Serial.println("");
    delay(2000);  
    

for迴圈從0跑到7,設定多工器的ABC控制訊號選擇想要的訊號
再在呼叫 Thermistor函式讀取溫度資料

就可以單靠一隻Analog Input讀取8個溫度值

下篇要來測試SNMP了


[Source Code]
https://github.com/Aspertw/ArduinoSNMPTemp/blob/master/ThermistorWithMUX.ino

Arduino SNMP溫度監控: 熱敏電組讀值

主要是參考此篇文章
Arduino: Analog Input NTC Thermistor 用熱敏電阻量度溫度 @ 未来ガジェット研究所

請先到 Arduino Playground: Reading a Thermistor
複製 The Elaborate Code (cleaned up a bit) 這個版本的程式碼至Arduino IDE。


電路:
 (Ground) ---- (10k-Resistor) -------|------- (Thermistor) ---- (+5v)
                                     |
                                Analog Pin 0 
熱敏電阻與10K電阻串連後,熱敏端接電源,兩個電阻中間訊號接到Arduino的A0類比輸入。

 程式碼:
#define ThermistorPIN 0 > 0表示你接的類比輸入,如果接A2,這裡就要改2

float pad = 9850;   >  電阻的實際數字,請用電表量測後填入

 驗證之後燒錄進Arduino就可以看到溫度了(右下的Baud Rate請改115200)

想知道更多計算的詳細的話,請把程式碼的21~32行注解拿掉
想取得華式溫度請把第36行注解取消


ADC是Arduino取得的值。
pad是電阻值。
volt是計算出來熱敏電阻的電壓,所以程式碼上方有個vcc = 4.91,這可以拿掉。
Resistance也是計算出的電阻值

十分簡單吧!
下一篇要來加上多工器

Arduino SNMP溫度監控: 前言

這是篇寫廢話與雜七雜八的東西

由於要監控機房內每個機櫃的上下溫度(冷氣從下吸,熱風往上排)
土砲是買便宜的LCD溫度計來用,
但是一定得在現場才看的到,
也無法知道歷史紀錄;

於是就想到利用Arduino與SNMP通訊協定做溫度的紀錄
由於SNMP在網路設備很普遍,也很多Tool可以監測與紀錄
最著名的大概就是Cacti吧!

整個架構就是利用Arduino讀取多組溫度感應器,
透過SNMP通訊協定讓Cacti讀取資料後紀錄


成品照:

需求設備:
  • Arduino UNO (其他板子也行 但是UNO便宜又好用
  • Ethernet Shield W5100 網路擴充版,有Library直接可套
  • 4051 多工器,可以把一個輸入擴充為八個 (Arduino Playgroud介紹)
  • 熱敏電阻溫度線
  • 10K精密電阻(電組會影響溫度計算的誤差)

函式庫:

一開始就把全部東西放在一起會很複雜,
所以我習慣每個功能拆開測試正常後再組合,
下一篇就是怎麼使用熱敏電阻抓溫度。


系列文章

[Source Code] https://github.com/Aspertw/ArduinoSNMPTemp