2015/12/21

Codeigniter 整合 Symfony event dispatcher

我是參考此篇文章 http://codeigniter.org.cn/forums/thread-15598-1-1.html
補上一些我自己實作的步驟跟心得

[環境] 

Codeigniter 3.0.3

[步驟] 

  1. 安裝Composer(略 
  2. 安裝symfony/event-dispatcher (Packagist
  3. 設定CI使用composer 
  4. 設定composer autoload PSR-4 
  5. 新增CI Hook 讓 Controller 實例後執行事件註冊 
  6. 實作Symfony EventDispatcher 與 Event 
  7. 在CI Controller建立Event Dispatcher 
  8. 呼叫Event  

2015/10/11

Montreal - Day 3 10/10 賞楓聖地坐纜車賞楓去

我姐在兩個月前就買了便宜的庫碰券
可以搭乘纜車上山加午餐

地點叫Sommet du Mont-Tremblant
從蒙特婁開車要兩個半多小時
沿路已經有很多樹黃紅相接

到達時渡假村是濃濃的歐洲風格
建築物搭配楓紅美景美不勝收

搭乘纜車到達最高點之後
第一眼看見的不是楓樹
而是公告牌寫目前溫度是攝氏0度
真的非常的冷啊

之後到山頂各處看看
整個山頭被黃紅綠色裝扮著
搭怕美麗的建築物、藍天白雲與河流
楓葉原來這麼美麗啊


賞玩楓葉回到蒙特婁之後
還有一點時間我到了奧運場管晃晃
場管還是保持的很好
只是建物都沒有開放
周邊也只有幾個當地人在打排球
玩滑板、越野腳踏車等


Montreal - Day 2 10/09 downtown隨便逛

凌晨就開始下雨
這邊的雨通常都毛毛雨
但是會讓原本已經很冷的天氣冷到骨頭裡
所以今天幫我姊帶小孩
逛了一下超級超市買食材

下午雨停了
天氣還是陰天
就搭公車到市中心晃晃

高樓大廈很多
不過隨處都是綠地加公園
讓市中心沒這麼水泥叢林
加上規劃的方方正正的
隨處走走還不錯

Montreal - Day 1 10/08 生活準備

下飛機後跟我姐會面
我姐已經預先幫我買好一些好用的工具


>
捷運公車一個月無限坐卡

卡片是像悠遊卡靠卡感應的
進站/上車刷卡 到達直接出站/下車
有這個卡片想去哪都很方便又省錢
價錢好像是$30加幣的樣子(不是很記得


公共自行車bixi鑰匙
這個需要當地居民才可以申請
不然用信用卡註冊也可以
詳細怎麼用就不清楚了

用鑰匙可以直接用車
45分鐘之內免費
bixi的點非常的多
用來搭配捷運很方便

手機
身為吃到飽手機用戶
沒有隨身網路可以用有點不方便
當地的網路費用非常昂貴
我辦的fido易付卡開卡$10
一個月1GB流量要多加$30
比我在台灣吃到飽還要貴許多

由於第一天還在調時差
搭飛機也非常的累
確定好住的地方跟大致整理後
八點半就已經很想睡了

2015/10/10

Montreal - Day 0 飛行與轉機


有個機會可以去加拿大的蒙特婁走走
而且還是住個25天
也是生平第一次自己一人出國
剛開始還是有點怕怕的
查了一些資料跟分享文章就比較安心點

我是搭乘長榮航空23:55的班機
從台中搭乘國光客運到桃園機場
國光客運班次大約一小時一班
我做的班次車上只有兩個人
(還都去第二航廈 超巧)

避免塞車下午四點就出發
很幸運的完全沒塞還提前到
check-in跟通關也超快速
所以我七點左右就在候機處等我那五個小時後的班機= =|||


想著反正有五個小時
就把整個第二航廈的登機口全部逛過一次
第二航廈登機口比起第一都有設計過
只是有些登機口的候機位子有USB 大部分都沒有
不知道的人可能就會在上機前把手機電用光


去程搭乘的是B747-400客機
有二樓的位子可以選
好處在窗邊有個櫃子可以放東西
不需要一直從前放座位下拿進拿出
非常的方便


由於班機飛到溫哥華也是晚上
航空公司要讓旅客休息都會要求把窗戶關閉
所以整趟旅程都是黑的
也看不到美麗的空景


機上的餐點包括晚餐+早餐各一份
味道普普
但是也有爬到文章說因為人在高空對甜鹹味覺遲鈍
所以不會覺得東西好吃
隨餐都有水果跟甜點


下機後到達溫哥華
大約14度加下雨冷的不得了
隨之讓我最擔心的入境跟海關講話也來了
好險這次遇到的沒像網路爬到的這麼恐怖
用爛爛的英文回答問題就讓我入境了
<<別人的入境流程>>

實際需要加一下拿入境卡給出口官員後
要往出口的方向(路上交通指示的另一邊)一直走一直走到有賣東西的地方後
搭電扶梯上到國際線大廳
然後再一直走大約十分鐘以上才會看到air canada的櫃台辦理轉機


查說明都是要找轉機櫃台
但是現場沒有這個指示
鼓起勇氣問了服務人員
就直接帶我把行李丟進轉盤了
(由於我的票是跟長榮一起買的 所以行李只要丟進去就好 不用再check-in)


到達溫格華時間已經很晚了
機場商店幾乎都休息
沒什麼人在機場內
讓我覺得溫哥華機場只有入境區很大之外
其他都很像地方機場的感覺


等待約四個小時的轉機時間後
搭乘air canada班機前往蒙特婁
由於在長榮沒什麼睡(引擎聲超吵,椅子也不舒服)
三小時多的航程我只醒不到一小時吧!

到達蒙特婁是早上
在飛機上有感到日出的光芒(坐走道看不到悲劇)
也很順利的領到行李與我姐會合

溫哥華還有蒙特婁的機場都有提供無線網路
到了可以立刻報平安跟聯絡 挺不錯的



2015/07/04

Webduino 改寫用 jquery


今天參加了Webduino的開發教學 圖片來自<官網>
因為範例都是原生的javascript,上課時一直Try不出來怎麼改用jQuery
晚上研究了一下改寫成功

以官網範例  https://webduino.io/tutorials/tutorial-01-led.html

window.addEventListener('WebComponentsReady', function () {
 var board = document.getElementById('board'),
 light = document.getElementById('light');

 board.on('ready',function ready() {
  var led = document.getElementById('led');

  light.addEventListener('click', function() {
   if(light.className == 'on'){
    led.off();
    light.className = 'off';
   }else{
    led.on();
    light.className = 'on';
   }
  }, false);
 });

}, false);

使用jQuery改寫

$(document).bind('WebComponentsReady', function(){
 $light = $("#light");

 $("#board")[0].on('ready',function ready() {
  var led = $("#led")[0];
  $light.click(function(){
   if( $light.hasClass('on') ){
    led.off();
    $light.attr('class', 'off');
   }else{
    led.on();
    $light.attr('class', 'on');
   }
  });
 });
}, false);

  • addEventListener 可用 bind 替換,我習慣綁在Document
  • 使用WebComponents時,需要的是HTML DOM,所以加上 [0] 已取得 (不加表示取得jQuery Object)

2015/06/29

Laravel Debug Tool

clockwork
https://github.com/itsgoingd/clockwork

Laravel Debugbar
https://github.com/barryvdh/laravel-debugbar

2015/06/27

北科徵選學生證悠遊卡設計

由於我之前有做過喬巴學生證


不免俗的也設計了一下,但是沒實際拿去投稿啦 (我又不是北科學生


延伸閱讀
喬巴證件才是王道!!

2015/06/26

laravel 5 預設處理 Exception

如果想要讓Exception有個預設的處理方法
如:  遇到 NotFoundHttpException 想記錄起來,回傳甚麼特別的東西之類的

或者是
用Model::findOrFail() 找不到記錄產生 ModelNotFoundExceptio時,
可以預設回到列表或是首頁之類的。

一來不會讓Client看到炸掉的網站
二來不用到處在Controller處理Exception

 方法很簡單,修改 App\Exceptions\Handler.php
method render 內增加類似:

if ($e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
    return response('not found');
}

這邊只是簡單回傳個response物件
要做甚麼就看需要了


http://laravel.com/docs/master/errors#the-exception-handler

2015/06/18

2015/05/27

Gulp 處理資料夾內的scss 並 concat 成資料夾名稱

事情是這樣的
我想要把每個view的scss都單獨一個檔案
然後相關的放在同個資料夾,檢單來說就是跟著Controller走
然後輸出時利用gulp編譯後自動合併成資料夾名稱引用
這樣比較不容易動到其他的scss

目錄結構:
\resource\assets\sass
|    \memo
|    |    list.scss
|    |    show.scss
|    \article
|    |     list.scss
|    |     show.scss

\public\css

目標是執行完gulp之後
public\css會出現 memo.css, article.css
是由底下的 *.scss 編譯後合併
試了很多種方法最後終於成功了

======================================

由於gulp-concat的檔名需要根據資料夾
網路的範例都是寫死檔名
如果需要用變數處理,需要重新傳回 stream 給 gulp 處理
檔名的部分是使用 gulp-tap 抓取
gulp.task('sassIn', function () {
 var srcPath = 'resources/assets/sass';
 var outputPath = 'public/css';

 return gulp.src(srcPath + '/**/')
  .pipe(tap(function(file, t) {
         if ( file.isDirectory() && file.relative.length) {                
                var name = file.relative + '.css';

                console.log("processing folder:", file.path);
                console.log("\toutput filename is:", name);

                return gulp.src( path.join(file.path , '/*.scss') )
                   .pipe(tap(function(file, t) {
                    console.log("\tprocessing file:", file.path);
                   }))
                   .pipe(sass().on('error', sass.logError))
                   .pipe(gulpif(util.env.production, minifier()))
                      .pipe(concat(name))
                      .pipe(gulp.dest(outputPath));
            }
     }));  
});

如果處理的檔案是目錄的話,
而且該目錄是位於srcPath下(直接指srcPath 的 file.path會沒東西)
就重新傳回一個gulp並且處理

=====================================

搭配laravel elixir使用
elixir(function(mix) {
  mix.task('sassIn');
});

BTW如果要使用elixir的module
需要多加路徑去指(這個我也卡很久)
var requireBase = './node_modules/laravel-elixir/node_modules/';
var util        = require(requireBase + 'gulp-util');
var gulpif      = require(requireBase + 'gulp-if');
var minifyCss   = require(requireBase + 'gulp-minify-css');
var sass        = require(requireBase + 'gulp-sass');
var concat      = require(requireBase + 'gulp-concat');
var minifier    = require(requireBase + 'gulp-minify-css');

 

======================================

留一下紀念我寫的無用code XDD
function sassIn(mix, src, output, options){
 var srcPath = 'resources/assets/sass';
 var compilePath = 'resources/assets/sass/compiled';
 var outputPath = 'public/css';
 if( src ){ srcPath = path.join(srcPath, src); } 
 if( output ){ outputPath = path.join(outputPath, output) };

 //get src dir's sub dirs
 var dirs = readDirFiles(srcPath, null, true);
 for(i in dirs){
  var dir = dirs[i];
  if( dir == 'compiled') continue;
  var currentCompiledDir = path.join(compilePath, dir);
  var currentDir = path.join(srcPath, dir);
  var outputFile = path.join(outputPath, dir) + '.css';

  //check file exists
  try{ console.log('unlinkSync', outputFile); console.log(fs.unlinkSync(outputFile)); console.log('unlinkSync done');} catch(err){ /*just igone it*/ }
  sleep(1000);

  //loop subdir files to sass
  var files = readDirFiles(currentDir, function(file){
   filepath = path.join(dir, file);
   mix.sass(filepath, currentCompiledDir);
  });

  sleep(1000);

  //merge under subdir
  console.log('stylesIn');
  mix.stylesIn(currentCompiledDir, outputFile);
  console.log('stylesIn end');
 }
}

function readDirFiles(srcPath, cb, onlyDir){
 var dirFiles = [];
 var files = fs.readdirSync(srcPath);
 for(var i in files){
  var file = files[i];
  var filepath = path.join(srcPath, file);

  var isDir = fs.statSync(filepath).isDirectory();
  if( (onlyDir && !isDir) || (!onlyDir && isDir) ){
   continue; 
  }

  dirFiles.push(file);
  if(cb) cb(file);
 }
 return dirFiles;
}
function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
    if ((new Date().getTime() - start) > milliseconds){
      break;
    }
  }
}

2015/05/07

[筆記] lumen不用指到public

lumen如果直接丟到htdoc裡面跑
即使指到 public 內 還是會出現



試了很多方法都不動,看來還是只能用virtual host
但就是不想用vhost !!

做法
** lumen 是專案名稱
1. 將 lumen/public 兩個檔案 copy 到 lumen/ 下
2. 修改index.php
$app = require __DIR__.'/../bootstrap/app.php';
改成
$app = require __DIR__.'/bootstrap/app.php';

$app->run();
改成
$app->run($app['request']);

3.存檔,收工




[來源] https://laracasts.com/lessons/introducing-lumen
@

2015/05/03

台灣銀行的匯率JSON

2018/01/05
停止服務

2017/05/16
要用這個服務我很歡迎,但是太頻繁要求已經是變相的攻擊了
  • 要拿到所有的匯率可以直接要currency.json就好,裡面有全部的資料。
  • 資料更新時間是9~17每整點更新,每分鐘要都是相同的
211.23.4.187 這個IP每分鐘持續來要資料,造成伺服器負荷,已BAN



花了點時間把原本的程式全部改寫
前端列表的部分也加上更改外幣金額 與 台幣換算的功能 (實際換算還是要以銀行為主)
http://rate.asper.tw/

Source Code: https://github.com/Aspertw/bot-rates
PS. 這是以GAE環境寫的


( 其實如果要找匯率我覺得 比率網 比較好用 ((自己打臉 )
=====================

最近要去日本玩,有一直在觀察日本匯率
加上有在玩Slack Bot,想結合BOT跟匯率
無奈匯率很少有OPEN DATA,
用了YAHOO的YQL查詢結果跟台灣銀行的差距不小。

所以,自己的匯率API自己刻!!
然後,丟給Google App Engine當線上服務XDD

網址: http://asper-bot-rates.appspot.com/currency.json
直接開也有訊息怎麼連

範例: http://asper-bot-rates.appspot.com/currency.json?JPy
就是後面加 ?幣值
大小寫都沒關係

結果是這樣
{
   "updateTime":1430409617,
   "rates":{
      "buyCash":"0.24940",
      "buySpot":"0.25560",
      "sellCash":"0.26000",
      "sellSpot":"0.25960"
   }
}

updateTime: 台灣銀行的最後更新時間,unix timestamp
rates: 四種匯率價, buy:買入, sell:賣出, Cash:現金, Spot:即期

由於GAE免費有限制額度,
這資料每24小時更新

原始碼只有34行,就不丟GitHub了

$site = "http://rate.bot.com.tw";
$csvIndex = array(
 'currency'=>0, 'buyCash'=>2, 'buySpot'=>3, 'sellCash'=>12, 'sellSpot'=>13
);

//get csv link
$page = "/Pages/Static/UIP003.zh-TW.htm";
$html = file_get_contents($site.$page); 

$aPos = strpos($html, '<a id="DownloadCsv"');
$hrefPos = strpos($html, 'href="', $aPos);
$endPos = strpos($html, '">', $hrefPos);
$link = substr($html, $hrefPos+6, $endPos-$hrefPos-6);
$link = htmlspecialchars_decode($link);
$updateTime = strtotime(substr($link, strpos($link, '&date=')+6 )); 

//open taiwan bank rate csv
$fp = fopen($site.$link, "r");
$row = 0;
$currencyRates = array();
while( ($data = fgetcsv($fp, 1000, ',')) !== FALSE ){
 $row++;  if($row == 1){ continue; } //skip title row
 foreach($csvIndex as $name=>$index){
  $$name = trim($data[$index]);
 }
 $currencyRates[ $currency ] = compact('buyCash', 'buySpot', 'sellCash', 'sellSpot');
}
fclose($fp);

//save to json cache
$data = array(
 'createTime' => time(),
 'updateTime' => $updateTime,
 'rates' => $currencyRates
);

2015/05/02

在Google App Engine(GAE) 使用 firebase REST API

本想用firebase當作GAE的DB的
但是讀取速度有點慘...
好在資料量不大,所以改用gae的memcache
寫都寫了做個紀錄

溫馨提示:
由於GAE不能用curl,所以要用GAE自己的URL Fetch
需要使用file_get_contents with stream_context


function firebase($url, $data=null, $method='GET'){
 $method = strtoupper($method);
 $context = [
  'http' => [
   'method' => $method
  ]
 ];

 if($method != 'GET' && $method != 'DELETE' && !is_null($data)){
  $content = json_encode($data);
  $header = "Content-Type: application/json\r\n".
      "Content-Length: ".strlen($content)."\r\n";
  $context['http']['header'] = $header;
  $context['http']['content'] = $content;
 }

 $context = stream_context_create($context);
 $result = file_get_contents($url, false, $context);
 return $result;
}

Sample:
$url = "https://example.firebaseio.com/somedata.json";

//取得資料
$res = firebase($url);  //$res 是json text

//送出資料
$data = array('a'=>123, 'b'=>456);
$res = firebase($url, $data, 'PUT');

PATCH跟POST沒試過,但是應該也可以動啦XDD


firebase REST API: https://www.firebase.com/docs/rest/guide/saving-data.html
Google App Engine Url Fetch: https://cloud.google.com/appengine/docs/php/urlfetch/

2015/04/28

PHP Slack Bot

使用PHP以Slack的Incoming WebHooks與Outgoing WebHook的機器人回應程式。

Slack端

Slack Service依次新增Incoming WebHooks與Outgoing WebHook。

Outgoing WebHooks

當User發送關鍵字起頭的訊息時,Slack會照此設定傳送到指定URL


  • Channel: 監聽的頻道
  • Trigger Word: 觸發的關鍵字,可以逗號分隔
  • URL: 接收資料的URL,一行一個
  • Token: Slack產生的,可以做為核對身分的依據(不會希望隨便任何人都可以傳送到這隻程式吧)


Incoming WebHooks

接收外部訊息顯示在Slack內的服務

Post to Channel: 預設發送的頻道,程式端可以複寫
Webhook URL: Slack產生,程式端要傳送的位址


PHP原始碼在 Github

2015/04/20

571的接替者: iRobot Roomba 780


571不知不覺也買了四年
幾次回家都覺得沒有掃得像以往乾淨
充電座跟集塵盒等配件壽命都差不多了
於是在一段時間的找尋後
還是繼續選擇iRobot

2015/04/16

PHP dio_write 在 Windows 會讓 0a 變成 0d0a

這兩天再寫RS232的程式,
用了Direct IO(DIO)這個模組,

dio_write 碰到 0a 這個16進制數值時
會自動轉成 0d 0a 的換行字元 (等同 CR LF)
導致送出的Code會多出 0d 而不正確

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也是計算出的電阻值

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