2016/01/19

Arduino IDE 1.5:程式庫規格書

翻譯自官方文件:Arduino IDE 1.5: Library specification · arduino/Arduino Wiki


Arduino IDE 1.5.x(以上)的第三方程式庫規格書。

  • rev.1,從1.5.3開始實作。(已被rev.2取代)
  • rev.2,從1.5.6開始實作此規格。
制定新程式庫格式的用意是為了搭配自動化的程式庫管理員Library Manager),自1.6.2版開始啟用。有了程式庫管理員,使用者就能夠根據專案需求,透過操作簡易的圖形化介面,自動下載並安裝所需程式庫;目前尚未能處理程式庫之間相依性,關於程式庫管理員的進一步資訊,請參閱此處說明文件

Arduino 1.5.x支援多種微控制器架構(諸如AVR、SAM、等等),意思是說,程式庫可能需要能在多種架構上運作,新版的1.5.x程式庫格式,並未對跨架構程式庫提供特殊支援,但提供了前置處理機制,供程式庫判斷,可為不同架構提供不同的程式碼。

其他參考文件:


1.5版程式庫格式(rev.2)

程式庫後設資料(metadata)

新格式最重要的地方是透過名為library.properties這支屬性檔案,描述程式庫本身的資訊。

有了這支檔案,將來的程式庫管理員就能夠以簡易且自動的方式,搜尋並安裝相依的程式庫。

library.properties檔案格式

library.properties檔案含有「鍵=值」的屬性列表,此檔內每一欄位都採用UTF-8文字編碼,可用欄位如下:

name:程式庫的名稱。
version:程式庫的版本,版本編號應相容於semver,1.2.0是正確的,1.2可接受,但r5、003、1.1c則為無效的版本編號。
author:作者的姓名或暱稱,以及電子郵件地址(非必要),以逗號「,」隔開。
maintainer:維護者的姓名與電子郵件地址。
sentence:以一句話檢視這套程式庫的用途。
paragraph:用更長的句子描述說明。一定會加上sentence的句子,所以你應該從第二個句子開始撰寫。
category:若有此欄位,請從Display、Communication、Signal Input/Output、Sensors、Device Control、Timing、Data Storage、Data Processing、Other之中選一個。
url:程式庫專案的網址,供他人參訪。可以是github或其他類似頁面。
architectures:這套程式庫支援的架構清單,以逗號「,」隔開。若程式庫不含有特定於某架構的程式庫,請使用「*」代表所有架構。

範例:
name=WebServer
version=1.0
author=Cristian Maglie <c.maglie@example.com>, Pippo Pluto <pippo@example.com>
maintainer=Cristian Maglie <c.maglie@example.com>
sentence=A library that makes coding a Webserver a breeze.
paragraph=Supports HTTP1.1 and you can do GET and POST.
category=Communication
url=http://example.com/
architectures=avr

資料夾與檔案的位置規劃

Arduino程式庫由數個資料夾組合而成,每個資料夾皆有其特定用途(原始碼、範例、文件、等等),將來改版時可能會加入底下未提及的新資料夾。

原始碼

只相容於1.5.x版的程式庫,把原始碼檔案放在資料夾src裡,例如:

Servo/src/Servo.h 
Servo/src/Servo.cpp

資料夾src與它的所有子資料夾裡的原始碼,都會被編譯並跟使用者的草稿碼連結在一起,只有資料夾src被加入搜尋路徑清單裡(編譯草稿碼與程式庫之時)。當使用者匯入程式庫到他的草稿碼(透過選單「工具」-「匯入程式庫」),src資料夾裡的所有.h標頭檔(但不包括子資料夾),都會加上#include前置處理指令放進草稿碼,因此,這些標頭檔可算是你的程式庫的對外介面,一般來說,放在src資料夾內的標頭檔,會是你想要對外公開的東西,並且計畫在程式庫未來版本維持相容性。程式庫內部自己使用得標頭檔,應放在src的子資料夾裡。

若想向後相容於Arduino 1.0.x,程式庫作者可選擇不要把原始碼放進src資料夾內,此時應採用1.0版程式庫格式的資料夾規劃,放在根資料夾以及utility子資料夾裡,例如:

Servo/Servo.h 
Servo/Servo.cpp 
Servo/utility/ServoTimers.h 
Servo/utility/ServoTimers.cpp

This will allow existing 1.0.x libraries to compile under 1.5.x as well and vice-versa. If a library only needs to run on 1.5.x, we recommend placing all source code in the src/ folder. If a library requires recursive compilation of nested source folders, its code must be in the src/ folder (since 1.0.x doesn’t support recursive compilation, backwards compatibility wouldn’t be possible anyway).

這麼一來,1.0.x程式庫便可在1.5.x之下進行編譯,反之亦然。若程式庫只需相容於1.5.x,我們建議把所有原始碼放進src資料夾,若程式庫需要巢狀原始碼資料夾的遞迴式編譯,那麼原始碼必須放在src資料夾內(因為1.0.x不支援遞迴式編譯,所以無法維持向後相容性)。

程式庫範例

程式庫的範例程式,應放在examples資料夾裡,此資料夾的名稱必須一字不差,全部都是小寫字母。

Servo/examples/...

範例資料夾內的草稿碼,會顯示在IDE選單「範例」裡。

額外文件

開發人員可在extra資料夾內,放入說明文件或其他想跟程式庫綁在一起的東西。請記得,此資料夾裡的東西將會增加程式庫的大小,因此若放進僅含數KB內容的20MB PDF檔,大概不會是個好主意。

IDE完全忽視extra資料夾的內容,你可以放入任何東西,諸如其他說明文件等等。

關鍵字

在名為keywords.txt的檔案裡,放入應特別顯示的關鍵字清單,此檔案的格式與1.0版相同。

Servo/keywords.txt

完整示範

根據上述規格,底下是名為Servo的假想程式庫:

Servo/ 
Servo/library.properties 
Servo/keywords.txt 
Servo/src/ 
Servo/src/Servo.h 
Servo/src/Servo.cpp
Servo/src/ServoTimers.h 
Servo/examples/ 
Servo/examples/Sweep/Sweep.ino 
Servo/examples/Pot/Pot.ino 
Servo/extras/ 
Servo/extras/Servo_Connectors.pdf

多重架構

In 1.5.x, libraries placed in the user’s sketchbook folder (in the libraries/ subfolder) will be made available for all boards, which may include multiple different processor architectures. To provide architecture-specific code or optimizations, library authors can use the ARDUINO_ARCH_XXX preprocessor macro (#define), where XXX is the name of the architecture (as determined by the name of the folder containing it), e.g. ARDUINO_ARCH_AVR will be defined when compiling for AVR-based boards. For example,

在1.5.x版裡,放在草稿碼簿資料夾裡的程式庫(位於libraries子資料夾內),可供所有板子使用,可能包含不同處理器架構的特定程式碼;若想撰寫特定架構的程式碼或最佳化,程式庫作者可以使用前置處理器的巨集ARDUINO_ARCH_XXX(#define)來判斷,其中XXX是架構的名稱(根據包含該架構的資料夾之名稱),譬如在為AVR板子進行編譯時,將會定義ARDUINO_ARCH_AVR,寫法如下:

#if defined(ARDUINO_ARCH_AVR)
  // AVR特定程式碼
#elif defined(ARDUINO_ARCH_SAM)
  // SAM特定程式碼
#else
  // 泛用、非特定平台的程式碼
#endif

Alternatively, if a library only works on certain architectures, you can provide an explicit error message (instead of allowing the compilation to fail in an difficult to understand way):
或者,若你的程式庫只能用於某些特定架構,可提供明確的錯誤訊息(而不是直接讓編譯失敗,難以了解到底哪裡出錯):

#if defined(ARDUINO_ARCH_AVR)
  // AVR特定程式碼
#elif defined(ARDUINO_ARCH_SAM)
  // SAM特定程式碼
#else
  #error "This library only supports boards with an AVR or SAM processor."
#endif

舊程式庫格式(1.5版之前)

為了支援舊版格式的程式庫(自Arduino 1.0.x版以來),Arduino 1.5.x也能編譯缺少後設資料檔library.properties的程式庫,使用時,這些舊程式庫應如同在Arduino 1.0.x的樣子;可用於所有板子,包括非AVR架構的板子(不會出現於1.0.x版)。

31 comments:

  1. 小弟是個Arduino初學者,
    想請問一下葉難大大,
    要如何在序列埠中顯示中文呢?

    --
    Serial.print("濕度: ");
    Serial.print("溫度: ");
    --
    我這兩段在序列埠中顯示的是亂碼,
    但其餘都是正常的。

    感謝葉難大大 ><

    ReplyDelete
    Replies
    1. 據我所知,Arduino IDE應該會把你的程式檔案儲存成UTF-8文字編碼格式,但在繁體中文版的Windows上,Serial Monitor應該會使用Big-5,因此亂碼。

      你可以把程式碼改成
      Serial.print("\xC0\xE3\xAB\xD7: ");
      Serial.println("\xB7\xC5\xAB\xD7: ");
      輸出Big5碼。

      或是維持原狀,但Windows端改用Tera Term,但要修改文字編碼的設定。

      http://imgur.com/a/JfXh8

      Delete
    2. 感謝大大幫忙,我剛照著更改,
      但輸出完則變成偶爾亂碼偶爾中文,
      如下:
      濕度: 51.00%
      �贖�:
      27.00*C 80.60*F
      濕度: 51.00%
      溫度:
      27.00*C 80.60*F
      �舕�: 51.00%
      溫度:
      27.00*C 80.60*F
      �舕�: 51.00%
      溫度:
      27.00*C 80.60*F
      �舕�: 51.00%
      溫度:
      27.00*C 80.60*F

      不知哪裡出了問題?

      Delete
    3. 沒辦法,序列埠一個byte一個byte傳送。
      有時序列埠監控視窗收到一個中文字的一半(一個byte)時,就試著顯示出來,於是出現�。

      Delete
    4. 好的 我了解了
      還是非常感謝大大的幫忙 ^^

      Delete
  2. 請教葉難大大,我網購了一個PM2.5感測器(SHARP GP2Y1051AU0F),想搭配LinkitOne+WiFi來使用。目前利用廠商給的範例檔案來測試,但是出現一個問題,就是SoftwareSerial.h 這個函式庫。我的最新版Arduino1.6.7並無這個函式庫,我要如何去新增呢?網路上我找了好久也沒找到,網路上查找的到嗎?還是我必須自己設計或建立一個函式庫?勞煩您解惑,感恩!

    ReplyDelete
    Replies
    1. > SoftwareSerial.h 這個函式庫。我的最新版Arduino1.6.7並無這個函式庫
      有的,在hardware\arduino\avr\libraries\SoftwareSerial
      已轉成AVR架構的程式庫。

      > 網路上我找了好久也沒找到
      你開玩笑吧,搜尋「arduino softwareserial」不就有了,
      https://www.arduino.cc/en/Reference/SoftwareSerial
      https://www.pjrc.com/teensy/td_libs_SoftwareSerial.html

      需要在LinkitOne上使用serial port的話:

      LinkIt ONE has two serial ports.

      One is USB serial port, in code you use Serial to control it.

      Another is hardware serial port which is pin D0(RX) and D1(TX).
      In code you use Serial1 to control it.

      Delete
  3. 你好,
    我剛學arduino , 有買了你的來看, 想組一台實驗機台,
    用直流馬達控制滑台,左右來回運動,並用紅外線開關做左右的極限.

    用UNO主板,控制L298N的馬達驅動器,外加兩個紅外線開關
    寫的程式如下, 可是用手碰觸紅外線開關,開關有訊號,可是馬達卻沒有回轉
    請問是哪裡出問題了.

    onst int motorIn1 =5;
    const int motorIn2 =6;
    const int IRRight =8;
    const int IRLeft =9;
    boolean Right;
    boolean Left;
    boolean Go;
    const int i=250;
    //
    void setup()
    {
    pinMode(motorIn1, OUTPUT);
    pinMode(motorIn2, OUTPUT);
    pinMode(IRRight,INPUT);
    pinMode(IRLeft,INPUT);
    }

    void loop()
    {
    boolean Left = digitalRead(9);
    boolean Right = digitalRead(8);
    Left == 1;
    Go = 1;
    Right == 1;
    Go = 0;

    if (Go == 1)
    {
    analogWrite(motorIn1, i); //馬達正轉
    analogWrite(motorIn2, 0);
    }
    else
    {
    analogWrite(motorIn1, 0); //馬達反轉
    analogWrite(motorIn2, i);
    }
    }

    ReplyDelete
    Replies
    1. 底下這部份,你寫錯了吧?
      Left == 1;
      Go = 1;
      Right == 1;
      Go = 0;



      在執行if之前,Go的值一定會是0,
      所以一定會執行馬達反轉那部分的程式碼。

      Delete
    2. 謝謝你. 請問,如果它先執行GO值是0的反轉, 此時如果觸動紅外線開關,讓boolean Right = digitalRead(8) 執行的話, 應該也會變成正轉吧?不知道為何馬達就只轉一個方向,對紅外線開關好像沒反應....

      Delete
    3. 根據你的程式碼,loop()前半段結束後,Go一定是0,
      所以後半段一定會執行
      analogWrite(motorIn1, 0); //馬達反轉
      analogWrite(motorIn2, i);

      Delete
    4. 請詳細描述想要的行為。

      Delete
    5. 你的意思是說,後半段一定會執行,但是前半段,不一定會執行嗎?..
      我有畫一張結構的示意圖,可是這邊好像不能貼圖,可以用mail寄嗎?我的mail是idfcat.tw@gmail.com
      多謝

      Delete
    6. Left == 1;
      Go = 1;
      Right == 1;
      //不管前面是什麼,
      //執行底下這行後,Go就變成0了,
      Go = 0;


      if (Go == 1) //所以Go == 1一定不成立,
      {
      analogWrite(motorIn1, i); //馬達正轉
      analogWrite(motorIn2, 0);
      }
      else // 於是一定會執行這個else的部份
      {
      analogWrite(motorIn1, 0); //馬達反轉
      analogWrite(motorIn2, i);
      }

      > 這邊好像不能貼圖
      貼到別的地方,再把網址貼到這裡。

      Delete
    7. http://photo.pchome.com.tw/idfcat/145628185525
      這是我機台上的滑台示意圖, 麻煩您了,謝謝

      Delete
    8. 你想要什麼行為,請詳細描述。

      試試看吧,沒測試過。
      http://pastebin.com/3X20uxhX

      Delete
  4. byte a[];
    int i ;
    void setup() {
    Serial.begin(9600);
    }
    void loop() {
    if(Serial.available() ){
    for(i=1;i<3;i++){
    a[i]=Serial.read();
    delay(500);
    }
    }
    }

    請問一下,這邊是哪邊出了問題呢
    麻煩老師了~~~

    ReplyDelete
    Replies
    1. error: storage size of 'a' isn't known
      編譯器不知道a的大小。
      你要指定元素個數。

      Delete
  5. Anonymous7/7/16 13:20

    我想問說arduino版本不同,會造成library有些不能讀取嗎?因為我很多library是網路抓的,抓完之後都複製直接貼到C槽內的libray裡面,但是有些這樣貼可以有些這樣貼我再引用#include <....h>的時候就沒有讀到

    ReplyDelete
    Replies
    1. > 版本
      只要位置放對,目錄名正確,應該都能#include。
      至於能不能用,那就看該程式庫是否支援你的Arduino版本。
      通常都可以,但或有例外。

      > 貼到C槽內的libray裡面
      Arduino程式庫有規定的存放位置,以及目錄結構。
      應放在sketchbook裡的libraries子目錄裡。
      其目錄名稱,應和裡頭的主程式碼(.cpp .h)的主檔名相同。
      (但新版Arduino,可以不同。)

      Delete
    2. Anonymous7/7/16 14:08

      我的sketchbook裡面子目錄是在D槽的myuser/document/arduino裡面,但是我剛剛看D槽的library是空的,因為我之前都直接貼到C槽內,後來我把我把那個讀不出來的放到D槽子目錄裡可是還是沒有讀到

      Delete
    3. 放進 "arduino安裝目錄"/libraries 應該也可以,但這裡應該放內建程式庫。

      myuser/document/arduino裡頭應該有libraries目錄,若無自行建立,然後把程式庫放進去。

      Delete
  6. 你好 我要用arduino做nfc相關東西 為什麼arduino不能執行nfc的東西 會跑出燒入超時和函式庫找不道

    ReplyDelete
    Replies
    1. 可以,為何不行?

      你自己都說出問題所在了:
      燒入超時和函式庫找不到

      Delete
  7. 這錯誤訊息該怎麼解決 麻煩你了

    avrdude: stk500v2_ReceiveMessage(): timeout
    avrdude: stk500v2_getsync(): timeout communicating with programmer
    上傳草稿碼時發生錯誤
    在C:\arduino-1.6.10-windows\arduino-1.6.10\libraries\nfc_moudle_v1.1: C:\arduino-1.6.10-windows\arduino-1.6.10\libraries\nfc_moudle_v1.1裡找到無效的程式庫
    在C:\arduino-1.6.10-windows\arduino-1.6.10\libraries\nfc_moudle_v1.1: C:\arduino-1.6.10-windows\arduino-1.6.10\libraries\nfc_moudle_v1.1裡找到無效的程式庫

    ReplyDelete
  8. > avrdude: stk500v2...
    原因太多了,請參考
    http://yehnan.blogspot.tw/2014/10/arduinostk500getsync-not-in-sync.html

    > 找到無效的程式庫
    代表Arduino看不懂你的程式庫nfc_moudle_v1.1

    ReplyDelete
    Replies
    1. Anonymous13/9/16 14:56

      我也有無效程式庫的問題
      可是我的資料夾裡就只有一個"sketch_sep13a.ino"的檔案
      這是我的程式檔
      依然無解QQ

      Delete
    2. 什麼?
      程式檔在哪?
      無解什麼?

      Delete
  9. 你好想請問一下
    /**
    * This example demonstrates pushing a NDEF message from Arduino + NFC Shield to Android 4.0+ device
    *
    * This demo does not support UNO, because UNO board has only one HardwareSerial.
    * Do not try to use SoftwareSerial to control PN532, it won't work.
    * SotfwareSerial is not fast and stable enough.
    *
    * This demo only supports the Arduino board which has at least 2 Serial,
    * Like Leonard(1 USB serial and 1 Hardware serial), Mega ect.
    *
    * Make sure your PN532 board is in HSU(High Speed Uart) mode.
    *
    * This demo is tested with Leonard.
    */


    #include
    #include
    #include

    #include

    /** Use hardware serial to control PN532 */
    // PN532 Arduino
    // VCC --> 5V
    // GND --> GND
    // RXD --> Serial1-TX
    // TXD --> Serail1-RX
    /** Serial1 can be */
    PN532 nfc(Serial);
    NFCLinkLayer linkLayer(&nfc);
    SNEP snep(&linkLayer);


    // NDEF messages
    #define MAX_PKT_HEADER_SIZE 50
    #define MAX_PKT_PAYLOAD_SIZE 100
    uint8_t txNDEFMessage[MAX_PKT_HEADER_SIZE + MAX_PKT_PAYLOAD_SIZE];
    uint8_t *txNDEFMessagePtr;
    uint8_t txLen;

    void setup(void) {
    Serial.begin(115200);
    while(!Serial);
    Serial.println(F("----------------- nfc ndef push url --------------------"));


    txNDEFMessagePtr = &txNDEFMessage[MAX_PKT_HEADER_SIZE];
    NdefMessage message = NdefMessage();
    message.addUriRecord("http://elechouse.com");
    txLen = message.getEncodedSize();
    if (txLen <= MAX_PKT_PAYLOAD_SIZE) {
    message.encode(txNDEFMessagePtr);
    }
    else {
    Serial.println("Tx Buffer is too small.");
    while (1) {
    }
    }

    nfc.initializeReader();

    uint32_t versiondata = nfc.getFirmwareVersion();
    if (! versiondata) {
    Serial.print("Didn't find PN53x board");
    while (1); // halt
    }
    // Got ok data, print it out!
    Serial.print("Found chip PN5");
    Serial.println((versiondata>>24) & 0xFF, HEX);
    Serial.print("Firmware ver. ");
    Serial.print((versiondata>>16) & 0xFF, DEC);
    Serial.print('.');
    Serial.println((versiondata>>8) & 0xFF, DEC);
    Serial.print("Supports ");
    Serial.println(versiondata & 0xFF, HEX);

    nfc.SAMConfig();
    }

    void loop(void)
    {
    Serial.println();
    Serial.println(F("---------------- LOOP ----------------------"));
    Serial.println();

    uint32_t txResult = GEN_ERROR;

    if (IS_ERROR(nfc.configurePeerAsTarget(SNEP_SERVER))) {
    extern uint8_t pn532_packetbuffer[];

    Serial.println(F("\nSNEP Sever:Blocking wait response."));
    nfc.readspicommand(PN532_TGINITASTARGET, (PN532_CMD_RESPONSE *)pn532_packetbuffer, 0);
    }

    txResult = snep.pushPayload(txNDEFMessagePtr, txLen);
    Serial.print(F("Result: 0x"));
    Serial.println(txResult, HEX);
    if(txResult == 0x00000001){
    delay(3000);
    }
    }

    編譯為什麼找不到資料庫 還是程式有錯誤

    ReplyDelete
    Replies
    1. 找不到程式庫,代表你程式庫的檔案沒放好。

      Delete
    2. 你貼的程式碼是範例,不是程式庫。

      Delete