3/6/64

วัดก๊าซคาร์บอนไดออกไซด์ แสดงผลที่จอ LCD

 


ระดับคาร์บอนไดออกไซด์ปกติ (Normal CO2 Levels)



ระดับคาร์บอนไดออกไซด์ปกติ
ผลกระทบของ CO2 ต่อผู้ใหญ่ที่มีสุขภาพที่ดีสามารถสรุปได้ดังนี้

-ระดับกลางแจ้งปกติ: 350 – 450 ppm
-ระดับที่ยอมรับได้: <600 ppm=”” span=””>
-ข้อร้องเรียนเกี่ยวกับความเข้มและกลิ่น: 600 – 1000 ppm
-ตามมาตรฐาน ASHRAE และ OSHA: 1000 ppm
-อาการง่วงนอนทั่วไป: 1000 – 2500 ppm
-คาดว่าจะมีผลต่อสุขภาพที่ไม่คาดคิด: 2500 – 5000 ppm
-ความเข้มข้นสูงสุดที่อนุญาตภายในระยะเวลาการทำงาน 8 ชั่วโมง: 5000 – 10000 ppm
-ความเข้มข้นสูงสุดที่อนุญาตภายในระยะเวลาการทำงาน 15 นาที: 30000 ppm


**ระดับข้างต้นค่อนข้างปกติและระดับสูงสุดอาจเป็นครั้งคราวเกิดขึ้นเป็นครั้งคราว โดยทั่วไปแล้วอัตราการถ่ายเทอากาศควรให้ความเข้มข้นของคาร์บอนไดออกไซด์ต่ำกว่า 1000 ppm เพื่อสร้างเงื่อนไขคุณภาพอากาศภายในที่ยอมรับได้สำหรับบุคคลส่วนใหญ่

ระดับ CO2 ที่รุนแรงและเป็นอันตราย
เพิ่มอัตราการมึนเมาการหายใจและชีพจรเล็กน้อยอาการคลื่นไส้: 30000 – 40000 ppm
อาการปวดศีรษะบวกและอาการสายตาผิดปกติ: 50000 ppm
หมดสติเสียชีวิต: 100000 ppm

หน่วยวัด ppm ค่ามลพิษทางอากาศ คืออะไร


ถ้าพูดถึงเรื่องหน่วยวัด ppm หลายท่านที่มีความรู้อยู่แล้วอาจตอบได้เลยว่าคืออะไรและมีความเกี่ยวข้องกับการทำงานหรือการใช้ชีวิตประจำวันอย่างไร แต่หลายท่านอาจไม่แน่ใจในคำตอบ ในที่นี้จะได้ทำความเข้าใจเกี่ยวกับเรื่องดังกล่าว

Part Per Million (ppm) หมายถึง หนึ่งส่วนในล้านส่วน(1 ใน 1,000,000) หน่วยวัด PPM ที่เรามักจะพบเจอส่วนใหญ่แล้วจะเป็นเรื่องของสิ่งแวดล้อมในงานอุตสาหกรรมหรือตามหน่วยงานต่างๆ เช่น การวัดค่ามลพิษทางอากาศ หมายถึงส่วนของปริมาณก๊าซพิษในปริมาณของอากาศล้านส่วน หรือปริมาณของตัวถูกละลายในสารละลายล้านส่วน นั่นเอง


สารประกอบอินทรีย์ระเหยง่าย (TVOC) คืออะไร ?


สารประกอบอินทรีย์ระเหยง่าย (TVOC) คือ กลุ่มสารประกอบอินทรีย์ที่ระเหยเป็นไอได้ง่าย กระจายตัวไปในอากาศได้ในอุณหภูมิและความดันปกติ สารประกอบอินทรีย์ระเหยง่าย (TVOC) มักจะเป็นสารที่อยู่ในผลิตภัณฑ์ในครัวเรือน เช่น น้ำยาทำความสะอาดและฆ่าเชื้อ สีทาบ้าน น้ำยาย้อมสีผมและดัดผม สารจำกัดศัตรูพืช หรือแม้แต่ ควันบุหรี่

ค่ามาตรฐานของ TVOC ต้องมีค่าต่ำกว่า 1 มก./ลบม. จึงจะไม่เป็นอันตรายต่อสุขภาพ

กรณีศึกษาคือให้อาสาสมัครที่ไม่สูบบุหรี่ ซึ่งสัมผัสกับสาร VOC ที่ 02.5 มก/ลบม. และ 25 มก/ลบม. ซึ่งคนที่ได้รับสาร 02.5 มก. แทบจะไม่มีอาการผิดปกติ ส่วนคนที่ได้รับสาร 25 มก. จะมีผลกระทบต่อระบบทางเดินหายใจ


อุปกรณ์ที่ใช้ในโปรเจค



ขั้นตอนการทำโปรเจค


1. การใช้งานจอ LCD 1602 กับ Arduino Keyestudio PLUS


โดยศึกษา ขั้นตอนการทำงาน จากลิงค์บทความ ด้านล่าง


2. การใช้งาน เซ็นเซอร์ วัดคาร์บอนไดออกไซด์ กับ Arduino


โดยศึกษา ขั้นตอนการทำงาน จากลิงค์บทความ ด้านล่าง



3. อัพโหลดโค้ด โปรเจควัดก๊าซคาร์บอนไดออกไซด์


เปิดโปรแกรม Arduino IDE เขียนโปรแกรม หรือ Sketch ตามโค้ดด้านล่างนี้




#include <CCS811.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

/*
   IIC address default 0x5A, the address becomes 0x5B if the ADDR_SEL is soldered.
*/
//CCS811 sensor(&Wire, /*IIC_ADDRESS=*/0x5A);
CCS811 sensor;

void setup(void)
{
  Serial.begin(115200);
  lcd.begin();
  lcd.backlight();
  /*Wait for the chip to be initialized completely, and then exit*/
  while (sensor.begin() != 0) {
    Serial.println("failed to init chip, please check if the chip connection is fine");
    delay(1000);


  }

  sensor.setMeasCycle(sensor.eCycle_250ms);
}
void loop() {
  delay(1000);

  if (sensor.checkDataReady() == true) {
    Serial.print("CO2: ");
    Serial.print(sensor.getCO2PPM());
    lcd.setCursor(1, 0);
    lcd.print("CO2: " + String(sensor.getCO2PPM()) + " ppm       ");
    Serial.print("ppm, TVOC: ");
    Serial.print(sensor.getTVOCPPB());
    Serial.println("ppb");
    lcd.setCursor(0, 1);
    lcd.print("TVOC: " + String(sensor.getTVOCPPB()) + " ppb       ");

  } else {
    Serial.println("Data is not ready!");
    lcd.setCursor(0, 0);
    lcd.print("Data is not ready!");
  }
  /*!
     @brief Set baseline
     @param get from getBaseline.ino
  */
  sensor.writeBaseLine(0x847B);
  //delay cannot be less than measurement cycle
  //delay(1000);
}



4. ผลลัพธ์การทำงาน


หาแหล่งก๊าซคาร์บอนไดออกไซด์ เช่นการหายใจออกไปที่เซ็นเซอร์ หรือ สารประกอบอินทรีย์ระเหยง่าย TVOC ข้อมูลจะเปลี่ยนแปลงอย่างเห็นได้ที่จอ LCD ตามคลิปด้านล่าง...




26/11/63

เครื่องวัดฝุ่น PM2.5 ด้วย เซ็นเซอร์ GP2Y1014AU



เครื่องวัดฝุ่น PM2.5 ด้วย เซ็นเซอร์ GP2Y1014AU

เครื่องวัดฝุ่น PM2.5 ด้วย เซ็นเซอร์ GP2Y1014AU

 

บทความนี้ กล่าวถึงขั้นตอนการทำงานโปรเจค เครื่องวัดฝุ่น PM2.5 กับ Arduino UNO โดยใช้ เซ็นเซอร์วัดฝุ่น PM2.5 Keyestudio GP2Y1014AU ของ Sharp เซ็นเซอร์ฝุ่นนี้มีขนาดเล็กและสามารถตรวจจับฝุ่นละอองและอนุภาคควันในสิ่งแวดล้อมได้ ใช้พลังงานน้อยมากในขณะที่ทำงานจึงเหมาะอย่างยิ่งสำหรับระบบตรวจสอบที่เปิดตลอดเวลา


อุปกรณ์ที่ใช้ในโปรเจค


2. Sensor Shield V 5.0

3. เซ็นเซอร์วัดฝุ่น PM2.5 Keyestudio GP2Y1014AU

4. LCD2004 I2C 20×4 Character LCD Display Module

5. 
Jumper (F2F) cable wire 30cm Female to Female


ขั้นตอนการทำโปรเจค


1. ทดสอบใช้งาน จอ LCD 20×4 กับ Arduino UNO


ทดสอบใช้งาน จอ LCD 20×4 กับ Arduino UNO ตามลิงค์ด้านล่าง



2. ทดสอบใช้งาน เซ็นเซอร์วัดฝุ่น PM2.5 GP2Y1014AU


ทดสอบใช้งาน เซ็นเซอร์วัดฝุ่น PM2.5 GP2Y1014AU ตามลิงค์ด้านล่าง




3. อัพโหลดโค้ด โปรเจคเครื่องวัดฝุ่น PM2.5


เปิดโปรแกรม Arduino IDE เขียนโปรแกรม หรือ Sketch ตามโค้ดด้านล่างนี้



#include  <Wire.h>
#include  <LiquidCrystal_I2C.h>
LiquidCrystal_I2C  lcd(0x27, 20, 4); 
int  measurePin  =  0;
int  ledPower  =  2; 
int  samplingTime  =  280;
int  deltaTime  =  40;
int  sleepTime  =  9680;
float  voMeasured  =  0;
float calcVoltage = 0;
float  dustDensity  =  0;


void setup() {

  // initialize the LCD,
  lcd.begin();
  // Turn on the blacklight and print a message.
  lcd.backlight();
  lcd.clear();

  //  Print  a  message  to  the  LCD.

  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Raw  Signal  Value:  ");
  lcd.setCursor(0, 2);
  lcd.print("Voltage:");
  lcd.setCursor(0, 3);
  lcd.print("Dust  Density:");
  pinMode(ledPower, OUTPUT);


}

void loop() {

  digitalWrite(ledPower, LOW); //  power  on  the  LED
  delayMicroseconds(samplingTime);
  voMeasured  =  analogRead(measurePin);  //  read  the  dust  value
  delayMicroseconds(deltaTime);
  digitalWrite(ledPower, HIGH); //  turn  the  LED  off
  delayMicroseconds(sleepTime);

  //  0  -  5V  mapped  to  0  -  1023  integer  values
  //  recover  voltage

  calcVoltage  =  voMeasured  *  (5.0  /  1024.0);
  dustDensity  =  1.7  *  calcVoltage  -  0.1;
  lcd.setCursor(1, 1);
  lcd.print(voMeasured);
  lcd.setCursor(9, 2);
  lcd.print(calcVoltage);
  lcd.setCursor(14, 3);
  lcd.print(dustDensity);

  delay(1000);

}




4. ผลลัพธ์การทำงาน


เครื่องวัดฝุ่น PM2.5 ด้วย เซ็นเซอร์ GP2Y1014AU


เครื่องวัดฝุ่น PM2.5 ด้วย เซ็นเซอร์ GP2Y1014AU



ข้อมูลเปรียบเทียบกับคุณภาพอากาศ:

3000 + = Very Bad
1050-3000 = Bad
300-1050 = Ordinary
150-300 = Good
75-150 = Very Good
0-75 = Tiptop


นี่คือแผนภูมิเปรียบเทียบความเข้มข้นของฝุ่น
ตรวจสอบได้ไหมว่าค่าความเข้มข้นของฝุ่นอยู่ในระดับใด?

ในการทดสอบของเราตอนนี้ค่าที่วัดได้แสดงให้เห็นว่าคุณภาพอากาศดีมาก (Very Good)


3/7/63

หรี่ไฟ AC 220V ผ่านบลูทูธ ด้วย App Android

หรี่ไฟ AC 220V ผ่านบลูทูธ ด้วย App Android





เป้าหมายและหลักการทำงานของโปรเจค



ต้องการหรี่ไฟ หรือ
ควบคุมแสงสว่างของหลอดไฟหรืออุปกรณ์ไฟฟ้าอื่นๆ  แบบไร้สายผ่านโทรศัพท์มือถือด้วยแอพแอนดรอยด์ (App Android) เพื่อเป็นการประหยัดพลังงาน และความสะดวกในการควบคุม ซึ่งจะทำให้หลอดไฟกินไฟน้อยลง นอกจากนั้นยังช่วยทำให้ยืดอายุหลอดไฟได้ยาวนานขึ้น เพราะไม่ต้องทำงานเต็มกำลังความสว่าง 100% ตลอดเวลา


รู้จัก PWM (Pulse Width Modulation)

PWM คือเทคนิดการส่งสัญญาณแบบสวิต หรือ ส่งค่าดิจิตอล 0-1 โดยให้สัญญาณความถี่คงที่ การควบคุมระยะเวลาสัญญาณสูงและสัญญาณต่ำ ที่ต่างกัน ก็จะทำให้ค่าแรงดันเฉลี่ยของสัญญาณสวิต ต่างกันด้วย







สำหรับโมดุล PWM ของ Arduino มีความละเอียด 8 bit หรือ ปรับได้ 255 ระดับ ดังนั้นค่าสัญญาณ 0 โวลต์ถึง 5 โวลต์ จะถูกแสดงเป็นสัญญาณแบบดิจิตอล จะได้ 0 ถึง 255



อุปกรณ์ที่ใช้ในโปรเจค



2. Sensor Shield V 5.0

3.  PWM Dimming Adjust 220V Module

4. Jumper (F2F) 20cm Female to Female

5. Bluetooth HC-06 Slave


อุปกรณ์ไฟฟ้าอื่นๆ เช่น หลอดไฟ , ปลั๊กไฟ , ขั้วหลอดไฟ , สายไฟ หาซื้อได้ตามร้านขายอุปกรณ์ไฟฟ้าทั่วๆไป

*** การใช้งานแบบไม่ต้องการเชื่อมต่อสาย USB กับ คอมพิวเตอร์ ให้ใช้ Adapter DC 9V 1A Power Supply เป็นแหล่งจ่ายไฟ เสียบเข้ากับ DC Power Jack ของ  บอร์ด Arduino ***


(เพื่อความปลอดภัย : ผู้ทำโปรเจค ควรมี ความรู้ด้านไฟฟ้าเป็นอย่างดี)




ขั้นตอนการทำโปรเจค




1.  ทดสอบการทำงาน PWM Dimming Module


ประกอบ Sensor Shield เข้ากับ Arduino UNO





ประกอบ Sensor Shield V 5.0




เชื่อมต่อ Sensor Shield กับ Dimmer Module






เชื่อมต่อ PWM Dimming Module กับ  อุปกรณ์ไฟฟ้าอื่นๆ


( หลอดไฟควรมีคุณสมบัติหรี่ไฟได้ เช่น หลอดไส้ร้อนแบบธรรมดา 40 - 100 วัตต์ )






เปิดโปรแกรม Arduino IDE เขียนโค้ดและอัพโหลดโค้ดดังนี้



int PWM_Pin = 3;

void setup() {
  pinMode(PWM_Pin, OUTPUT);

}

void loop() {
  analogWrite(PWM_Pin, 0);
  delay(1000);
  analogWrite(PWM_Pin, 64);
  delay(1000);
  analogWrite(PWM_Pin, 127);
  delay(1000);
  analogWrite(PWM_Pin, 191);
  delay(1000);
  analogWrite(PWM_Pin, 255);
  delay(1000);
}



ไปที่ Tools > Board เลือกเป็น Arduino UNO แล้วเลือกพอร์ตที่ปรากฏ ในตัวอย่างเลือกเป็น "COM8"


กดปุ่ม  เพื่ออัพโหลด หากสามารถอัพโหลดโปรแกรมลงบอร์ดได้สำเร็จ จะแสดงคำว่า Done uploading. ที่แถบด้านล่าง




ผลลัพธ์การทำงาน :

  • หลอดไฟสว่าง 0 %
  • หลอดไฟสว่าง 25 %
  • หลอดไฟสว่าง 50 %
  • หลอดไฟสว่าง 75 %
  • หลอดไฟสว่าง 100 %

วนลูปไปเรื่อยๆ 
แสดงว่า การเชื่อมต่อ PWM Dimming Module ของเรานั้นถูกต้อง และ พร้อมใช้งานแล้ว



2. เชื่อมต่อสาย Bluetooth HC-06






3. อัพโหลดโค้ด หรี่ไฟ AC 220V ผ่านบลูทูธ



เปิดโปรแกรม Arduino IDE เขียนโค้ดและอัพโหลดโค้ดดังนี้





#include <SoftwareSerial.h>
SoftwareSerial BTSerial(4, 5); // RX, TX

int PWM_Pin = 3;
char input; 
int minBright = 50; 
int maxBright = 190;
bool stringComplete = false;

void setup() {
  Serial.begin(9600);
  BTSerial.begin(9600);
  pinMode(PWM_Pin, OUTPUT);
  Serial.println("AC Light Dimmer program has started.");
}

void loop() {
  serialEvent();
  if (stringComplete) {
    int fadeValue = map(input, 0, 100, minBright, maxBright);
    analogWrite(PWM_Pin, fadeValue);
    Serial.println(fadeValue);
    stringComplete = false;
  }
}

void serialEvent() {
  while (BTSerial.available()) {
    input = BTSerial.read();
    stringComplete = true;
  }
}



*** ค่า PWM ที่ minBright และ maxBright สามารถปรับได้ ตามความเหมาะสม ***


4. ทดสอบการทำงาน


เปิดการใช้งานบลูทูธ และ เชื่อมต่อกับ HC-06



ใช้ สมาร์ทโฟน Android  เปิดการใช้งานบลูทูธ และ เลือกเชื่อมต่อกับ HC-06







ใส่รหัสผ่าน 1234 -> ตกลง






 ใช้สมาร์ทโฟน Android ดาวน์โหลดแอพ  AC Light Dimmer



https://community.createlabz.com/wp-content/uploads/2019/04/AC-Light-Dimmer-aia-and-apk.zip


คลายซิป และ เลือกไฟล์ AC_Light_Dimmer.apk เพื่อทำการติดตั้งโปรแกรม




เลือกไอคอน Bluetooth




เลือก HC-06




ทดสอบการทำงาน




credit : https://community.createlabz.com/knowledgebase/bluetooth-controlled-ac-light-dimmer-with-android-mobile-app/

14/5/63

สร้างเกม Endless Runner ด้วย Arduino และ จอ LCD


มินิโปรเจค สร้างเกม Endless Runner ด้วย Arduino และ จอ LCD






การสร้างเกม ก็คือการเขียนโปรแกรมแบบหนึ่ง ให้แสดงผลถี่ๆ แล้วเขียนโปรแกรมให้การแสดงผลในแต่ละครั้ง ค่อยๆทำให้ตัวละครในภาพค่อยๆขยับ โดยการทำการเปลี่ยนภาพ หรือเคลื่อนที่ตัวละคร ก็จะเกิดการเคลื่อนไหวในเกมขึ้น




Endless Runner เป็นเกม ที่ตัวละครวิ่งหลบอุปสรรคระหว่างทางในเมือง New York อีกอย่างที่คุ้นเคยแน่นอนคือตัวละครและการเคลื่อนไหว และเกมแนว Endless Runner หรือ การวิ่งไปเรื่อยๆ โดยให้ทำคะแนนให้ได้มากที่สุด เป็นเกมที่เล่นง่าย สนุกและใช้ฆ่าเวลาได้เป็นอย่างดี




ประโยชน์และการนำโปรเจคไปพัฒนาต่อ

– เพื่อศึกษาการใช้งานของโปรแกรม Arduino IDE
– เพื่อศึกษาการใช้งานของ Arduino กับจอ LCD
– เพื่อศึกษาการใช้งานอินเตอร์รัพท์ของ Arduino
– เพื่อศึกษาการสร้างเกมด้วย Arduino
– เพื่อให้ผู้ที่สนใจสามารถนำไปพัฒนาต่อได้


อุปกรณ์ที่ใช้ในโปรเจค



*** การใช้งานแบบไม่ต้องการเชื่อมต่อสาย USB กับ คอมพิวเตอร์ ให้ใช้ Adapter DC 9V 1A Power Supply เป็นแหล่งจ่ายไฟ เสียบเข้ากับ DC Power Jack ของ  บอร์ด Arduino ***


ขั้นตอนการทำโปรเจค



1. ต่อใช้งาน จอ LCD กับ Arduino





1.1 ยึดบอร์ด Arduino UNO



1.2 ประกอบ Sensor Shield V 5.0



1.3 เชื่อมต่อสาย LCD








1.4 ดาวน์โหลด Arduino I2C Library สำหรับ LCD

https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library


1.5 ติดตั้ง I2C Library สำหรับ LCD


1.5.1 เชื่อมต่อสาย USB ระหว่าง คอมพิวเตอร์ กับ บอร์ด Arduino




1.5.2 เปิดโปรแกรม Arduino IDE


1.5.3 ไปที่ Skecth -> Include Library -> Add .ZIP Library...




1.5.4 ไปที่ ไลบรารี Arduino-LiquidCrystal-I2C-library-master.zip ที่เรา ดาวน์โหลด มา -> Open



1.5.5 ตรวจสอบที่ Skecth -> Include Library  จะพบ ไลบรารี Arduino-LiquidCrystal-I2C-library-master เพิ่มเข้ามาใน Arduino IDE ของเรา




1.6 อัพโหลดโค้ด


1.6.1 เขียนโค้ดดังนี้


#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
 
LiquidCrystal_I2C lcd(0x27, 16, 2);
 
void setup()
{
  
  lcd.begin();
 
  lcd.backlight();
  lcd.print("Hello, world!");
}
 
void loop()
{
  
}

1.6.2 ไปที่ Tools > Board เลือกเป็น Arduino/Genuino UNO




1.6.3 ไปที่ Tools > Port แล้วเลือกพอร์ตที่ปรากฏ (กรณีใช้เครื่องคอมพิวเตอร์ที่มี COM Port มากกว่าหนึ่ง  ให้เลือกตัวอื่นที่ไม่ใช่ COM1)

ในตัวอย่างเลือกเป็น "COM6"

(ถ้ายังว่าง หรือ เป็น COM1 ให้ตรวจสอบการติดตั้งไดร์เวอร์ การต่อสาย USB ของ Arduino UNO)



1.6.4 กดปุ่ม  เพื่ออัพโหลด




1.6.5 หากสามารถอัพโหลดโปรแกรมลงบอร์ดได้สำเร็จ จะแสดงคำว่า Done uploading. ที่แถบด้านล่าง




1.7 ปรับความสว่างหน้าจอ LCD






2. เชื่อมต่อสาย Push Button Switch


ปุ่มกด (Push Button) หรือสวิตซ์ (Switch) เป็นอุปกรณ์พื้นฐานที่ใช้กันทั่วไป เพื่อเชื่อมจุด 2 จุดในวงจรให้ถึงกัน มักใช้เพื่อรับข้อมูลจากผู้ใช้

พอร์ตที่สามารถใช้งานอินเตอร์รัพท์ได้


ในบอร์ด Arduino จะมีพอร์ตที่สามารถใช้อินเตอร์รัพท์ได้แบบจำกัด โดยพอร์ตที่สามารถใช้อินเตอร์รัพท์ได้ในบอร์ด Arduino แต่ละรุ่นก็จะแตกต่างกัน ซึ่งสามารถดูได้ตามตารางด้านล่างนี้

Boardint.0int.1int.2int.3int.4int.5
Uno 23
Mega25602321201918
Leonardo32017

ในการสร้างอินเตอร์รัพท์ จะใช้ลำดับขาอินเตอร์รัพท์มาใส่ในฟังก์ชั่น เช่น ในตาราง บอร์ด Arduino Uno มีพอร์ต 2 เป็นขาอินเตอร์รัพท์ลำดับที่ 0 ดังนั้นเมื่อนำไปใช้ในคำสั่ง ต้องนำหมายเลข 0 ไปใส่

คำสั่งกำหนดใช้อินเตอร์รัพท์


คำสั่ง attachInterrupt() เป็นคำสั่งกำหนด และสร้างอินเตอร์รัพท์ โดยมีรูปแบบการใช้งานดังนี้
รูปแบบคำสั่ง attachInterrupt(interrupt, ISR, mode)
  • interrupt คือลำดับขาอินเตอร์รัพท์ตามที่ได้ดูไปในตาราง
  • ISR คือชื่อฟังก์ชั่นที่จะไปถูกเรียกขึ้นมาเมื่อเกิดอินเตอร์รัพท์
  • mode รูปแบบที่จะให้เกิดอินเตอร์รัพท์ มีทั้งหมด 4 รูปแบบดังนี้
    • LOW จะเกิดอินเตอร์รัพท์ต่อเมื่อพอร์ตที่กำหนดไว้มีสถานะเป็น LOW
    • CHANGE จะเกิดอินเตอร์รัพท์เมื่อพอร์ตที่กำหนดไว้มีการเปลี่ยนสถานะ เช่น จากสถานะ HIGH เป็น LOW หรือจาก LOW เป็น HIGH
    • RISING จะเกิดอินเตอร์รัพท์เมื่อพอร์ตที่กำหนดไว้มีการเปลี่ยนสถานะจาก LOW เป็น HIGH
    • FALLING จะเกิดอินเตอร์รัพท์เมื่อพอร์ตที่กำหนดไว้มีการเปลี่ยนสถานะจาก HIGH เป็น LOW
    • HIGH จะเกิดอินเตอร์รัพท์ต่อเมื่อพอร์ตที่กำหนดไว้มีสถานะเป็น HIGH

2.1 เชื่อมต่อสาย Switch



3. อัพโหลดโค้ด เกม Endless Runner 


เปิดโปรแกรม Arduino IDE อัพโหลดโค้ดดังนี้






#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
 
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define PIN_BUTTON 2
#define PIN_AUTOPLAY 1
#define PIN_READWRITE 10
#define PIN_CONTRAST 12

#define SPRITE_RUN1 1
#define SPRITE_RUN2 2
#define SPRITE_JUMP 3
#define SPRITE_JUMP_UPPER '.'         // Use the '.' character for the head
#define SPRITE_JUMP_LOWER 4
#define SPRITE_TERRAIN_EMPTY ' '      // User the ' ' character
#define SPRITE_TERRAIN_SOLID 5
#define SPRITE_TERRAIN_SOLID_RIGHT 6
#define SPRITE_TERRAIN_SOLID_LEFT 7

#define HERO_HORIZONTAL_POSITION 1    // Horizontal position of hero on screen

#define TERRAIN_WIDTH 16
#define TERRAIN_EMPTY 0
#define TERRAIN_LOWER_BLOCK 1
#define TERRAIN_UPPER_BLOCK 2

#define HERO_POSITION_OFF 0          // Hero is invisible
#define HERO_POSITION_RUN_LOWER_1 1  // Hero is running on lower row (pose 1)
#define HERO_POSITION_RUN_LOWER_2 2  //                              (pose 2)

#define HERO_POSITION_JUMP_1 3       // Starting a jump
#define HERO_POSITION_JUMP_2 4       // Half-way up
#define HERO_POSITION_JUMP_3 5       // Jump is on upper row
#define HERO_POSITION_JUMP_4 6       // Jump is on upper row
#define HERO_POSITION_JUMP_5 7       // Jump is on upper row
#define HERO_POSITION_JUMP_6 8       // Jump is on upper row
#define HERO_POSITION_JUMP_7 9       // Half-way down
#define HERO_POSITION_JUMP_8 10      // About to land

#define HERO_POSITION_RUN_UPPER_1 11 // Hero is running on upper row (pose 1)
#define HERO_POSITION_RUN_UPPER_2 12 //                              (pose 2)

static char terrainUpper[TERRAIN_WIDTH + 1];
static char terrainLower[TERRAIN_WIDTH + 1];
static bool buttonPushed = false;

void initializeGraphics(){
  static byte graphics[] = {
    // Run position 1
    B01100,
    B01100,
    B00000,
    B01110,
    B11100,
    B01100,
    B11010,
    B10011,
    // Run position 2
    B01100,
    B01100,
    B00000,
    B01100,
    B01100,
    B01100,
    B01100,
    B01110,
    // Jump
    B01100,
    B01100,
    B00000,
    B11110,
    B01101,
    B11111,
    B10000,
    B00000,
    // Jump lower
    B11110,
    B01101,
    B11111,
    B10000,
    B00000,
    B00000,
    B00000,
    B00000,
    // Ground
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    // Ground right
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    // Ground left
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
  };
  int i;
  // Skip using character 0, this allows lcd.print() to be used to
  // quickly draw multiple characters
  for (i = 0; i < 7; ++i) {
    lcd.createChar(i + 1, &graphics[i * 8]);
  }
  for (i = 0; i < TERRAIN_WIDTH; ++i) {
    terrainUpper[i] = SPRITE_TERRAIN_EMPTY;
    terrainLower[i] = SPRITE_TERRAIN_EMPTY;
  }
}

// Slide the terrain to the left in half-character increments
//
void advanceTerrain(char* terrain, byte newTerrain){
  for (int i = 0; i < TERRAIN_WIDTH; ++i) {
    char current = terrain[i];
    char next = (i == TERRAIN_WIDTH-1) ? newTerrain : terrain[i+1];
    switch (current){
      case SPRITE_TERRAIN_EMPTY:
        terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY;
        break;
      case SPRITE_TERRAIN_SOLID:
        terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID;
        break;
      case SPRITE_TERRAIN_SOLID_RIGHT:
        terrain[i] = SPRITE_TERRAIN_SOLID;
        break;
      case SPRITE_TERRAIN_SOLID_LEFT:
        terrain[i] = SPRITE_TERRAIN_EMPTY;
        break;
    }
  }
}

bool drawHero(byte position, char* terrainUpper, char* terrainLower, unsigned int score) {
  bool collide = false;
  char upperSave = terrainUpper[HERO_HORIZONTAL_POSITION];
  char lowerSave = terrainLower[HERO_HORIZONTAL_POSITION];
  byte upper, lower;
  switch (position) {
    case HERO_POSITION_OFF:
      upper = lower = SPRITE_TERRAIN_EMPTY;
      break;
    case HERO_POSITION_RUN_LOWER_1:
      upper = SPRITE_TERRAIN_EMPTY;
      lower = SPRITE_RUN1;
      break;
    case HERO_POSITION_RUN_LOWER_2:
      upper = SPRITE_TERRAIN_EMPTY;
      lower = SPRITE_RUN2;
      break;
    case HERO_POSITION_JUMP_1:
    case HERO_POSITION_JUMP_8:
      upper = SPRITE_TERRAIN_EMPTY;
      lower = SPRITE_JUMP;
      break;
    case HERO_POSITION_JUMP_2:
    case HERO_POSITION_JUMP_7:
      upper = SPRITE_JUMP_UPPER;
      lower = SPRITE_JUMP_LOWER;
      break;
    case HERO_POSITION_JUMP_3:
    case HERO_POSITION_JUMP_4:
    case HERO_POSITION_JUMP_5:
    case HERO_POSITION_JUMP_6:
      upper = SPRITE_JUMP;
      lower = SPRITE_TERRAIN_EMPTY;
      break;
    case HERO_POSITION_RUN_UPPER_1:
      upper = SPRITE_RUN1;
      lower = SPRITE_TERRAIN_EMPTY;
      break;
    case HERO_POSITION_RUN_UPPER_2:
      upper = SPRITE_RUN2;
      lower = SPRITE_TERRAIN_EMPTY;
      break;
  }
  if (upper != ' ') {
    terrainUpper[HERO_HORIZONTAL_POSITION] = upper;
    collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true;
  }
  if (lower != ' ') {
    terrainLower[HERO_HORIZONTAL_POSITION] = lower;
    collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true;
  }
  
  byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1;
  
  // Draw the scene
  terrainUpper[TERRAIN_WIDTH] = '\0';
  terrainLower[TERRAIN_WIDTH] = '\0';
  char temp = terrainUpper[16-digits];
  terrainUpper[16-digits] = '\0';
  lcd.setCursor(0,0);
  lcd.print(terrainUpper);
  terrainUpper[16-digits] = temp;  
  lcd.setCursor(0,1);
  lcd.print(terrainLower);
  
  lcd.setCursor(16 - digits,0);
  lcd.print(score);

  terrainUpper[HERO_HORIZONTAL_POSITION] = upperSave;
  terrainLower[HERO_HORIZONTAL_POSITION] = lowerSave;
  return collide;
}

// Handle the button push as an interrupt
void buttonPush() {
  buttonPushed = true;
}

void setup(){
  pinMode(PIN_READWRITE, OUTPUT);
  digitalWrite(PIN_READWRITE, LOW);
  pinMode(PIN_CONTRAST, OUTPUT);
  digitalWrite(PIN_CONTRAST, LOW);
  pinMode(PIN_BUTTON, INPUT);
  digitalWrite(PIN_BUTTON, HIGH);
  pinMode(PIN_AUTOPLAY, OUTPUT);
  digitalWrite(PIN_AUTOPLAY, HIGH);
  
  // Digital pin 2 maps to interrupt 0
  attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING);
  
  initializeGraphics();
  
  lcd.begin();
}

void loop(){
  static byte heroPos = HERO_POSITION_RUN_LOWER_1;
  static byte newTerrainType = TERRAIN_EMPTY;
  static byte newTerrainDuration = 1;
  static bool playing = false;
  static bool blink = false;
  static unsigned int distance = 0;
  
  if (!playing) {
    drawHero((blink) ? HERO_POSITION_OFF : heroPos, terrainUpper, terrainLower, distance >> 3);
    if (blink) {
      lcd.setCursor(0,0);
      lcd.print("Game Over");
    }
    delay(250);
    blink = !blink;
    if (buttonPushed) {
      initializeGraphics();
      heroPos = HERO_POSITION_RUN_LOWER_1;
      playing = true;
      buttonPushed = false;
      distance = 0;
    }
    return;
  }

  // Shift the terrain to the left
  advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
  advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
  
  // Make new terrain to enter on the right
  if (--newTerrainDuration == 0) {
    if (newTerrainType == TERRAIN_EMPTY) {
      newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK;
      newTerrainDuration = 2 + random(10);
    } else {
      newTerrainType = TERRAIN_EMPTY;
      newTerrainDuration = 10 + random(10);
    }
  }
    
  if (buttonPushed) {
    if (heroPos <= HERO_POSITION_RUN_LOWER_2) heroPos = HERO_POSITION_JUMP_1;
    buttonPushed = false;
  }  

  if (drawHero(heroPos, terrainUpper, terrainLower, distance >> 3)) {
    playing = false; // The hero collided with something. Too bad.
  } else {
    if (heroPos == HERO_POSITION_RUN_LOWER_2 || heroPos == HERO_POSITION_JUMP_8) {
      heroPos = HERO_POSITION_RUN_LOWER_1;
    } else if ((heroPos >= HERO_POSITION_JUMP_3 && heroPos <= HERO_POSITION_JUMP_5) && terrainLower[HERO_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) {
      heroPos = HERO_POSITION_RUN_UPPER_1;
    } else if (heroPos >= HERO_POSITION_RUN_UPPER_1 && terrainLower[HERO_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) {
      heroPos = HERO_POSITION_JUMP_5;
    } else if (heroPos == HERO_POSITION_RUN_UPPER_2) {
      heroPos = HERO_POSITION_RUN_UPPER_1;
    } else {
      ++heroPos;
    }
    ++distance;
    
    digitalWrite(PIN_AUTOPLAY, terrainLower[HERO_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW);
  }
  delay(100);
}




credit :  https://www.youtube.com/watch?v=LMWp1eWxj08 Ideas TV