บทความตัวอย่างการควบคุม RC Servo Motor ด้วย Arduino

บทความ “ตัวอย่างการควบคุม RC Servo Motor ด้วย Arduino”

01

RC Servo Motor รุ่นที่มีจำหน่าย:

 EFDV245 Futaba S3003 Servo Motor

EFDV242 Tower Pro Micro Servo Motor SG90

          Servo เป็นคำศัพท์ที่ใช้กันทั่วไปในระบบควบคุมอัตโนมัติ มาจากภาษาละตินคำว่า Sevus หมายถึง “ทาส” (Slave) ในเชิงความหมายของ Servo Motor ก็คือ Motor ที่เราสามารถสั่งงานหรือตั้งค่า แล้วตัว Motor จะหมุนไปยังตำแหน่งองศาที่เราสั่งได้เองอย่างถูกต้อง โดยใช้การควบคุมแบบป้อนกลับ (Feedback Control) ในบทความนี้จะกล่าวถึง RC Servo Motor ซึ่งนิยมนำมาใช้ในเครื่องเล่นที่บังคับด้วยคลื่นวิทยุ (RC = Radio – Controlled) เช่น เรือบังคับวิทยุ รถบังคับวิทยุ เฮลิคอปเตอร์บังคับวิทยุ เป็นต้น

          Feedback Control คือ ระบบควบคุมที่มีการวัดค่าเอาต์พุตของระบบนำมาเปรียบเทียบกับค่าอินพุตเพื่อควบคุมและปรับแต่งให้ค่าเอาต์พุตของระบบให้มีค่า เท่ากับ หรือ ใกล้เคียงกับค่าอินพุต 
ส่วนประกอบภายนอก RC Servo Motor

02

– Case ตัวถัง หรือ กรอบของตัว Servo Motor

– Mounting Tab ส่วนจับยึดตัว Servo กับชิ้นงาน
– Output Shaft เพลาส่งกำลัง
– Servo Horns ส่วนเชื่อมต่อกับ Output shaft เพื่อสร้างกลไกล
– Cable สายเชื่อมต่อเพื่อ จ่ายไฟฟ้า และ ควบคุม Servo Motor จะประกอบด้วยสายไฟ 3 เส้น และ ใน RC Servo Motor จะมีสีของสายแตกต่างกันไปดังนี้ 
         o สายสีแดง คือ ไฟเลี้ยง (4.8-6V)
         o สายสีดำ หรือ น้ำตาล คือ กราวด์
         o สายสีเหลือง (ส้ม ขาว หรือฟ้า) คือ สายส่งสัญญาณพัลซ์ควบคุม (3-5V) 
– Connector จุดเชื่อมต่อสายไฟ 
ส่วนประกอบภายใน RC Servo Motor

Ref: www.pololu.com

1. Motor เป็นส่วนของตัวมอเตอร์
2. Gear Train หรือ Gearbox เป็นชุดเกียร์ทดแรง
3. Position Sensor เป็นเซ็นเซอร์ตรวจจับตำแหน่งเพื่อหาค่าองศาในการหมุน
4. Electronic Control System เป็นส่วนที่ควบคุมและประมวลผล 
Servo Motor Block Diagram

04

หลักการทำงานของ RC Servo Motor 
             เมื่อจ่ายสัญญาณพัลซ์เข้ามายัง RC Servo Motor ส่วนวงจรควบคุม (Electronic Control System) ภายใน Servo จะทำการอ่านและประมวลผลค่าความกว้างของสัญญาณพัลซ์ที่ส่งเข้ามาเพื่อแปลค่าเป็นตำแหน่งองศาที่ต้องการให้ Motor หมุนเคลื่อนที่ไปยังตำแหน่งนั้น แล้วส่งคำสั่งไปทำการควบคุมให้ Motor หมุนไปยังตำแหน่งที่ต้องการ โดยมี Position Sensor เป็นตัวเซ็นเซอร์คอยวัดค่ามุมที่ Motor กำลังหมุน เป็น Feedback กลับมาให้วงจรควบคุมเปรียบเทียบกับค่าอินพุตเพื่อควบคุมให้ได้ตำแหน่งที่ต้องการอย่างถูกต้องแม่นยำ
สัญญาณ RC ในรูปแบบ PWM
            ตัว RC Servo Motor ออกแบบมาใช้สำหรับรับคำสั่งจาก Remote Control ที่ใช้ควบคุมของเล่นด้วยสัญญาณวิทยุต่างๆ เช่น เครื่องบินบังคับ รถบังบังคับ เรือบังคับ เป็นต้น ซึ่ง Remote จำพวกนี้ที่ภาครับจะแปลงความถี่วิทยุออกมาในรูปแบบสัญญาณ PWM (Pulse Width Modulation)

05

 มุมหรือองศาจะขึ้นอยู่กับความกว้างของสัญญาณพัลซ์ ซึ่งโดยส่วนมากความกว้างของพัลซ์ที่ใช้ใน RC Servo Motor จะอยู่ในช่วง 1-2 ms หรือ 0.5-2.5 ms 

           ยกตัวอย่างเช่นหากกำหนดความกว้างของสัญญาณพัลซ์ไว้ที่ 1 ms ตัว Servo Motor จะหมุนไปทางด้ายซ้ายจนสุด ในทางกลับกันหากกำหนดความกว้างของสัญญาณพัลซ์ไว้ที่ 2 ms ตัว Servo Motor จะหมุนไปยังตำแหน่งขวาสุด แต่หากกำหนดความกว้างของสัญญาณพัลซ์ไว้ที่ 1.5 ms ตัว Servo Motor ก็จะหมุนมาอยู่ที่ตำแหน่งตรงกลางพอดี

06

 ดังนั้นสามารถกำหนดองศาการหมุนของ RC Servo Motor ได้โดยการเทียบค่า เช่น RC Servo Motor สามารถหมุนได้ 180 องศา โดยที่ 0 องศาใช้ความกว้างพัลซ์เท่ากับ 1000 us ที่ 180 องศาความกว้างพัลซ์เท่ากับ 2000 us เพราะฉะนั้นค่าที่เปลี่ยนไป 1 องศาจะใช้ความกว้างพัลซ์ต่างกัน (2000-1000)/180 เท่ากับ 5.55 us
           จากการหาค่าความกว้างพัลซ์ที่มุม 1 องศาข้างต้น หากต้องกำหนดให้ RC Servo Motor หมุนไปที่มุม 45 องศาจะหาค่าพัลซ์ที่ต้องการได้จาก 5.55 x 45 เท่ากับ 249.75 us แต่ที่มุม 0 องศาเราเริ่มที่ความกว้างพัลซ์ 1ms หรือ 1000 us เพราะฉะนั้นความกว้างพัลซ์ที่ใช้กำหนดให้ RC Servo Motor หมุนไปที่ 45 องศา คือ 1000 + 249.75 เท่ากับประมาณ 1250 us

07

วิธีควบคุม RC Servo Motor ด้วย Arduino
            Arduino มีไลบรารี่สำหรับสั่งงาน RC Servo Motor มาให้ใช้งานอยู่แล้วเป็นฟังก์ชั่นสำเร็จรูปและใช้งานได้ง่าย ในหน้าเว็บไซต์ http://arduino.cc/en/reference/servo ได้ให้ข้อมูลไว้ว่า Servo Library ของ Arduino สามารถสั่งงาน RC Servo Motor ได้ทั้งแบบหมุนไป-กลับได้ 0-180 องศา (ที่กล่าวถึงตามตัวอย่างข้างต้น) และแบบต่อเนื่องที่หมุนครบรอบได้เรียกว่าเป็น Continuous Rotation Servo (ซึ่งในช่วงท้ายบทความจะกล่าวถึงเพิ่มเติม) โดยสามารถรองรับการเชื่อมต่อ RC Servo Motor ได้ถึง 12 ตัวกับบอร์ด Arduino UNO และรองรับสูงสุดถึง 48 ตัวหากใช้บอร์ด Arduino Mega 
ฟังก์ชั่นภายใน Servo Library
– attach()
– write()
– writeMicroseconds()
– read()
– attached()
– detach()
attach()
Description
          คือฟังก์ชั่นที่ใช้ในการกำหนดขาสัญญาณที่ Servo Motor ต่อกับ Arduino และกำหนดความกว้างของพัลซ์ที่ 0 องศาและ 180 องศา 
Syntax
          Servo.attach(pin)
          Servo.attach(pin,min,max)
Parameters
          Pin: คือ ขาสัญญาณของ Arduino ที่ใช้เชื่อมต่อกับ Servo Motor
          Min: คือ ความกว้างของพัลซ์ที่ 0 องศาของ Servo ตัวที่ใช้ในหน่วยไมโครวินาที (us) โดยปกติแล้วหากไม่มีการตั้งค่าโปรแกรมจะกำหนดค่าไว้ที่ 544 us
          Max: คือ ความกว้างของพัลซ์ที่ 180 องศาของ Servo ตัวที่ใช้ในหน่วยไมโครวินาที (us) โดยปกติแล้วหากไม่มีการตั้งค่าโปรแกรมจะกำหนดค่าไว้ที่ 2400 us
Write()
Description
          คือฟังก์ชั่นที่ใช้ควบคุมตำแหน่งที่ต้องการให้ Servo Motor หมุนไปยังองศาที่กำหนดสามารถกำหนดเป็นค่าองศาได้เลย คือ 0-180 องศา แต่ใน Servo Motor ที่เป็น Full Rotation คำสั่ง write จะเป็นการกำหนดความเร็วในการหมุน โดย 
          ค่าเท่ากับ 90 คือคำสั่งให้ Servo Motor หยุดหมุน
          ค่าเท่ากับ 0 คือการหมุนด้วยความเร็วสูงสุดในทิศทางหนึ่ง
          ค่าเท่ากับ 180 คือการหมุนด้วยความเร็วสูงสุดในทิศทางตรงกันข้าม
Syntax
          servo.write(angle)
Parameters
          Angle: คือมุมที่ต้องการให้ RC Servo Motor แบบ 0-180 องศาหมุนไป แต่หากเป็น RC Servo Motor แบบ Full Rotation ค่า Angle คือ การกำหนดความเร็วและทิศทางในการหมุน
writeMicroseconds()
Description
          คือฟังก์ชั่นที่ใช้ควบคุมตำแหน่งที่ให้ Servo Motor หมุนไปยังตำแหน่งองศาที่กำหนดโดยกำหนดเป็นค่าความกว้างของพัลซ์ในหน่วย us ซึ่งปกติแล้ว RC Servo Motor จะใช้ความกว้างของพัลซ์อยู่ที่ 1000-2000 us ตามที่ได้กล่าวไปข้างต้นแล้ว แต่ RC Servo Motor บางรุ่นหรือบางยี่ห้อไม่ได้ใช้ ช่วงความกว้างของพัลซ์ตามที่ได้กล่าวเอาไว้นี้ อาจจะใช้ช่วง 700-2300 แทนก็สามารถใช้ฟังก์ชั่น writeMicroseconds นี้เพื่อกำหนดความกว้างพัลซ์ได้เอง
          การใช้ฟังก์ชั่น writeMicroseconds สามารถกำหนดค่าได้อิสระ ตรงนี้ ”ต้องระวังในการใช้งาน” หากสั่งงาน RC Servo Motor (แบบ 0 – 180 องศา) จนหมุนไปเกินจุดสิ้นสุดคือเกินทั้งฝั่ง 0 หรือ 180 องศา จะทำให้เกิดเสียงครางดังจากการหมุนไปต่อไม่ได้และมอเตอร์จะกินกระแสสูงขึ้นด้วยในเวลาเดียวกันนั้น ซึ่งอาจทำให้ RC Servo Motor เกิดความเสียหายได้
Syntax
          servo.writeMicroseconds(uS)
Parameters
          uS: คือค่าความกว้างของพัลซ์ที่ต้องการกำหนดในหน่วยไมโครวินาที (โดยตัวแปร int)
read()
Description
          คือฟังก์ชั่นอ่านค่าองศาที่สั่งเข้าไปด้วยฟังก์ชั่น write() เพื่อให้รู้ว่าตำแหน่งองศาสุดท้ายที่เราสั่งเข้าไปนั้นมีค่าเท่าไหร่ซึ่งค่าที่อ่านออกมานั้นจะมีค่าอยู่ในช่วง 0 – 180 
Syntax
          servo.read()
Parameters
          ไม่มี: จะ Return ค่า 0-180
attached()
Description
           คือฟังก์ชั่นตรวจสอบว่า Servo ที่เราต้องการใช้กำลังต่ออยู่กับขสัญญาณของ Arduino หรือไม่
Syntax
           servo.attached()
Parameters
           ไม่มี: จะ Return ค่า True ออกมา หาก Servo Motor เชื่อมต่ออยู่กับ Arduino แต่ถ้าหาก Return ออกมาเป็นค่าอื่นถือว่าไม่เชื่อมต่อ
detach()
Description
           คือฟังก์ชั่นคืนสถานะของขาที่เรากำหนดให้เป็นขาควบคุม Servo Motor ด้วยคำสั่ง attached() ให้กลับคือสู่การใช่งานปกติ
Syntax
           servo.detach()
Parameters
           ไม่มี

ตัวอย่างการเชื่อมต่อ RC Servo Motor เข้ากับบอร์ด Arduino

08

อุปกรณ์ RC Servo Motor ที่ใช้ในบทความ :  EFDV245 Futaba S3003 Servo Motor

โค้ดตัวอย่างการควบคุมตำแหน่ง RC Servo Motor?

#include <Servo.h> 
Servo myservo;   
void setup() 
{ 
  myservo.attach(9); 
} 
void loop() 
{       myservo.write(0); 
        delay(1000);      
        myservo.write(90); 
        delay(1000);      
        myservo.write(180); 
        delay(1000);                     
}

ผลการทำงานของโค้ด

09

myservo.write(0); 
delay(1000);
Servo Motor จะหมุนไปที่ตำแหน่ง 0 องศา และ หยุดเป็นเวลา 1 วินาที 

10

myservo.write(90); 
delay(1000);
Servo Motor จะหมุนไปที่ตำแหน่ง 90 องศา และ หยุดเป็นเวลา 1 วินาที

11

myservo.write(90); 
delay(1000);
Servo Motor จะหมุนไปที่ตำแหน่ง 180 องศา และ หยุดเป็นเวลา 1 วินาที
จากนั้นจะหมุนกลับไปที่ตำแหน่ง 0 องศา และวนรอบไปเช่นนี้เรื่อยๆ
โค้ดตัวอย่างการควบคุมตำแหน่ง RC Servo Motor แบบ Sweep?

#include <Servo.h>
Servo myservo;        //create servo object to control a servo
                              // a maximum of eight servo objects can be created
int pos = 0;             // variable to store the servo position
void setup(){
       myservo.attach(9); // attaches the servo on pin 9 to the servo object
}
 
void loop(){
       for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees
      {
            myservo.write(pos); // tell servo to go to position in variable 'pos'
            delay(15); // waits 15ms for the servo to reach the position
       }
       for(pos = 180; pos>=1; pos-=1)// goes from 180 degrees to 0 degrees
      {
            myservo.write(pos); // tell servo to go to position in variable 'pos'
           delay(15); // waits 15ms for the servo to reach the position
      }
}

ผลการทำงานของโค้ด

for(pos = 0; pos < 180; pos += 1){
myservo.write(pos);
delay(15);
}

            ลูป for กำหนดให้ค่า pos มีค่าเท่ากับ 0 และทุกๆ การทำงานคำสั่งภายใน for loop ค่า pos จะเพิ่มค่าขึ้น 1 ค่า จนจนถึง 180 ก็จะหลุดออกจาก loop

           ภายใน loop for คำสั่ง myservo.write(pos); ก็คือการกำหนดให้ Servo Motor หมุนไปยังตำแหน่ง มุมตามค่าในตัวแปร pos และหน่วงเวลา 15ms ด้วยคำสั่ง delay(15); ดังนั้น Servo Motor จะค่อยๆ หมุนอย่างช้าๆ จากตำแหน่ง 0 องศาไปที่ 180 องศา

for(pos = 180; pos>=1; pos-=1) 

myservo.write(pos);
delay(15); 
}

           ใน loop for ที่สองนี้จะทำงานเช่นเดียวกับใน loop for แรกเพียงแต่เปลี่ยนค่าเริ่มต้นจาก 180 เป็น 0 และลดลงค่าลง 1 ค่าทุกๆ การทำงาน 1 รอบ ส่งผลให้ Servo Motor จะหมุนจากตำแหน่งมุม 180 องศา ไปยังมุม 0 องศาอย่างช้าๆ 

           !!! ทดลองเพิ่มค่าในคำสั่ง delay() ให้มากขึ้นจะพบว่า Servo Motor จะหมุนช้าลงและในทางกลับกันหากลดค่าใน delay() ลงจะพบว่า Servo Motor จะหมุนเร็วขึ้น

ตัวอย่างการควบคุมตำแหน่ง RC Servo Motor โดยใช้ Potentiometer

13

โค้ดตัวอย่างการควบคุมตำแหน่ง RC Servo Motor โดยใช้ Potentiometer?

#include <Servo.h>
Servo myservo; // create servo object to control a servo
int potpin = 0; // analog pin used to connect the potentiometer
int val; // variable to read the value from the analog pin
 
  
 
void setup(){
         myservo.attach(9); // attaches the servo on pin 9 to the servo object
}
void loop(){
         val = analogRead(potpin); // reads the value of the potentiometer (value // between 0 and 1023)
         val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value
                                                        // between 0 and 180)
         myservo.write(val); // sets the servo position according to the
                                      // scaled value
         delay(15); // waits for the servo to get there
}

ผลการทำงานของโค้ด

val = analogRead(potpin); 
           อ่านค่า Analog จาก Potentiometer ที่ต่ออยู่ที่ขา A0 เก็บไว้ในตัวแปร val
val = map(val, 0, 1023, 0, 179);
           เนื่องจาก ADC ภายใน Arduino เป็น ADC ขนาด 10-bit จึงอ่านค่า Analog ได้ตั้งแต่ 0 – 1023 แต่ RC Servo Motor สามารถหมุนได้เพียงแค่ 1-180 องศา จึงต้องใช้ Function map เพื่อทำการสเกลค่าลงจาก 0-1023 เป็น 0-179 แล้วนำไปเก็บไว้ในตัวแปร val 
myservo.write(val);
           เมื่อสเกลค่า จาก 0-1023 ลงเหลือ 0-179 แล้วก็นำมาสั่งให้ Servo Motor หมุนไปยังตำแหน่งในค่าตัวแปร val
delay(15); 
           หน่วงเวลา 15 ms
ผลของการทำงานทำให้สามารถปรับตำแหน่งองศาของ Servo Motor ได้โดยการหมุนปรับค่า Potentiometer

Continuous Rotation Servo

14

 Continuous Rotation Servo คือ RC Servo Motor แบบที่สามารถหมุนได้ 360 องศา ส่วนประกอบภายนอกนั้นจะมีหน้าตาคล้ายกับ RC Servo Motor แบบที่หมุนได้ 180 องศา เพียงแต่จะมี Potentiometer เพื่อใช้สำหรับปรับ ตำแหน่ง Center Stop Adjust ของตัว Servo 
           ลักษณะการใช้งาน RC Servo Motor ชนิดนี้จะแตกต่างจากการใช้งาน RC Servo Motor แบบ 180 องศาตรงที่ Servo ชนิดนี้จะใช้ความกว้างของสัญญาณพัลซ์ในการกำหนดความเร็วและทิศทางในการหมุน ไม่ได้ใช้เพื่อกำหนดมุมจึงไม่สามารถกำหนดให้ Motor หมุนไปยังตำแหน่งมุมต่างๆ ตามความต้องการได้ สัญญาณความกว้างของพัลซ์ที่ใช้ควบคุมจะอยู่ในช่วง 1000-2000 us แต่จะมีความแตกในความหมายของแต่ละความกว้างของพัลซ์ดังนี้
           ความกว้าง 1000 us หมายถึงการหมุนไปทางซ้ายด้วยความเร็วสูงสุดที่ Servo Motor จะหมุนได้ 

15

ความกว้าง 1500 us หมายถึงการสั่งให้ Servo Motor หยุดหมุน

16

  ความกว้าง 2000 us หมายถึงการหมุนไปทางขวาด้วยความเร็วสูงสุดที่ Servo Motor จะหมุนได้

17

การ Calibrate Center Stop
            ในการใช้งาน Continuous Rotation Servo เมื่อซื้อมาใหม่หรือใช้งานไปสักระยะหนึ่งจุด Center Stop อาจมีการคลาดเคลื่อนได้ ซึ่งแม้เราสั่งให้สัญญาณพัลซ์มีความกว้างเท่ากับ 1500 us ไป Continuous rotation servo ก็จะไม่หยุดหมุน เราจึงต้องปรับตั้งค่า Center Stop ดังนี้ 
– ต่อ Continuous rotation servo เข้ากับ Arduino

18

– เขียนโปรแกรมจ่ายความกว้างพัลซ์ 1500 us ให้กับ Servo Motor

#include <Servo.h>
Servo myServo;
void setup() {
          myServo.attach(9);
          myServo.writeMicroseconds(1500); // Stop 
}

– เมื่อรันโปรแกรมจ่ายความกว้างพัลซ์ 1500 us แล้ว Servo Motor ไม่หยุดหมุน ให้ใช้ไขควงขนาดเล็กหมุนปรับ Center Stop Adjust จน Servo Motor หยุดหมุน

19

การควบคุม Continuous Rotation Servo โดยใช้ Potentiometer ปรับความเร็วและทิศทางการหมุน

20

โค้ดตัวอย่างการควบคุม Continuous Rotation Servo โดยใช้ Potentiometer?

#include <Servo.h>
Servo myservo; // create servo object to control a servo
int potpin = 0; // analog pin used to connect the potentiometer
int val; // variable to read the value from the analog pin
void setup(){
          myservo.attach(9); // attaches the servo on pin 9 to the servo object
}
void loop(){
          val = analogRead(potpin); // reads the value of the potentiometer (value // between 0 and 1023)
          val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value // between 0 and 180)
          myservo.write(val); // sets the servo position according to the scaled value
          delay(15); // waits for the servo to get there
}

!!! โค้ดเหมือนกับตัวอย่าง RC Servo Motor โดยใช้ Potentiometer แต่ผลการทำงานจะแตกต่างกัน

ผลการทำงานของ Code
val = analogRead(potpin); 
          อ่านค่า Analog จาก Potentiometer ที่ต่ออยู่ที่ขา A0 เก็บไว้ในตัวแปร val
val = map(val, 0, 1023, 0, 179);
          เนื่องจาก ADC ภายใน Arduino เป็น ADC ขนาด 10-bit จึงอ่านค่า Analog ได้ตั้งแต่ 0-1023 แต่ Continuous Rotation Servo รับค่าได้ในช่วง 1-180 จึงต้องใช้ Function map เพื่อสเกลค่าจาก 0-1023 เป็น 0-179 แล้วนำไปเก็บไว้ในตัวแปร val
myservo.write(val);
          เมื่อสเกลค่า จาก 0-1023 ลงเหลือ 0-179 แล้วก็นำมาสั่งให้ Servo Motor หมุนในความเร็วและ ทิศทางตามค่าความกว้างของพัลซ์ที่จ่ายออกไป
delay(15); 
          หน่วงเวลา 15 ms
          ผลของการทำงานในโค้ดนี้จะเห็นได้ว่า หากปรับ Potentiometer ไปทางด้านใดด้านหนึ่งจนสุด Continuous Rotation Servo จะหมุนในทิศทางหนึ่งด้วยความเร็วสูงสุดและเมื่อเราค่อยๆ ปรับ Potentiometer กลับมาให้มาอยู่ในตำแหน่งกึ่งกลาง Servo Motor จะค่อยหมุนช้าลงแต่ยังหมุนในทิศทางเดิม และจะหยุดสนิทหากเราปรับ Potentiometer มาที่ตำแหน่งกึ่งกลางพอดี เมื่อปรับ Potentiometer เกินกว่า ครึ่งหนึ่งในทิศตรงกันข้าม Servo Motor จะเปลี่ยนทิศการหมุน แต่จะหมุนอย่างช้าๆ และจะค่อยๆหมุนเร็วขึ้นเมื่อเราปรับ Potentiometer มากขึ้น

RC Servo Motor รุ่นที่มีจำหน่าย:

EFDV245 Futaba S3003 Servo Motor
EFDV242 Tower Pro Micro Servo Motor SG90

Ref: 
http://arduino.cc/en/reference/servo 
http://ebldc.com/?p=48
http://www.pololu.com/blog/13/gettin-all-up-in-your-servos
http://learn.parallax.com/KickStart/900-00008
https://www.sparkfun.com/tutorials/283