2019年7月1日 星期一

如何自製arduino的函式庫

動機是來自Otto。

因為自己組合了有手的Otto,但是APP卻都是只有腳的部分,所以想說自己編寫APP看看,發現自己要重複編寫那些動作很麻煩,而library的函式庫就是在處理這種重複功能用的,就想來研究看看。

在看過幾本書幾篇文章之後,嗯,完全看不懂,果然代誌不是憨人想得那麼簡單,於是又來記錄自己的學習過程。

我是先參考底下這一篇。

https://chtseng.wordpress.com/2017/05/31/%E5%A6%82%E4%BD%95%E7%B7%A8%E5%AF%ABarduino-library/

釐清自己對於library的觀念就是會不斷使用的程式碼寫成函式,就可以只呼叫函式名稱來使用,不用再重複整段程式碼。

所以,首先就是確認自己要改成library的程式碼可以用,再以既定的格式改編。

依照格式來說要準備三個檔案,
函式名稱.h                   (這是定義函式功能的變數、參數,稱為header file)
函式名稱.cpp               (這是C++編譯使用的檔案,執行的動作放這邊,稱為source file)
keywords.txt                 (這是在編寫程式有寫到關鍵字時,要變色,檔案可有可無)

這時候發現一開始看不懂的問題就是,不知道該把什麼放到哪裡,因為作者都很熟了,所以文章的內容就很順理成章的把程式碼都已經分類到兩個檔案去了,但是我卻完全搞不清楚為什麼要這樣分類。

就走一步算一步了。

在上面連結有一個例子,說明如何將閃爍的功能寫成library。
這時候已經稍有概念了,但是還似懂非懂,看了幾篇還是有疑問,最後想說找arduino官網的說明看看,底下是說明摩斯密碼的例子,看起來比較基礎的樣子,我先紀錄這個功能試試看之後,再回頭看其他的。

https://www.arduino.cc/en/Hacking/LibraryTutorial

摩斯密碼適用亮光的時間長跟短來組合出文字,底下就是長跟短的程式碼

void dot()
{
  digitalWrite(pin, HIGH);
  delay(250);
  digitalWrite(pin, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(pin, HIGH);
  delay(1000);
  digitalWrite(pin, LOW);
  delay(250);
}

再根據要幾個長跟幾個短來呼叫就可以

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

完整的程式碼

int pin = 13;

void setup()
{
  pinMode(pin, OUTPUT);
}

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

void dot()
{
  digitalWrite(pin, HIGH);
  delay(250);
  digitalWrite(pin, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(pin, HIGH);
  delay(1000);
  digitalWrite(pin, LOW);
  delay(250);
}

確認這段程式碼的動作沒有問題,接下來就是開始拆解。
以功能來說,就是長dash跟短dot,還有一個變數,就是腳位。

我們先在header file定義功能跟變數,使用的是class,private是呼叫Morse時才能使用, public則是整段程式碼都可以用,基本上,盡量使用private,減少公用記憶體的用量

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

不過,我們自己定義的功能根本上來說,是使用arduino的基本功能,所以我們需要把arduino的功能放進去。

#include "Arduino.h"

最後是檔案頭尾要放上宣告header file用的字眼。

#ifndef Morse_h
#define Morse_h

// the #include statment and code go here...

#endif


所以完整的header file會是
#ifndef Morse_h
#define Morse_h

#include "Arduino.h"

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

#endif

接著是source file,先把變數的作用寫出來,就是指定腳位並設定為output。

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

接著是長跟短的時間
void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

有一個雙冒號的記號::,那是指附屬功能,比如void Morse::dot()就是指附屬在Morse的dot功能,用這個就可以描述變數跟功能。

其實,我還有一個地方不懂,既然腳位已經用pin變數來指定了,為什麼還要再加一個_pin,而且還設定_pin=pin,用兩個變數來代表同一個意思。

雖然source file不需要頭尾宣告,但是,還是要把需要的功能寫進來,

#include "Arduino.h"
#include "Morse.h"

因此,完整的source file的內容就會是

#include "Arduino.h"
#include "Morse.h"

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

有了完整的header file 跟source file,就可以引用來寫程式了。
#include <Morse.h>

Morse morse(13);

void setup()
{
}

void loop()
{
  morse.dot(); morse.dot(); morse.dot();
  morse.dash(); morse.dash(); morse.dash();
  morse.dot(); morse.dot(); morse.dot();
  delay(3000);
}


看來要會拆解程式,才能夠學好library,上面的例子是給了一個變數,腳位用的,
我找到一個例子,是給三個變數的,如果可以看得懂,應該可以說入門了吧。
http://yehnan.blogspot.com/2013/01/arduino.html