2014/04/13

Arduino Yún:Bridge程式庫入門(FileIO類別)

Yún不僅可插micro SD記憶卡,用來儲存檔案,其實也可存放在Atheros AR9331它自己本身的Flash快閃記憶體空間,但只有幾MB而已。

作法是透過Bridge程式庫的FileIO類別,範例程式如下,每10秒讀取一次類比腳位A0的值,並且取得當時的日期時間,然後一併記錄在檔案裡。

#include <FileIO.h>

void setup() {
  Bridge.begin();
  Serial.begin(19200);
  FileSystem.begin(); // 初始化檔案系統

  while(!Serial)
    ;
}
void loop () {
  // 欲儲存的路徑檔名
  // 若想儲存在micro SD卡裡,路徑需改成/mnt/sda1
  // 若在micro SD卡裡建立目錄arduino,那麼可使用路徑/mnt/sd
  const char filenamepath[] = "/root/data.txt";
  String data; // 將要寫入檔案的資料
  data += getTimeStamp(); // 取得日期時間
  data += ", Analog Pin A0 = ";
  data += analogRead(A0); // 讀取A0的值

  // 根據檔案存在與否判斷開檔模式
  int mode = FileSystem.exists(filenamepath) ? FILE_APPEND : FILE_WRITE;
  File file = FileSystem.open("/root/data.txt", mode); // 開檔

  if(file){ // 確認開檔成功
    file.println(data); // 寫入資料
    file.close(); // 關檔
  }

  delay(10000); // 每10秒記錄一次,請注意不要用光儲存空間了
}
String getTimeStamp(){
  String result;
  Process p;
  p.begin("date");
  p.run();
  while(p.available() > 0) {
    char c = p.read();
    if(c != '\n')
      result += c;
  }

  return result;
}

檔案內容大概如下:
Sun Apr 13 14:00:19 CST 2014, Analog Pin A0 = 150
Sun Apr 13 14:00:29 CST 2014, Analog Pin A0 = 54
Sun Apr 13 14:00:39 CST 2014, Analog Pin A0 = 54
Sun Apr 13 14:00:49 CST 2014, Analog Pin A0 = 52
...

同時只能開啟一支檔案,所以必須先關掉前一支檔案,然後才能開啟另一支檔案。


參考資料:

44 comments:

  1. Anonymous11/8/14 14:07

    您好,我想請教一個問題~
    欲使Arduino R3 搭配 RJ45 Connector with Integrated,請問是可行的嗎?
    我在網路找了很多資源、但大家似乎都是此用 Arduino Yún 來做....

    這塊 http://helpdev.com.br/2013/07/07/arduino-criando-servico-web-com-ethernet-shield/

    ReplyDelete
    Replies
    1. Anonymous11/8/14 14:12

      我想藉由這樣做,連到NTP Server

      Delete
  2. 這是很一般的Arduino以太網路擴充板吧,沒問題啊。
    搜尋關鍵字「arduino ethernet shield」,就能找到很多文章了吧。

    因為Yún的話有WiFi啊,無線傳輸。

    ReplyDelete
  3. 請問SD卡裡的TXT檔,有沒有說檔案太大不能讀?還是說有限制一次可以讀多少容量的檔案?

    ReplyDelete
    Replies
    1. 不確定。照理說應該沒有。
      容量大小限制的話,跟檔案系統有關係而已才對。

      Delete
  4. 您好,您這是把資料存進flash嗎?這樣的話我該如何讀取flash上的東西?
    我理想狀況是想把資料放進PC(或某一台PC)中,請問有辦法可以直接做到嗎0.0

    ReplyDelete
    Replies
    1. 是。

      讀取flash上的東西? 這不就是此篇文章的內容嗎?

      那麼yun跟你的PC的連線是什麼?序列介面?藍牙?WiFi?
      PC端也要有支程式,兩端互相溝通吧。

      Delete
    2. 不好意思我剛接觸這類東西比較不熟。
      想說yun跟pc(無線網卡或筆電)在同一個wifi區域之下,能有方法將 txt 建在電腦中嗎?

      Delete
    3. 既然在同一wifi裡,那就可以透過網路傳輸檔案內容。
      pc端的程式,可以自己寫。
      或是執行已有的伺服器,譬如web server或ftp server之類的。

      Delete
  5. 葉大您好 有兩個關於arduino yun的問題 想請問您

    1.想請問在W5100上SD卡可以直接使用FILE_WRITE把資料直接接續著寫入,可是在yun上卻不行會直接重頭寫入,要使用FILE_APPEND才會接續,請問有甚麼差別嗎 我看官網寫的解釋如下都相同
    FILE_WRITE: open the file for reading and writing, starting at the end of the file.
    怎麼用起來不太一樣 是我哪邊有漏掉嗎?

    2.我想將arduino yun 上的SD卡內容讀出,顯示在串列埠視窗 結果會出現Connection closed by foreign host的訊息


    以下是程式碼,用SD卡的範例下去改的
    #include
    #include

    void setup() {
    Bridge.begin();
    Console.begin();
    FileSystem.begin();
    while(!Console);

    }

    void loop() {
    File dataFile = FileSystem.open(“/mnt/sda1/test.txt”, FILE_APPEND);
    if(dataFile){
    Console.println(“opening test.txt”);
    while(dataFile.available()) {
    Console.println(dataFile.read());
    }
    dataFile.close();
    }
    else{
    Console.println(“error opening test.txt”);
    }

    delay(1000);
    }

    ReplyDelete
    Replies
    1. 1. 文件的確如你所說。不過我看了一下原始碼,SD程式庫似乎(不是100%)會自行加上append的旗標設定。

      2. 嗯,該錯誤訊息是指yun被watchdog重置,於是該訊息通知你。
      你用FILE_APPEND,那麼檔案指標會指到結尾,這麼一來read不就讀不到任何東西了嗎?

      Delete
  6. This comment has been removed by the author.

    ReplyDelete
  7. 葉老師的文章總是讓我受益良多^^

    ReplyDelete
  8. 葉大 請問SD卡內一個TXT檔 內容例如1 2 3 4 5, 用數的加上空格我知道5是在第10個位子
    假如要怎麼用程式知道5的位子是在哪裡?有去官網看了一下 position好像能知道位子 但要怎麼使用能稍微提示一些範例嗎?看不太懂官網寫的

    另外在請問可以去刪除SD卡內TXT檔的內容嗎 有wirte read 那有delete可以用嗎? 還是要變成知道內容位子用空白蓋過去?

    ReplyDelete
    Replies
    1. txt檔裡是字串吧,讀取後放進String,就有許多方法可用,譬如charAt()、indexOf()等,請查詢https://www.arduino.cc/en/Reference/StringObject。

      沒有delete。
      可以write,以一個「空格字元」覆蓋掉原本的某個字元。

      Delete
    2. 不能直接在txt內做尋找的動作嗎? 我目前是要做個簡單的RFID門禁。
      我把卡號寫入SD卡內 然後想要一筆一筆來比對卡號驗證,每一筆最後會有個換行
      RFID卡號的長度不是固定的,所以我想找到換行的位子在哪,比對完第一筆我就可以
      以那一個換行位子+1繼續去讀下一筆出來比對,但是目前完全沒有頭緒去怎麼找這個換行的位子

      Delete
    3. > 直接在txt內做尋找的動作嗎?
      技術上做得到。但要有人寫好、寫出程式庫。
      一般Arduino的程式庫,跟其他語言、平台相比,都很陽春。

      ASCII文字檔的換行,通常是由0x0D 0x0A兩個字元代表,或是由0x0A代表。
      不斷用read讀取,讀取到這些字元時,就代表換行。

      Delete
  9. #include
    #include
    #include
    #include



    #define SS SS_PIN
    #define RST_PIN 9 // Configurable, see typical pin layout above
    #define SS_PIN 10 // Configurable, see typical pin layout above

    MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.

    void setup() {

    Bridge.begin();
    FileSystem.begin();
    SPI.begin(); // Init SPI bus
    mfrc522.PCD_Init(); // Init MFRC522 card
    pinMode(13, OUTPUT);
    }

    void loop() {

    if (!mfrc522.PICC_IsNewCardPresent())
    return;
    if (!mfrc522.PICC_ReadCardSerial())
    return;

    dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);


    Console.println("3");

    }


    void dump_byte_array(byte *buffer, byte bufferSize) {
    String result[128];
    String password;
    int a=0;
    int j=0;
    File dataFile = FileSystem.open("/mnt/sda1/test.txt", FILE_READ);
    dataFile.seek(a);
    for (byte i = 0; i < bufferSize; i++) {
    password +=buffer[i] ;
    }
    Console.println(password);
    for(int i=0;i<100;i++){
    char c=dataFile.read();
    if(c==' ')
    ;
    else if(c==',')
    j++;
    else if(c=='\n'){
    for(int i=0;i<128;i++){
    if( password==result[i])
    Console.println('1');
    else
    ; }
    }
    else
    result[j] +=c;
    }
    Console.println(result[0]);
    Console.println(result[1]);
    dataFile.close();

    }


    txt內容假設為
    123 45 678 900 ,
    21 21 212 212 ,

    我將txt的內容一個一個讀出第一筆就丟到陣列result,當讀到空格與逗號不做任何事讀到換行時就去比對
    結果第一個卡號可以PASS 第2個卡號卻不行
    有試著將卡號列出來也都正確 不知道為什麼無法PASS 是我有哪個部分沒有考慮到嗎?

    ReplyDelete
    Replies
    1. txt內容第二行,最後有換行嗎?

      Delete
    2. 恩恩 有的最後也是有換行,明天我再繼續試試看,或想別的方法,謝謝!

      Delete
    3. 確定你的txt檔的換行是0x0A '\n'嗎?還是由0x0D 0x0A兩個字元代表,'\r' '\n' ?

      若是後者,那麼第2個卡號就會不行 。

      Delete
    4. 感謝葉大 確實是這個\n 與\r\n 的問題!!

      Delete
  10. 請問葉大 假如txt內有123456 我想在34中間插入寫入有辦法嗎?
    該使用什麼指令?

    ReplyDelete
    Replies
    1. Arduino的核心程式庫很陽春,並沒有簡易的作法能達到你的需求。
      txt是文字檔吧,讀取整支檔案,放在String物件裡,找出想插入的位置,從該位置切開字串變成兩個子字串,假設為a與b,
      然後建立新字串,也就是a + 想插入的東西 + b。

      Delete
  11. 我測試完arduino yun的SD card datalogger功能
    發現他在openfile 跟寫入資料時會花掉很多時間(分別10ms跟37ms左右)
    請問有沒有辦法可以解決這個問題?謝謝

    ReplyDelete
    Replies
    1. 我目的是想作一個採樣頻率在1000~2000Hz的測量儀器

      Delete
    2. 假定採樣頻率是1000Hz,也就是每1ms要採樣一次,
      那麼,既然開檔與寫入動作所需時間都超過1ms,
      不就無解?

      Delete
    3. 以上所述,假定開檔與寫入動作是阻斷式行為(blocking)。

      Delete
    4. 你確定開檔與寫入動作要花那麼多時間嗎?
      或許可從程式著手,減少開檔的次數、減少寫入動作的次數,
      收集夠多的採樣數據,之後再開檔寫入。

      Delete
    5. This comment has been removed by the author.

      Delete
    6. 其實我的想法跟你一樣想減少寫入次數
      所以我就用MatrixMath 做了儲存空間
      並且儲存了一段資料在寫入
      不過這個做法讓寫入時間也變長了
      一次紀錄20個資料寫入時間變105ms

      Delete
    7. 結果後來去計算讀取data的時間
      讀取50筆Data只需要17ms
      照這樣算來yun理論上可以達到3000Hz的採樣頻率
      不過就是慢在寫入SD的部分
      所以想請問有沒有辦法讓寫入速度變快
      或是在通信的部分出了甚麼問題(因為我本身是讀機械系對通信部分不太了解
      謝謝!!

      Delete
    8. > 讀取50筆Data只需要17ms 達到3000Hz的採樣頻率
      讀取50筆Data只需要17ms,不代表讀取1筆只需要17/50 ms。

      SD的寫入速度比讀取速度慢很多。你需要的是寫入速度吧。

      換張SD試試看,但要注意板子是否支援,有些太高級太新的記憶卡,可能不支援。

      > 通信
      我無從得知是否有問題。



      Delete
    9. >讀取50筆Data只需要17ms,不代表讀取1筆只需要17/50 ms。
      恩,謝謝我懂了,這我後來測試過

      那想在最後請問板主一個問題
      如果改用RF無線射頻會不會解決對我的project比較有幫助?

      Delete
    10. 這跟RF無線射頻有什麼關係?

      Delete
    11. 我的意思是想用無線射頻傳輸數據回筆電

      Delete
    12. 確定無線射頻傳輸速度可符合你的需求嗎?

      直接從序列埠輸出如何?輸出到另一台電腦,由它負責儲存。
      請參考這篇http://www.dustynrobots.com/uncategorized/arduino-data-logging-and-speed-test/

      Delete
    13. 測量器想裝在人體上,所以需要無線操作。
      謝謝葉大!你的教學對我很有幫助!

      Delete
    14. 嗯,還是可以參考那篇文章,但是改成無線傳輸,測試可達到多快。

      Delete
  12. 葉大, 想請問一下, 可以在arduino上跑mkdosfs之類的format SD程式嗎???我找了uno上都沒有, 不知道yun上的Linino是可否可以達成這個功能?

    ReplyDelete
  13. > uno上都沒有
    很難吧,uno的記憶體那麼小,也不適合這種功能mkdosfs。

    > linino
    有mkdosfs

    ReplyDelete
  14. 有指令 可以得知 SD card 存滿了 ,換另一個新的SD card 繼續做存資料使用

    ReplyDelete
  15. 請問我將路徑改成const char filepath[] = "/mnt/sda1/data.txt"
    但是跳出'filenamepath' was not declared in this scope是哪裡有問題嗎

    ReplyDelete