2015年4月28日 星期二

氣象站 3.1 Router (nrf24l01 router)

由於最近Rpi一代借出去了,目前沒有Server可以用,所以就把我的Wifi Arduino拿來用
基本上這個就是Leonardo + Linkit connect module

總之就是將nRF24l01接收到的資料打包之後,送到Xively上面(P.S Xively看來是很難在註冊了..)

完整的程式在這邊(待補)
底下因為Sublime Text 3 的字體+上色太好看了,所以我用截圖來說明

 首先是Setup,要做的就是設定nRF24l01,這邊使用的Library是Mirf,很方便使用的一個library,設定完通訊方式,接收地址,以及接收大小之後就可以了.
再來就是利用AT cmd讓MT7681模組Reset,之後等待RecoveryMode結束的左箭頭之後就連線到AP.
但是如果MT7681並沒有對Reset回應的話,就會用GPIO將Wifi Arduino整台Reset,因為我當初Layout的時候並沒有把MT7681的RST接上GPIO而是和Leonardo的接在一起.













Loop也很簡單,收到nRF的資料之後,就將資料放到Structure裡面,然後把Wifi 的資料送出去
另外我用WatchDog來防止整台因網路不通的關西而Freeze的狀況.

最後就是Linkit connect module library當中需要的callback了










Wifi送資料的部分,剛開始先準備好資料,將Structure內的各個資料轉成String,同時查詢api.xively.com的IP位置,最後連到Server送Http header與資料.


============================
PUT /v2/feeds/(feedID).csv HTTP/1.1
Host: api.xively.com
X-ApiKey: (API_key)
Content-Length: (datalength)
Content-Type: text/csv
Connection: close

Humidity,(humidity)
Temperature,(temperature)
============================
大概是這種感覺

順帶一提,一樣是為了防止網路連線出錯導致的Freezing,加了WDT作保護


連線到AP的大概就是這種感覺...

以上就是針對接收站的紀錄了~

2015年4月27日 星期一

Linkit connect module 使用

上圖就是Linkit connect module,使用的其實就是MT7681 Wifi Soc
最近在完成的Project其中一個有用到這個模組,所以留個筆記方便日後使用

首先就是Linkit connect module其實是一個可以獨立Program的Wifi module
SDK是Open出來的,在官方網站就可以抓到最新的SDK與說明文件

先從硬體開始,因為這個模組,Seedstudio有做了一個Open Hardware的開發版可以用,所以可以先從HDK上面下載Eagle Layout files,打開來之後就可以直接沿用他們的做好的Linkit connect module Library,順帶一提,在Eagle裡面要跨檔案的複製只需要再命令列打copy之後打paste就可以貼上了,直接用左邊那一欄的複製是不行的

Update:我把它拿出來做成Library了,在這邊下載
所以就可以很直接的整合到其他PCB當中,底下這是Linkit connect with Arduino的電路圖
再來就是軟體,因為我的使用方式是把模組當作Wifi模組使用,所以是Arduino控制的,這就需要在Linkit connect上面燒錄AT cmd 的韌體,網路上已經有人寫好了一套韌體可以拿來用,不過我有再修改過,在這邊,可以看到我是fork IOTPlayer來改的
把Arduino_sample底下的MT7681的資料夾整包放到Arduino 的 library資料夾就可以在Arduino裡面使用了.

燒錄方式如下:在官網下載好Linkit connect module SDK之後,找個Serial to USB(總之就是一個接上Linkit connect module 的 COM port)接上模組,SDK裡面有個資料夾叫做Uploader,開命令程式列並且把AT cmd的兩個韌體放到該資料夾之後打這串指令:
mt7681_uploader.exe -f MT7681_sta_header.bin -c COM7
這樣就燒錄好了

Update:我把Uploader從SDK拿出來放在Github了,在這邊下載

接下來就從範例開始
首先,修改這三行
LC7681Wifi wifi(&Serial1);   <=填入使用的Serial
            Uno的話因為只有一個Serial,所以把Linkit connect接上Serial之後,這邊填入Serial
            Leonardo的話因為本身有USB CDC的Serial,所以實體的Serial是Serial1,所以填                               Serial1而Due,與Mega等等具有多個Serial的就看你是接上哪一組就填哪一組

const char ssid[] = "";          
const char key[] = "";
                       以上兩行填入你想連接AP的SSID與Key

接下來接上模組,燒錄完就可以用了.




2015年4月24日 星期五

Nrf24l01 Sensor node (氣象站 3.1)

 這是繼氣象站3.0的後續版本,基本上因為前一版真的是頗悲劇的
前一版的主要問題在於耗電量與體積問題,還有Layout的各種錯誤

這次的版本基本上就是

  1. 核心 Arduino Atmega328
  2. 氣壓計 BMP085
  3. 溫溼度計 HDC1000 
  4. 無線模組 Nrf24l01

 可以看到上方的Nrf24l01模組與底下的BMP085和右邊的HDC1000以及被遮住的Atmega328
 這次重點就擺在耗電量了,上次做的那一版可能是LDO的耗電量吧,啥待機耗電量大成這樣(60uA),這次是CR2032直接接上系統電壓,所以基本上不會需要任何穩壓,降低耗電量,
同時把讀取的時序調整過了,我簡單說明一下:
通常Sensor在測量的時候都會需要點時間,但是Arduino的Library常常會將這點時間用delay帶過
這就造成Arduino長時間的閒置,徒增耗電量的概念,所以我把開始測量和讀取測量值分開,中間用待機,這樣就可以省掉滿多閒置時間的

文字說明似乎不夠,用圖來解釋好了.
這是測試方法,由於最高耗電量不到數百mA的等級,用一個10歐姆的電阻串在中間,然後拿個示波器量就好(待機耗電量依然需要用電表來測)
這是目前的耗電量曲線,可以看到剛開始有兩次高峰,分別是HDC1000和BMP085的溫度啟動測量,第二個就是把測量的值讀進來,然後啟動BMP085的氣壓和HDC1000的濕度測量,最後將資料計算完之後,啟動RF,把資料發射出去

如果沒有特別設定的話,從第一個高峰到最後一個通通都會耗電.
這是RF發射的詳細波形,可以看到基本上設定要發射之後Arduino就睡眠了,等到最後面在啟動看有沒有傳成功.
耗電量的計算,基本上就是算面積,小心估計出各個耗電的時間和耗電量之後(V=IR,所以示波器上面的電壓除以10(我用的電阻值)就是電流),加總起來算平均

我的計算出來大概.....我忘了,等等翻一下計算過程去哪裡.....= ___ =
只記得大概靠CR2032撐個一年
有發射器當然就要有個接收站,但是我手邊的Raspberry Pi 一代借出去了,所以拿我的Wifi Arduino來傳,這就下一篇來解釋了.

2015年4月10日 星期五

Arduino countdown timer

 好久沒有用洞洞板做東西了..
這次是一個Arduino 倒數計時器,只是比一般的計時器多了一個警告的控制,也就是說倒數到幾分鐘會亮黃燈,歸零就亮紅燈這樣,功能很簡單,
架構就是三個按鈕,上/下/開始(長按:設定警告分鐘),一個Reset(回到開始的設定),然後兩個LED與四個七段顯示器.另外因為Pin不夠用了,所以加了一個74HC595控制七段顯示器的abcdefg

很亂的配線...
程式方面比較複雜的有兩個,長按切換模式,以及四個七段顯示器的掃描

接線圖我就不畫了,反正把程式碼的各個Pin設定好就能用

#include <stdio.h>

byte digit[8] = {1, 3, 5, 6, 7, 2, 4, 0};
byte num[4] = {5, 6, 7, 4};
byte led[3] = {2, 3, A4};
byte button[3] = {A0, A1, A2};
int SER_Pin = 8;   //pin 14 on the 75HC595
int RCLK_Pin = 9;  //pin 12 on the 75HC595
int SRCLK_Pin = A3; //pin 11 on the 75HC595
int reset = 10;

#define number_of_74hc595s 1
#define numOfRegisterPins number_of_74hc595s * 8
boolean registers[numOfRegisterPins];
byte seven_seg_digits[10][7] = { { 1, 1, 1, 1, 1, 1, 0 }, // = 0
  { 0, 1, 1, 0, 0, 0, 0 }, // = 1
  { 1, 1, 0, 1, 1, 0, 1 }, // = 2
  { 1, 1, 1, 1, 0, 0, 1 }, // = 3
  { 0, 1, 1, 0, 0, 1, 1 }, // = 4
  { 1, 0, 1, 1, 0, 1, 1 }, // = 5
  { 1, 0, 1, 1, 1, 1, 1 }, // = 6
  { 1, 1, 1, 0, 0, 0, 0 }, // = 7
  { 1, 1, 1, 1, 1, 1, 1 }, // = 8
  { 1, 1, 1, 0, 0, 1, 1 } // = 9
};


void setup() {
  Serial.begin(115200);
  for (int i = 0; i < 3; i++) {
    pinMode(button[i], INPUT_PULLUP);
  }
  pinMode(reset, INPUT_PULLUP);
  for (int i = 0; i <= 3; i++) {
    pinMode(num[i], OUTPUT);
  }
  for (int i = 0; i < 3; i++) {
    pinMode(led[i], OUTPUT);
    digitalWrite(led[i], HIGH);
  }

  pinMode(SER_Pin, OUTPUT);
  pinMode(RCLK_Pin, OUTPUT);
  pinMode(SRCLK_Pin, OUTPUT);
  clearRegisters();
  writeRegisters();
}
int second = 0;
int minute = 0;
int lastth = 0;
int lastminute = 0;
uint32_t lastbutton = 0;
uint32_t counttime = 0;
uint32_t ledtime = 0;
bool counting = 0;
int state = 0;
bool settime = 0;//0=countdown 1=last
int th = 5;
void loop() {
  if (settime) {
    disp(th / 10, th % 10, 0, 0);
    digitalWrite(led[1], HIGH);
  }
  else if(0==digitalRead(reset)){
   settime=0;
   counting =0;
   minute = lastminute;
   th = lastth;
   second = 0;
 }
 else {
  disp(minute / 10, minute % 10, second / 10, second % 10);
  if (millis() - counttime > 1000) {
 
    if (counting == 1) {
      second--;
      if (second < 0) {
        minute--;
        second = 59;
      }
      if (minute < 0) {
        minute = 0;
        second = 0;
      }
      Serial.print(minute);
      Serial.print(",");
      Serial.println(second);
      if (minute < th) {
        digitalWrite(led[0], HIGH);
       // Serial.println("Alarm");
      }
      else {
        digitalWrite(led[0], LOW);
      }
      if (minute <= 0 && second <= 0) {
        digitalWrite(led[1], HIGH);
        //Serial.println("STOP");
      }
      else {
        digitalWrite(led[1], LOW);
      }
      state = !state;
      digitalWrite(led[2], state);
      //Serial.println(digitalRead(led[2]));
    }
    else {
        //Serial.print(digitalRead(led[0]));
        //Serial.print(digitalRead(led[1]));
        //Serial.println(digitalRead(led[2]));
       // digitalWrite(led[0], !digitalRead(led[0]));
       // digitalWrite(led[1], !digitalRead(led[1]));
     }
     counttime = millis();
   }
   else if (millis() - ledtime>200){
     if (counting == 0){
      digitalWrite(led[0], !digitalRead(led[0]));
      digitalWrite(led[1], !digitalRead(led[1]));
      state = !state;
        digitalWrite(led[2], state);  //ditry fix about digitalRead error on led2
        ledtime = millis();
      }
    }
  }
  scan();

}
void change(int i) {
  switch (i) {
    case 0:
    if (0 == settime) {
      if(counting){
        counting = 0;
      }
      else{
        counting = 1;
        lastminute = minute;
        lastth = th;
      }
    }
    else {
    }
    break;
    case 1:
    if (0 == settime && counting == 0) {
      minute++;
      second = 0;
      //Serial.println(minute);
    }
    else  if (1 == settime) {
      th++;
      //Serial.println(th);
    }

    break;
    case 2:
    if (0 == settime && counting == 0) {
      minute--;
      if (minute < 0) {
        minute = 0;
      }
      second = 0;
      //Serial.println(minute);
    }
    else if (1 == settime) {
      th--;
      if (th < 0) {
        th = 0;
      }

      //Serial.println(th);
    }
    break;
  }


}
void scan() {
  uint32_t start = 0;
  int flag = 0;
  for (int i = 0; i <= 2; i++) {

    if (0 == digitalRead(button[i])) {


      if (millis() - lastbutton > 150) {
        start = millis();
        while (0 == digitalRead(button[i])) {
          if (settime) {
            disp(th / 10, th % 10, 0, 0);
          }
          else {
            disp(minute / 10, minute % 10, second / 10, second % 10);
          }
          if (i == 0 && millis() - start  >= 500 && flag==0) {
            settime = !settime;
            //Serial.print("SET TIME:");
            //Serial.println(settime);
            flag = 1;
            lastbutton = millis();
         
          }
        }

        if(flag==0){
          change(i);
          lastbutton = millis();
        }
      }

      //Serial.println(millis() - start);
    }

  }

}
void disp(int a, int b, int c, int d) {
  int data[4] = {a, b, c, d};
  for (int dig = 0; dig <= 3; dig++) {

    for (byte seg = 0; seg < 7; ++seg) {
      setRegisterPin(digit[seg], !seven_seg_digits[data[dig]][seg]);
      // digitalWrite(digit[seg], !seven_seg_digits[data[dig]][seg]);
    }
    writeRegisters();
    digitalWrite(num[dig], HIGH);
    delayMicroseconds(100);
    digitalWrite(num[dig], LOW);
  }
}

//set all register pins to LOW
void clearRegisters() {
  for (int i = numOfRegisterPins - 1; i >=  0; i--) {
    registers[i] = HIGH;
  }
}


//Set and display registers
//Only call AFTER all values are set how you would like (slow otherwise)
void writeRegisters() {

  digitalWrite(RCLK_Pin, LOW);

  for (int i = numOfRegisterPins - 1; i >=  0; i--) {
    digitalWrite(SRCLK_Pin, LOW);

    int val = registers[i];

    digitalWrite(SER_Pin, val);
    digitalWrite(SRCLK_Pin, HIGH);

  }
  digitalWrite(RCLK_Pin, HIGH);

}

//set an individual pin HIGH or LOW
void setRegisterPin(int index, int value) {
  registers[index] = value;
}

void serialEvent(){
 
  minute = Serial.parseInt();
  second = Serial.parseInt();
  th = Serial.parseInt();
  counting = Serial.parseInt();
  settime = 0;
  while(Serial.available()){
      int dummy = Serial.read();// statement
  }


}

====Update:2016/08/28====
Video:

AFE4400 breakout ver3.0


 總之就是把東西整理好一點,加入了ADXL345,打算用加速度計把移動造成的干擾削掉...
然後Sensor改用DCM03,整合好了PN與雙波段的LED

2015年4月5日 星期日

AFE4400 血氧計 ver2.0


第二版改的點主要是增加大容量的LED decoupling cap,然後Pin改用1.27mm的減少長度
同時把接點改成SMD並且放到背面

Code的部分則是新增了控制的面板,專門控制LED 亮度,Gain,還有AFE內部的參數