2014年8月18日 星期一

Arduino 程式設計

讀者若對本章節程式結構不了解之處,請參閱Arduino 官方網站的Language Reference (http://arduino.cc/en/Reference/HomePage),或參閱相關書籍(Anderson & Cervo, 2013; Boxall, 2013; Faludi, 2010; Margolis, 2011, 2012; McRoberts, 2010; Minns, 2013; Monk, 2010, 2012; Oxer & Blemings, 2009; Warren, Adams, & Molle, 2011; Wilcher, 2012),相信會對Arduino 程式碼更加了解與熟悉。
程式結構
Ø          setup()
Ø          loop()
一個Arduino程式碼(Sketch)由兩部分組成
程式初始化
void setup()
在這個函式範圍內放置初始化Arduino開發板的程式 - 在重複執行的程式(loop())之前執行,主要功能是將所有Arduino 開發板的pin腳設定,元件設定,需要初始化的部分設定等等。
迴圈重複執行
void loop()
在此放置你的Arduino程式碼。這部份的程式會一直重複的被執行,直到Arduino 開發板被關閉。

C 語言是區塊式結構的程式語言, 所謂的區塊是一對大括號:『{}』所界定的範圍, 每一對大括號及其涵括的所有敘述構成 C 語法中所謂的複合敘述 (Compound Statement), 這樣子的複合敘述不但對於編譯器而言,構成一個有意義的文法單位, 對於程式設計者而言,一個區塊也應該要代表一個完整的程式邏輯單元, 內含的敘述應該具有相當的資料耦合性 (一個敘述處理過的資料會被後面的敘述拿來使用), 及控制耦合性 (CPU 處理完一個敘述後會接續處理另一個敘述指定的動作), 當看到程式中一個區塊時, 應該要可以假設其內所包含的敘述都是屬於某些相關功能的, 當然其內部所使用的資料應該都是完成該種功能所必需的, 這些資料應該是專屬於這個區塊內的敘述, 是這個區塊之外的敘述不需要的。
命名空間 (naming space)
C 語言中區塊定義了一塊所謂的命名空間 (naming space), 在每一個命名空間內,程式設計者可以對其內定義的變數任意取名字, 稱為區域變數 (local variable), 這些變數只有在該命名空間 (區塊) 內部可以進行存取, 到了該區塊之外程式就不能在藉由該名稱來存取了, 如下例中 int 型態的變數 z。 由於區塊是階層式的, 大區塊可以內含小區塊, 大區塊內的變數也可以在內含區塊內使用, 例如:
{
    int x, r;
    x=10;
    r=20;
    {
        int y, z;
        float r;
        y = x;
        x = 1;
        r = 10.5;
    }
    z = x; // 錯誤,不可使用變數 z
}
上面這個例子裡有兩個區塊, 也就有兩個命名空間, 有任一個命名空間中不可有兩個變數使用相同的名字, 不同的命名空間則可以取相同的名字, 例如變數 r, 因此針對某一個變數來說, 可以使用到這個變數的程式範圍就稱為這個變數的作用範圍 (scope)
變數的生命期 (Lifetime)
變數的生命始於定義之敘述而一直延續到定義該變數之區塊結束為止, 變數的作用範圍:意指程式在何處可以存取該變數, 有時變數是存在的,但是程式卻無法藉由其名稱來存取它, 例如, 上例中內層區塊內無法存取外層區塊所定義的變數 r, 因為在內層區塊中 r 這個名稱賦予另一個 float 型態的變數了。
縮小變數的作用範圍
利用 C 語言的區塊命名空間的設計, 程式設計者可以儘量把變數的作用範圍縮小, 如下例:
{
int tmp;
    for (tmp=0; tmp<1000; tmp++)
        doSomeThing();
}
{
    float tmp;
    tmp = y;
    y = x;
    x = y;
}
上面這個範例中前後兩個區塊中的 tmp 很明顯地沒有任何關係, 看這個程式的人不必擔心程式中有藉 tmp 變數傳遞資訊的任何意圖。
特殊符號
; (semicolon)
{} (curly braces)
// (single line comment)
/* */ (multi-line comment)
Arduino 語言用了一些符號描繪程式碼,例如註解和程式區塊。
; //(分號)
Arduino 語言每一行程序都是以分號為結尾。這樣的語法讓你可以自由地安排代碼,你可以將兩個指令放置在同一行,只要中間用分號隔開(但這樣做可能降低程式的可讀性)。
範例:
delay(100);

{}(大括號)
大括號用來將程式代碼分成一個又一個的區塊,如以下範例所示,在loop()函式的前、後,必須用大括號括起來。
範例:
void loop(){
     Serial.pritln("Hello !! Welcome to Arduino world");
}

程式的註解就是對代碼的解釋和說明,編寫註解有助於程式設計師(或其他人)了解代碼的功能。
Arduino處理器在對程式碼進行編譯時會忽略註解的部份。
Arduino 語言中的編寫註解有兩種方式
//單行註解:這整行的文字會被處理器忽略
/*多行註解:
      在這個範圍內你可以
      寫 一篇 小說
  */

程式中的變數與數學使用的變數相似,都是用某些符號或單字代替某些數值,從而得以方便計算過程。程式語言中的變數屬於識別字 (identifier) C 語言對於識別字有一定的命名規則,例如只能用英文大小寫字母、數字以及底線符號
其中,數字不能用作識別字的開頭,單一識別字裡不允許有空格,而如 int char C 語言的關鍵字 (keyword) 之一,屬於程式語言的語法保留字,因此也不能用為自行定義的名稱。通常編譯器至少能讀取名稱的前 31 個字元,但外部名稱可能只能保證前六個字元有效。
變數使用前要先進行宣告 (declaration) ,宣告的主要目的是告訴編譯器這個變數屬於哪一種資料型態,好讓編譯器預先替該變數保留足夠的記憶體空間。宣告的方式很簡單,就是型態名稱後面接空格,然後是變數的識別名稱
常數
Ø          HIGH | LOW
Ø          INPUT | OUTPUT
Ø          true | false
Ø          Integer Constants
資料型態
Ø          boolean
Ø          char
Ø          byte
Ø          int
Ø          unsigned int
Ø          long
Ø          unsigned long
Ø          float
Ø          double
Ø          string
Ø          array
Ø          void
常數
Arduino語言中事先定義了一些具特殊用途的保留字。HIGH LOW 用來表示你開啟或是關閉了一個Arduino的腳位(pin)INPUT OUTPUT 用來指示這個Arduino的腳位(pin)是屬於輸入或是輸出用途。true false 用來指示一個條件或表示式為真或是假。
變數
變數用來指定Arduino 記憶體中的一個位置,變數可以用來儲存資料,程式人員可以透過程式碼去不限次數的操作變數的值。
因為Arduino 是一個非常簡易的微處理器,但你要宣告一個變數時必須先定義他的資料型態,好讓微處理器知道準備多大的空間以儲存這個變數值。
Arduino 語言支援的資料型態:
布林boolean
布林變數的值只能為真(true)或是假(false)

字元char
單一字元例如 A,和一般的電腦做法一樣Arduino 將字元儲存成一個數字,即使你看到的明明就是一個文字。
用數字表示一個字元時,它的值有效範圍為 -128 127
PS:目前有兩種主流的電腦編碼系統ASCII UNICODE
l          ASCII 表示了127個字元, 用來在序列終端機和分時計算機之間傳輸文字。
l          UNICODE可表示的字量比較多,在現代電腦作業系統內它可以用來表示多國語言。
在位元數需求較少的資訊傳輸時,例如義大利文或英文這類由拉丁文,阿拉伯數字和一般常見符號構成的語言,ASCII仍是目前主要用來交換資訊的編碼法。
位元組byte
儲存的數值範圍為0255。如同字元一樣位元組型態的變數只需要用一個位元組(8位元)的記憶體空間儲存。
整數int
整數資料型態用到2位元組的記憶體空間,可表示的整數範圍為 –32,768 32,767; 整數變數是Arduino內最常用到的資料型態。
整數unsigned int
無號整數同樣利用2位元組的記憶體空間,無號意謂著它不能儲存負的數值,因此無號整數可表示的整數範圍為0 65,535
長整數long
長整數利用到的記憶體大小是整數的兩倍,因此它可表示的整數範圍從 –2,147,483,648 2,147,483,647
長整數unsigned long
無號長整數可表示的整數範圍為0 4,294,967,295
浮點數float
浮點數就是用來表達有小數點的數值,每個浮點數會用掉四位元組的RAM,注意晶片記憶體空間的限制,謹慎的使用浮點數。
雙精準度 浮點數double
雙精度浮點數可表達最大值為 1.7976931348623157 x 10308
字串string
字串用來表達文字信息,它是由多個ASCII字元組成(你可以透過序串埠發送一個文字資訊或者將之顯示在液晶顯示器上)。字串中的每一個字元都用一個組元組空間儲存,並且在字串的最尾端加上一個空字元以提示Ardunio處理器字串的結束。下面兩種宣告方式是相同的。
char word1 = "Arduino world"; // 7 字元 + 1 空字元
char word2 = "Arduino is a good developed kit"; // 與上行相同

陣列array
一串變數可以透過索引去直接取得。假如你想要儲存不同程度的LED亮度時,你可以宣告六個變數light01light02light03light04light05light06,但其實你有更好的選擇,例如宣告一個整數陣列變數如下:

int light = {0, 20, 40, 65, 80, 100};
"array" 這個字為沒有直接用在變數宣告,而是[]{}宣告陣列。
控制指令
string(字串)
範例
  char Str1[15];
  char Str2[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o'};
  char Str3[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o', '\0'};
  char Str4[ ] = "arduino";
  char Str5[8] = "arduino";
  char Str6[15] = "arduino";
解釋如下:
l          Str1中 聲明一個沒有初始化的字元陣列
l          Str2中 聲明一個字元陣列(包括一個附加字元),編譯器會自動添加所需的空字元
l          Str3中 明確加入空字元
l          Str4中 用引號分隔初始化的字串常數,編譯器將調整陣列的大小,以適應字串常量和終止空字元
l          Str5中 初始化一個包括明確的尺寸和字串常量的陣列
l          Str6中 初始化陣列,預留額外的空間用於一個較大的字串
空終止字元
一般來說,字串的結尾有一個空終止字元(ASCII代碼0), 以此讓功能函數(例如Serial.prinf())知道一個字串的結束, 否則,他們將從記憶體繼續讀取後續位元組,而這些並不屬於所需字串的一部分。
這表示你的字串比你想要的文字包含更多的個字元空間, 這就是為什麼Str2Str5需要八個字元,  即使“Arduino”只有七個字元 - 最後一個位置會自動填充空字元, str4將自動調整為八個字元,包括一個額外的 null, 在Str3的,我們自己已經明確地包含了空字元(寫入'\0')
使用符號:單引號?還是雙引號?
l          定義字串時使用雙引號(例如“ABC”)
l          定義一個單獨的字元時使用單引號(例如'A')
範例
字串測試範例(stringtest01)
char* myStrings[]={
  "This is string 1", "This is string 2", "This is string 3",
  "This is string 4", "This is string 5","This is string 6"};

void setup(){
  Serial.begin(9600);
}

void loop(){
  for (int i = 0; i < 6; i++){
    Serial.println(myStrings[i]);
    delay(500);
  }
}
char* 在字元資料類型char後跟了一個星號'*'表示這是一個指標陣列, 所有的陣列名稱實際上是指標,所以這需要一個陣列的陣列。
 指標對於C語言初學者而言是非常深奧的部分之一, 但是目前我們沒有必要瞭解詳細指標,就可以有效地應用它。

Ø          char()
Ø          byte()
Ø          int()
Ø          long()
Ø          float()
char()
指令用法
將資料轉程字元形態:
語法:char(x)
參數
x: 想要轉換資料的變數或內容
回傳
字元形態資料
unsigned char()
一個無符號資料類型佔用1個位元組的記憶體:byte的資料類型相同,無符號的char資料類型能編碼0255的數位,為了保持Arduino的程式設計風格的一致性,byte資料類型是首選。

指令用法
將資料轉程字元形態:
語法:unsigned char(x)
參數
x: 想要轉換資料的變數或內容
回傳
字元形態資料
unsigned char myChar = 240;

byte()
指令用法
將資料轉換位元資料形態:
語法:byte(x) 
參數
x: 想要轉換資料的變數或內容
回傳
位元資料形態的資料
int(x)
指令用法
將資料轉換整數資料形態:
語法:int(x) 
參數
x: 想要轉換資料的變數或內容
回傳
整數資料形態的資料
unsigned int(x)
unsigned int(無符號整數)與整型資料同樣大小,佔據2位元組: 它只能用於存儲正數而不能存儲負數,範圍 0~65,535 (2^16) - 1)
指令用法
將資料轉換整數資料形態:
語法:unsigned int(x) 
參數
x: 想要轉換資料的變數或內容
回傳
整數資料形態的資料
unsigned int ledPin = 13;

long()
指令用法
將資料轉換長整數資料形態:
語法:int(x) 
參數
x: 想要轉換資料的變數或內容
回傳
長整數資料形態的資料

unsigned long()
無符號長整型變數擴充了變數容量以存儲更大的資料, 它能存儲32位元(4位元組)資料:與標準長整型不同無符號長整型無法存儲負數, 其範圍從04,294,967,295 ( 2^32-1 )

指令用法
將資料轉換長整數資料形態:
語法:unsigned int(x) 
參數
x: 想要轉換資料的變數或內容
回傳
長整數資料形態的資料

unsigned long time;

void setup()
{
     Serial.begin(9600);
}

void loop()
{
  Serial.print("Time: ");
  time = millis();
  //程式開始後一直列印時間
  Serial.println(time);
  //等待一秒鐘,以免發送大量的資料
  delay(1000);
}

        float()
指令用法
將資料轉換浮點數資料形態:
語法:float(x) 
參數
x: 想要轉換資料的變數或內容
回傳
浮點數資料形態的資料

控制流程
if
if...else
for
switch case
while
do... while
break
continue
return

Ardunio 利用一些關鍵字控制程式碼的邏輯。
if … else
If必須緊接著一個問題表示式(expression),若這個表示式為真,緊連著表示式後的代碼就會被執行。若這個表示式為假,則執行緊接著else之後的代碼. 只使用 if不搭配else是被允許的。
範例:
#define LED 12
void setup()
{
  int val =1;
  if (val == 1) {
  digitalWrite(LED,HIGH);
}
}
void loop()
{
}
for
用來明定一段區域代碼重覆指行的次數。
範例:
void setup()
{
  for (int i = 1; i < 9; i++) {
    Serial.print("2 * ");
    Serial.print(i);
    Serial.print(" = ");
    Serial.print(2*i);

  }
}
void loop()
{
}
switch case
if 敘述是程式裡的分叉選擇,switch case 是更多選項的分叉選擇。swith case 根據變數值讓程式有更多的選擇,比起一串冗長的if敘述,使用swith case可使程式代碼看起來比較簡潔。
範例 :
void setup()
{
  int sensorValue;
    sensorValue = analogRead(1);
  switch (sensorValue) {

  case 10:
    digitalWrite(13,HIGH);
    break;

case 20:
  digitalWrite(12,HIGH);
  break;

default: // 以上條件都不符合時,預設執行的動作
    digitalWrite(12,LOW);
    digitalWrite(13,LOW);
}
}
void loop()
{
  }
while
while之後的條件成立時,執行括號內的程式碼。
範例 :
void setup()
{
  int sensorValue;
  // sensor值小於256,閃爍LED 1
  sensorValue = analogRead(1);
  while (sensorValue < 256) {
    digitalWrite(13,HIGH);
    delay(100);
    digitalWrite(13,HIGH);
    delay(100);
    sensorValue = analogRead(1);
  }
}
void loop()
{
  }
do … while
while 相似,不同的是while前的那段程式碼會先被執行一次,不管特定的條件式為真或為假。因此若有一段程式代碼至少需要被執行一次,就可以使用do…while架構。
範例 :
void setup()
{
  int sensorValue;
  do
  {
    digitalWrite(13,HIGH);
    delay(100);
    digitalWrite(13,HIGH);
    delay(100);
    sensorValue = analogRead(1);
  }
  while (sensorValue < 256);
}
void loop()
{
}
break
Break讓程式碼跳離迴圈,並繼續執行這個迴圈之後的程式碼。此外,在break也用於分隔switch case 不同的敘述。
範例 :
void setup()
{
}
void loop()
{
  int sensorValue;
  do {
    // 按下按鈕離開迴圈
    if (digitalRead(7) == HIGH)
          break;
         digitalWrite(13,HIGH);
         delay(100);
         digitalWrite(13,HIGH);
         delay(100);
         sensorValue = analogRead(1);
  }
  while (sensorValue < 512);
}
continue
continue 用於迴圈之內,它可以強制跳離接下來的程式,並直接執行下一個迴圈。
範例 :
#define PWMpin 12
#define Sensorpin 8
void setup()
{
}
void loop()
{
  int light;
  int x ;
  for (light = 0; light < 255; light++)
  {
     // 忽略數值介於 140 200之間
       x = analogRead(Sensorpin) ;
   
    if ((x > 140) && (x < 200))
      continue;

    analogWrite(PWMpin, light);
    delay(10);

  }
}
return
函式的結尾可以透過return回傳一個數值。
例如,有一個計算現在溫度的函式 computeTemperature(),你想要回傳現在的溫度給temperature變數,你可以這樣寫:
#define PWMpin 12
#define Sensorpin 8

void setup()
{
}
void loop()
{
  int light;
  int x ;
  for (light = 0; light < 255; light++)
  {
    // 忽略數值介於 140 200之間
    x = computeTemperature() ;
    if ((x > 140) && (x < 200))
        continue;

        analogWrite(PWMpin, light);
        delay(10);
  }
}
int computeTemperature() {

  int temperature = 0;
  temperature = (analogRead(Sensorpin) + 45) / 100;
      return temperature;
}

算術符號
=  (給值)
+  (加法)
-  (減法)
*  (乘法)
/  (除法)
%  (求餘數)
你可以透過特殊的語法用 Arduino 去做一些複雜的計算。 + 就是一般數學上的加減法,乘法用*示,而除法用 /表示。
另外餘數除法(%),用於計算整數除法的餘數值: 一個整數除以另一個數,其餘數稱為模數,它有助於保持一個變數在一個特定的範圍(例如陣列的大小)
語法:
        result = dividend % divisor
參數:
l          dividend:一個被除的數字
l          divisor:一個數字用於除以其他數

{}括號
你可以透過多層次的括弧去指定算術之間的循序。和數學函式不一樣,中括號和大括號在此被保留在不同的用途(分別為陣列索引,和宣告區域程式碼)
範例 :
#define PWMpin 12
#define Sensorpin 8

void setup()
{
      int sensorValue;
      int light;
      int remainder;

      sensorValue = analogRead(Sensorpin) ;
      light = ((12 * sensorValue) - 5 ) / 2;
      remainder = 3 % 2;

}
void loop()
{
}

比較運算
==  (等於)
!=  (不等於)
<  (小於)
>  (大於)
<=  (小於等於)
>=  (大於等於)

當你在指定if,while, for 敘述句時,可以運用下面這個運算符號:
符號
意義
範例
==
等於
a==1
!=
不等於
a!=1
< 
小於
a<1
> 
大於
a>1
<=
小於等於
a<=1
>=
大於等於
a>=1

布林運算

Ø          && (and)
Ø          || (or)
Ø          ! (not)
當你想要結合多個條件式時,可以使用布林運算符號。
例如你想要檢查從感測器傳回的數值是否於510,你可以這樣寫:
#define PWMpin 12
#define Sensorpin 8
void setup()
{
}
void loop()
{
  int light;
  int sensor ;
  for (light = 0; light < 255; light++)
  {
        // 忽略數值介於 140 200之間
         sensor = analogRead(Sensorpin) ;
   
  if ((sensor >= 5) && (sensor <=10))
       continue;

       analogWrite(PWMpin, light);
      delay(10);
  }
}

這裡有三個運算符號: 交集(and)&& 表示; 聯集(or)|| 表示; 反相(finally not)!表示。
複合運算符號:有一般特殊的運算符號可以使程式碼比較簡潔,例如累加運算符號。
例如將一個值加1,你可以這樣寫:
Int value = 10 ;
value = value + 1 ;
你也可以用一個復合運算符號累加(++)
Int value = 10 ;
value ++;

複合運算符號

Ø          ++ (increment)
Ø          -- (decrement)
Ø          += (compound addition)
Ø          -= (compound subtraction)
Ø          *= (compound multiplication)
Ø          /= (compound division)

累加和遞減 (++ --)
當你在累加1或遞減1到一個數值時。請小心i++++i之間的不同。如果你用的是 i++i會被累加並且i的值等於i+1;但當你使用++i 時,i的值等於i,直到這行指令被執行完時i再加1。同理應用於––
+= , –=, *= and /=
這些運算符號可讓表示式更精簡,下面二個表示式是等價的:
Int value = 10 ;
value  = value +5 ;    // (此兩者都是等價)
value  += 5 ;         // (此兩者都是等價)

數位訊號輸出/輸入
Ø          pinMode()
Ø          digitalWrite()
Ø          digitalRead()

類比訊號輸出/輸入
Ø          analogRead()
Ø          analogWrite() - PWM
Arduino 內含了一些處理輸出與輸入的切換功能,相信已經從書中程式範例略知一二。

pinMode(pin, mode)
將數位腳位(digital pin)指定為輸入或輸出。
範例
#define sensorPin 7
#define PWNPin 8
void setup()
{
pinMode(sensorPin,INPUT); // 將腳位 sensorPin (7) 定為輸入模式
}
void loop()
{
}

digitalWrite(pin, value)
將數位腳位指定為開或關。腳位必須先透過pinMode明示為輸入或輸出模式digitalWrite才能生效。
範例 :
#define PWNPin 8
#define sensorPin 7
void setup()
{
digitalWrite (PWNPin,OUTPUT); // 將腳位 PWNPin (8) 定為輸入模式
}
void loop()
{}

int digitalRead(pin)
將輸入腳位的值讀出,當感測到腳位處於高電位時時回傳HIGH,否則回傳LOW
範例 :
#define PWNPin 8
#define sensorPin 7
void setup()
{
   pinMode(sensorPin,INPUT); // 將腳位 sensorPin (7) 定為輸入模式
   val = digitalRead(7); // 讀出腳位 7 的值並指定給 val
}
void loop()
{
}

int analogRead(pin)
讀出類比腳位的電壓並回傳一個 01023 的數值表示相對應的05的電壓值。
範例 :
#define PWNPin 8
#define sensorPin 7
void setup()
{
   pinMode(sensorPin,INPUT); // 將腳位 sensorPin (7) 定為輸入模式
   val = analogRead (7); // 讀出腳位 7 的值並指定給 val
}
void loop()
{
}
analogWrite(pin, value)
改變PWM腳位的輸出電壓值,腳位通常會在35691011value變數範圍0-255,例如:輸出電壓2.5伏特(V),該值大約是128
範例 :
#define PWNPin 8
#define sensorPin 7
void setup()
{
analogWrite (PWNPin,OUTPUT); // 將腳位 PWNPin (8) 定為輸入模式
}
void loop()
{   }
進階 I/O
Ø          tone()
Ø          noTone()
Ø          shiftOut()
Ø          pulseIn()
tone(Pin)
使用Arduino開發板,使用一個Digital Pin(數位接腳)連接喇叭,請參考 4所示,將喇叭接在您想要的腳位,並參考 1所示,可以產生想要的音調。
範例 :
#include <Tone.h>

Tone tone1;

void setup()
{
  tone1.begin(13);
  tone1.play(NOTE_A4);
}

void loop()
{
}

1 Tone頻率表
常態變數
頻率(Frequency (Hz))
NOTE_B2
123
NOTE_C3
131
NOTE_CS3
139
NOTE_D3
147
NOTE_DS3
156
NOTE_E3
165
NOTE_F3
175
NOTE_FS3
185
NOTE_G3
196
NOTE_GS3
208
NOTE_A3
220
NOTE_AS3
233
NOTE_B3
247
NOTE_C4
262
NOTE_CS4
277
NOTE_D4
294
NOTE_DS4
311
NOTE_E4
330
NOTE_F4
349
NOTE_FS4
370
NOTE_G4
392
NOTE_GS4
415
NOTE_A4
440
NOTE_AS4
466
NOTE_B4
494
NOTE_C5
523
NOTE_CS5
554
NOTE_D5
587
NOTE_DS5
622
NOTE_E5
659
NOTE_F5
698
NOTE_FS5
740
NOTE_G5
784
NOTE_GS5
831
NOTE_A5
880
NOTE_AS5
932
NOTE_B5
988
NOTE_C6
1047
NOTE_CS6
1109
NOTE_D6
1175
NOTE_DS6
1245
NOTE_E6
1319
NOTE_F6
1397
NOTE_FS6
1480
NOTE_G6
1568
NOTE_GS6
1661
NOTE_A6
1760
NOTE_AS6
1865
NOTE_B6
1976
NOTE_C7
2093
NOTE_CS7
2217
NOTE_D7
2349
NOTE_DS7
2489
NOTE_E7
2637
NOTE_F7
2794
NOTE_FS7
2960
NOTE_G7
3136
NOTE_GS7
3322
NOTE_A7
3520
NOTE_AS7
3729
NOTE_B7
3951
NOTE_C8
4186
NOTE_CS8
4435
NOTE_D8
4699
NOTE_DS8
4978

2 Tone音階頻率對照表
音階
常態變數
頻率(Frequency (Hz))
低音Do
NOTE_C4
262
低音Re
NOTE_D4
294
低音Mi
NOTE_E4
330
低音Fa
NOTE_F4
349
低音So
NOTE_G4
392
低音La
NOTE_A4
440
低音Si
NOTE_B4
494
中音Do
NOTE_C5
523
中音Re
NOTE_D5
587
中音Mi
NOTE_E5
659
中音Fa
NOTE_F5
698
中音So
NOTE_G5
784
中音La
NOTE_A5
880
中音Si
NOTE_B5
988
高音Do
NOTE_C6
1047
高音Re
NOTE_D6
1175
高音Mi
NOTE_E6
1319
高音Fa
NOTE_F6
1397
高音So
NOTE_G6
1568
高音La
NOTE_A6
1760
高音Si
NOTE_B6
1976
高高音Do
NOTE_C7
2093


4 Tone接腳圖
5 Arduino 喇吧接線圖

Mario音樂範例 :
/*
  Arduino Mario Bros Tunes
  With Piezo Buzzer and PWM
  by: Dipto Pratyaksa
  last updated: 31/3/13
*/
#include <pitches.h>

#define melodyPin 3
//Mario main theme melody
int melody[] = {
  NOTE_E7, NOTE_E7, 0, NOTE_E7,
  0, NOTE_C7, NOTE_E7, 0,
  NOTE_G7, 0, 0,  0,
  NOTE_G6, 0, 0, 0,

  NOTE_C7, 0, 0, NOTE_G6,
  0, 0, NOTE_E6, 0,
  0, NOTE_A6, 0, NOTE_B6,
  0, NOTE_AS6, NOTE_A6, 0,

  NOTE_G6, NOTE_E7, NOTE_G7,
  NOTE_A7, 0, NOTE_F7, NOTE_G7,
  0, NOTE_E7, 0,NOTE_C7,
  NOTE_D7, NOTE_B6, 0, 0,

  NOTE_C7, 0, 0, NOTE_G6,
  0, 0, NOTE_E6, 0,
  0, NOTE_A6, 0, NOTE_B6,
  0, NOTE_AS6, NOTE_A6, 0,

  NOTE_G6, NOTE_E7, NOTE_G7,
  NOTE_A7, 0, NOTE_F7, NOTE_G7,
  0, NOTE_E7, 0,NOTE_C7,
  NOTE_D7, NOTE_B6, 0, 0
};
//Mario main them tempo
int tempo[] = {
  12, 12, 12, 12,
  12, 12, 12, 12,
  12, 12, 12, 12,
  12, 12, 12, 12,

  12, 12, 12, 12,
  12, 12, 12, 12,
  12, 12, 12, 12,
  12, 12, 12, 12,

  9, 9, 9,
  12, 12, 12, 12,
  12, 12, 12, 12,
  12, 12, 12, 12,

  12, 12, 12, 12,
  12, 12, 12, 12,
  12, 12, 12, 12,
  12, 12, 12, 12,

  9, 9, 9,
  12, 12, 12, 12,
  12, 12, 12, 12,
  12, 12, 12, 12,
};

//

//Underworld melody
int underworld_melody[] = {
  NOTE_C4, NOTE_C5, NOTE_A3, NOTE_A4,
  NOTE_AS3, NOTE_AS4, 0,
  0,
  NOTE_C4, NOTE_C5, NOTE_A3, NOTE_A4,
  NOTE_AS3, NOTE_AS4, 0,
  0,
  NOTE_F3, NOTE_F4, NOTE_D3, NOTE_D4,
  NOTE_DS3, NOTE_DS4, 0,
  0,
  NOTE_F3, NOTE_F4, NOTE_D3, NOTE_D4,
  NOTE_DS3, NOTE_DS4, 0,
  0, NOTE_DS4, NOTE_CS4, NOTE_D4,
  NOTE_CS4, NOTE_DS4,
  NOTE_DS4, NOTE_GS3,
  NOTE_G3, NOTE_CS4,
  NOTE_C4, NOTE_FS4,NOTE_F4, NOTE_E3, NOTE_AS4, NOTE_A4,
  NOTE_GS4, NOTE_DS4, NOTE_B3,
  NOTE_AS3, NOTE_A3, NOTE_GS3,
  0, 0, 0
};
//Underwolrd tempo
int underworld_tempo[] = {
  12, 12, 12, 12,
  12, 12, 6,
  3,
  12, 12, 12, 12,
  12, 12, 6,
  3,
  12, 12, 12, 12,
  12, 12, 6,
  3,
  12, 12, 12, 12,
  12, 12, 6,
  6, 18, 18, 18,
  6, 6,
  6, 6,
  6, 6,
  18, 18, 18,18, 18, 18,
  10, 10, 10,
  10, 10, 10,
  3, 3, 3
};

void setup(void)
{
   pinMode(3, OUTPUT);//buzzer
   pinMode(13, OUTPUT);//led indicator when singing a note

}
void loop()
{
//sing the tunes
  sing(1);
  sing(1);
  sing(2);
}
int song = 0;

void sing(int s){     
   // iterate over the notes of the melody:
   song = s;
   if(song==2){
     Serial.println(" 'Underworld Theme'");
     int size = sizeof(underworld_melody) / sizeof(int);
     for (int thisNote = 0; thisNote < size; thisNote++) {

       // to calculate the note duration, take one second
       // divided by the note type.
       //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
       int noteDuration = 1000/underworld_tempo[thisNote];

       buzz(melodyPin, underworld_melody[thisNote],noteDuration);

       // to distinguish the notes, set a minimum time between them.
       // the note's duration + 30% seems to work well:
       int pauseBetweenNotes = noteDuration * 1.30;
       delay(pauseBetweenNotes);

       // stop the tone playing:
       buzz(melodyPin, 0,noteDuration);

    }

   }else{

     Serial.println(" 'Mario Theme'");
     int size = sizeof(melody) / sizeof(int);
     for (int thisNote = 0; thisNote < size; thisNote++) {

       // to calculate the note duration, take one second
       // divided by the note type.
       //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
       int noteDuration = 1000/tempo[thisNote];

       buzz(melodyPin, melody[thisNote],noteDuration);

       // to distinguish the notes, set a minimum time between them.
       // the note's duration + 30% seems to work well:
       int pauseBetweenNotes = noteDuration * 1.30;
       delay(pauseBetweenNotes);

       // stop the tone playing:
       buzz(melodyPin, 0,noteDuration);

    }
  }
}

void buzz(int targetPin, long frequency, long length) {
  digitalWrite(13,HIGH);
  long delayValue = 1000000/frequency/2; // calculate the delay value between transitions
  //// 1 second's worth of microseconds, divided by the frequency, then split in half since
  //// there are two phases to each cycle
  long numCycles = frequency * length/ 1000; // calculate the number of cycles for proper timing
  //// multiply frequency, which is really cycles per second, by the number of seconds to
  //// get the total number of cycles to produce
  for (long i=0; i < numCycles; i++){ // for the calculated length of time...
    digitalWrite(targetPin,HIGH); // write the buzzer pin high to push out the diaphram
    delayMicroseconds(delayValue); // wait for the calculated delay value
    digitalWrite(targetPin,LOW); // write the buzzer pin low to pull back the diaphram
    delayMicroseconds(delayValue); // wait again or the calculated delay value
  }
  digitalWrite(13,LOW);

}

/*************************************************
  * Public Constants
  *************************************************/

#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978

shiftOut(dataPin, clockPin, bitOrder, value)
把資料傳給用來延伸數位輸出的暫存器,函式使用一個腳位表示資料、一個腳位表示時脈。bitOrder用來表示位元間移動的方式(LSBFIRST最低有效位元或是MSBFIRST最高有效位元),最後value會以byte形式輸出。此函式通常使用在延伸數位的輸出。
範例 :
#define dataPin 8
#define clockPin 7
void setup()
{
shiftOut(dataPin, clockPin, LSBFIRST, 255);
}
void loop()
{   }
unsigned long pulseIn(pin, value)
設定讀取腳位狀態的持續時間,例如使用紅外線、加速度感測器測得某一項數值時,在時間單位內不會改變狀態。
範例 :
#define dataPin 8
#define pulsein 7
void setup()
{
Int time ;
time = pulsein(pulsein,HIGH); // 設定腳位7的狀態在時間單位內保持為HIGH
}
void loop()
{   }

Ø          millis()
Ø          micros()
Ø          delay()
Ø          delayMicroseconds()
控制與計算晶片執行期間的時間

unsigned long millis()
回傳晶片開始執行到目前的毫秒
範例:
int  lastTime ,duration;
void setup()
{
  lastTime = millis() ;
}
void loop()
{
  duration = -lastTime; // 表示自"lastTime"至當下的時間
}

delay(ms)
暫停晶片執行多少毫秒
範例:
void setup()
{
  Serial.begin(9600);
}
void loop()
{
  Serial.print(millis()) ;
  delay(500); //暫停半秒(500毫秒)
}
「毫」是10的負3次方的意思,所以「毫秒」就是10的負3次方秒,也就是0.001秒,參考 3
3 常用單位轉換表
符號
中文
英文
符號意義
p
微微
pico
10的負12次方
n
nano
10的負9次方
u
micro
10的負6次方
m
milli
10的負3次方
K
kilo
103次方
M
百萬
mega
106次方
G
十億
giga
109次方
T
tera
1012次方

delay Microseconds(us)
暫停晶片執行多少微秒
範例:
    void setup()
    {
      Serial.begin(9600);
    }
    void loop()
    {
      Serial.print(millis()) ;
      delayMicroseconds (1000); //暫停半秒(500毫秒)
    } 

Ø          min()
Ø          max()
Ø          abs()
Ø          constrain()
Ø          map()
Ø          pow()
Ø          sqrt()
三角函式以及基本的數學運算
min(x, y)
回傳兩數之間較小者
範例:
#define sensorPin1 7
#define sensorPin2 8
void setup()
{
  int val;
   pinMode(sensorPin1,INPUT); // 將腳位 sensorPin1 (7) 定為輸入模式
   pinMode(sensorPin2,INPUT); // 將腳位 sensorPin2 (8) 定為輸入模式
    val = min(analogRead (sensorPin1), analogRead (sensorPin2)) ;
}
void loop()
{   }

max(x, y)
回傳兩數之間較大者
範例:
#define sensorPin1 7
#define sensorPin2 8
void setup()
{
  int val;
  pinMode(sensorPin1,INPUT); // 將腳位 sensorPin1 (7) 定為輸入模式
  pinMode(sensorPin2,INPUT); // 將腳位 sensorPin2 (8) 定為輸入模式
  val = max (analogRead (sensorPin1), analogRead (sensorPin2)) ;
}
void loop()
{   }

abs(x)
回傳該數的絕對值,可以將負數轉正數。
範例:
#define sensorPin1 7
void setup()
{
  int val;
   pinMode(sensorPin1,INPUT); // 將腳位 sensorPin (7) 定為輸入模式
     val = abs(analogRead (sensorPin1)-500);
       // 回傳讀值-500的絕對值
}
void loop()
{    }

constrain(x, a, b)
判斷x變數位於ab之間的狀態。x若小於a回傳a;介於ab之間回傳x本身;大於b回傳b
範例:
#define sensorPin1 7
#define sensorPin2 8
#define sensorPin 12
void setup()
{
  int val;
  pinMode(sensorPin1,INPUT); // 將腳位 sensorPin1 (7) 定為輸入模式
  pinMode(sensorPin2,INPUT); // 將腳位 sensorPin2 (8) 定為輸入模式
  pinMode(sensorPin,INPUT); // 將腳位 sensorPin (12) 定為輸入模式
  val = constrain(analogRead(sensorPin), analogRead (sensorPin1), analogRead (sensorPin2)) ;
  // 忽略大於255的數
}
void loop()
{
}

map(value, fromLow, fromHigh, toLow, toHigh)
value變數依照fromLowfromHigh範圍,對等轉換至toLowtoHigh範圍。時常使用於讀取類比訊號,轉換至程式所需要的範圍值。
例如:
#define sensorPin1 7
#define sensorPin2 8
#define sensorPin 12
void setup()
{
  int val;
  pinMode(sensorPin1,INPUT); // 將腳位 sensorPin1 (7) 定為輸入模式
  pinMode(sensorPin2,INPUT); // 將腳位 sensorPin2 (8) 定為輸入模式
  pinMode(sensorPin,INPUT); // 將腳位 sensorPin (12) 定為輸入模式
  val = map(analogRead(sensorPin), analogRead (sensorPin1), analogRead (sensorPin2),0,100) ;
 // analog0 所讀取到的訊號對等轉換至100 – 200之間的數值
}
void loop()
{    }

double pow(base, exponent)
回傳一個數(base)的指數(exponent)值。
範例:
int y=2;
double x = pow(y, 32); // 設定xy32次方

double sqrt(x)
回傳double型態的取平方根值。
範例:
int y=2123;
double x = sqrt (y);  // 回傳2123平方根的近似值

三角函式
Ø          sin()
Ø          cos()
Ø          tan()

double sin(rad)
回傳角度(radians)的三角函式sine值。
範例:
int y=45;
double sine = sin (y);  // 近似值 0.70710678118654

double cos(rad)
回傳角度(radians)的三角函式cosine值。
範例:
int y=45;
double cosine = cos (y);  // 近似值 0.70710678118654

double tan(rad)
回傳角度(radians)的三角函式tangent值。
範例:
int y=45;
double tangent = tan (y);  // 近似值 1

Ø          randomSeed()
Ø          random()

本函數是用來產生亂數用途:

randomSeed(seed)
事實上在Arduino裡的亂數是可以被預知的。所以如果需要一個真正的亂數,可以呼叫此函式重新設定產生亂數種子。你可以使用亂數當作亂數的種子,以確保數字以隨機的方式出現,通常會使用類比輸入當作亂數種子,藉此可以產生與環境有關的亂數。
範例:
#define sensorPin 7
void setup()
{
randomSeed(analogRead(sensorPin)); // 使用類比輸入當作亂數種子
}
void loop()
{
}

long random(min, max)
回傳指定區間的亂數型態為long。如果沒有指定最小值預設為0
範例:
#define sensorPin 7
long randNumber;
void setup(){
  Serial.begin(9600);
  // if analog input pin sensorPin(7) is unconnected, random analog
  // noise will cause the call to randomSeed() to generate
  // different seed numbers each time the sketch runs.
  // randomSeed() will then shuffle the random function.
  randomSeed(analogRead(sensorPin));
}
void loop() {
  // print a random number from 0 to 299
  randNumber = random(300);
  Serial.println(randNumber); 

  // print a random number from  0 to 100
  randNumber = random(0, 100);  // 回傳0 – 99 之間的數字
  Serial.println(randNumber);
  delay(50);
}

你可以在許多例子中,看見一些使用序列埠與電腦交換資訊的範例,以下是函式解釋。
Serial.begin(speed)
你可以指定Arduino從電腦交換資訊的速率,通常我們使用9600 bps。當然也可以使用其他的速度,但是通常不會超過115,200 bps(每秒位元組)。
範例:
void setup() {
  Serial.begin(9600);      // open the serial port at 9600 bps:   
}
void loop() { 
 }

Serial.print(data)
Serial.print(data, 格式字串(encoding))
經序列埠傳送資料,提供編碼方式的選項。如果沒有指定,預設以一般文字傳送。
範例:
int x = 0;    // variable

void setup() {
  Serial.begin(9600);      // open the serial port at 9600 bps:   
}

void loop() { 
  // print labels
  Serial.print("NO FORMAT");       // prints a label
  Serial.print("\t");              // prints a tab
  Serial.print("DEC"); 
  Serial.print("\t");     
  Serial.print("HEX");
  Serial.print("\t");  
  Serial.print("OCT");
  Serial.print("\t");
  Serial.print("BIN");
  Serial.print("\t");
}

Serial.println(data)
Serial.println(data, ,格式字串(encoding))
Serial.print()相同,但會在資料尾端加上換行字元( )。意思如同你在鍵盤上打了一些資料後按下Enter
範例:
int x = 0;    // variable
void setup() {
  Serial.begin(9600);      // open the serial port at 9600 bps:   
}
void loop() { 
  // print labels
  Serial.print("NO FORMAT");       // prints a label
  Serial.print("\t");              // prints a tab
  Serial.print("DEC"); 
  Serial.print("\t");     
  Serial.print("HEX");
  Serial.print("\t");  
  Serial.print("OCT");
  Serial.print("\t");
  Serial.print("BIN");
  Serial.print("\t");

  for(x=0; x< 64; x++){    // only part of the ASCII chart, change to suit
    // print it out in many formats:
    Serial.print(x);       // print as an ASCII-encoded decimal - same as "DEC"
    Serial.print("\t");    // prints a tab
    Serial.print(x, DEC);  // print as an ASCII-encoded decimal
    Serial.print("\t");    // prints a tab
    Serial.print(x, HEX);  // print as an ASCII-encoded hexadecimal
    Serial.print("\t");    // prints a tab
    Serial.print(x, OCT);  // print as an ASCII-encoded octal
    Serial.print("\t");    // prints a tab
    Serial.println(x, BIN);  // print as an ASCII-encoded binary
    //             then adds the carriage return with "println"
    delay(200);            // delay 200 milliseconds
  }
  Serial.println("");      // prints another carriage return
}

格式字串(encoding)
Arduinoprint()println(),在列印內容時,可以指定列印內容使用哪一種格式列印,若不指定,則以原有內容列印。
列印格式如下:
1.          BIN(二進位,或以2為基數)
2.          OCT(八進制,或以8為基數)
3.          DEC(十進位,或以10為基數)
4.          HEX(十六進位,或以16為基數)
使用範例如下:
l          Serial.print(78,BIN)輸出為“1001110”
l          Serial.print(78,OCT)輸出為“116”
l          Serial.print(78,DEC)輸出為“78”
l          Serial.print(78,HEX)輸出為“4E”

對於浮點型數位,可以指定輸出的小數數位。例如
l          Serial.println(1.23456,0)輸出為“1”
l          Serial.println(1.23456,2)輸出為“1.23”
l          Serial.println(1.23456,4)輸出為“1.2346”

Print & Println 列印格式(printformat01)
/*
使用for迴圈列印一個數字的各種格式。
*/
int x = 0;    // 定義一個變數並賦值

void setup() {
  Serial.begin(9600);      // 打開串口傳輸,並設置串列傳輸速率為9600
}

void loop() {
  ///列印標籤
  Serial.print("NO FORMAT");       // 列印一個標籤
  Serial.print("\t");              // 列印一個轉義字元

  Serial.print("DEC"); 
  Serial.print("\t");     

  Serial.print("HEX");
  Serial.print("\t");  

  Serial.print("OCT");
  Serial.print("\t");

  Serial.print("BIN");
  Serial.print("\t");

  for(x=0; x< 64; x++){    // 列印ASCII碼表的一部分, 修改它的格式得到需要的內容

    //  列印多種格式
    Serial.print(x);       // 以十進位格式將x列印輸出 - "DEC"相同
    Serial.print("\t");    // 橫向跳格

    Serial.print(x, DEC);  // 以十進位格式將x列印輸出
    Serial.print("\t");    // 橫向跳格

    Serial.print(x, HEX);  // 以十六進位格式列印輸出
    Serial.print("\t");    // 橫向跳格

    Serial.print(x, OCT);  // 以八進制格式列印輸出
    Serial.print("\t");    // 橫向跳格

    Serial.println(x, BIN);  // 以二進位格式列印輸出
    //                             然後用 "println"列印一個回車
    delay(200);            // 延時200ms
  }
  Serial.println("");      // 列印一個空字元並自動換行
}

int Serial.available()
回傳有多少位元組(bytes)的資料尚未被read()函式讀取,如果回傳值是0代表所有序列埠上資料都已經被read()函式讀取。
範例:
int incomingByte = 0;   // for incoming serial data
 void setup() {
         Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
 }
 void loop() {
         // send data only when you receive data:
         if (Serial.available() > 0) {
                 // read the incoming byte:
                 incomingByte = Serial.read();
                 // say what you got:
                 Serial.print("I received: ");
                 Serial.println(incomingByte, DEC);
         }
 }

int Serial.read()
byte方式讀取1byte的序列資料
範例:
int incomingByte = 0;   // for incoming serial data
void setup() {
  Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
}
void loop() {
  // send data only when you receive data:
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();
    // say what you got:
    Serial.print("I received: ");
    Serial.println(incomingByte, DEC);
  }
}

int Serial.write()
byte方式寫入資料到序列
範例:
void setup(){
  Serial.begin(9600);
}
void loop(){
  Serial.write(45); // send a byte with the value 45
    int bytesSent = Serial.write("hello Arduino , I am a beginner in the Arduino world");
}

Serial.flush()
有時候因為資料速度太快,超過程式處理資料的速度,你可以使用此函式清除緩衝區內的資料。經過此函式可以確保緩衝區(buffer)內的資料都是最新的。
範例:
void setup(){
  Serial.begin(9600);
}
void loop(){
  Serial.write(45); // send a byte with the value 45
    int bytesSent = Serial.write("hello Arduino , I am a beginner in the Arduino world");
  Serial.flush();
}


沒有留言:

張貼留言