Posted on Leave a comment

Arduino Nano with sensor (ก้าวที่สาม)

จาก Arduino Nano with Serial (ก้าวที่สอง) หวังว่าจะเข้าใจและทำแบบฝึกหัดกันได้นะครับ แต่ถ้าไม่ได้ก็ไม่เป็นไร เพราะค่อนข้างยากสำหรับมือใหม่ แต่ถ้าเข้าใจในก้าวที่สามนี้ จะต้องกลับไปทำแบบฝึกหัดได้อย่างแน่นอนเลยละครับ

จากตอนที่แล้ว(ก้าวที่สอง) เราได้เรียนรู้ function สำคัญที่ช่วยให้เราส่งข้อมูลจาก computer ให้ Arduino Nano ไปแล้ว คราวนี้เราจะให้ Arduino Nano ส่งข้อมูลกลับมาให้เราบ้าง โดยให้ Arduino Nano รับข้อมูลจาก sensor แล้วส่งมายัง computer โดยในที่นี้เราจะใช้ sensor ที่ผู้เขียนชอบตัวหนึ่งคือ BME280 ซึ่งเป็น sensor ที่วัด อุณหภูมิ ความชื้น และแรงดันอากาศ ด้วย 

โดยปกติแล้ว ถ้าเราจะเขียนโปรแกรมดึงข้อมูลจาก sensor สักตัว เราต้องเข้าใจการทำงานของ sensor ตัวนั้นอย่างละเอียด โดยดูจากคู่มือของ sensor นั้น ๆ จึงจะสามารถดึงข้อมูลมาได้อย่างถูกต้อง BME280 เองก็เหมือนกัน ถ้าเราจะเขียนโปรแกรมติดต่อเพื่อดึงข้อมูลเองนั้น เป็นเรื่องที่ต้องใช้เวลามากเลยทีเดียว (คู่มือ BME280) ดังนั้นจึงมีใครบางคนหรือบางกลุ่ม ได้ศึกษาการใช้งาน sensor และเขียนโปรแกรมเตรียมไว้ให้เรานำไปใช้ได้ง่าย ๆ สิ่งที่เค้าเขียนเตรียมไว้ให้นี้ ก็เป็นลักษณะของ function ให้เราเรียกใช้ โดยทั้งหมดที่เค้าเตรียมไว้ให้นั้น เรียกว่า library ดังนั้น เราต้องติดตั้ง library ของ BME280 ก่อน โดยวิธีการติดตั้ง library นั้นสามารถศึกษาได้จาก ขั้นตอนการติดตั้ง library ได้เลยครับ ในตัวอย่างนี้จะใช้ library ของ Adafruit ซึ่งต้องขอบคุณที่เค้าทำให้เราได้ใช้กันง่าย ๆ ฟรี ๆ ถ้ามีโอกาสก็อยากให้สนับสนุนสินค้าของ Adafruit เค้าด้วยนะครับ แล้วเราก็มาเริ่มกันเลย

สิ่งที่ต้องมี

เมื่อเตรียมอุปกรณ์พร้อมแล้ว ก็มาเริ่มกันเลย

Hardware

ต่อสาย โดยให้ต่อสายจาก Arduino Nano ไปที่ BME280 sensor module ดังนี้

Arduino Nano↔︎BME280 (Adafruit)
5V↔︎Vin
GND↔︎GND
A5↔︎SCK
A4↔︎SDI
Arduino Nano and BME280 connecting table
Arduino Nano and BME280 wiring

Software

หลังจากติดตั้ง library แล้ว มักจะมีตัวอย่างการใช้งาน library มาให้ด้วย และเป็นจุดเริ่มต้นที่ดีในการศึกษาการใช้ library นั้น ๆ ดังนั้น เราก็มาดูตัวอย่างกันเลย จากเมนู ให้ไปที่ File -> Examples -> AdafruitBME280 Library -> bme280test

//Comment

เมื่อเปิด code ตัวอย่าง เราต้องอ่านทั้ง comment และ code เพื่อให้เข้าใจว่า code ตัวอย่างนี้ถูกเขียนให้ใช้ hardware แบบไหน เช่น BME280 สามารถสื่อสารได้ทั้งแบบ I2C หรือ SPI แต่ใน code มักจะถูกเขียนให้เลือกใช้อย่างเดียว เราก็ต้องดูว่า code นั้นเลือกใช้การสื่อสารแบบไหน เหมือนกับที่เราต่อ hardware ไว้หรือไม่ เช่น ในระบบของเรา เราต่อ hardware เพื่อใช้ I2C อย่างน้อยเราก็ต้องดูให้แน่ใจว่า code ตัวอย่างนั้นใช้ I2C ด้วยเช่นกัน

เราเริ่มจาก comment ที่หัว file กันเลย จะเห็นว่า comment ก็บอกว่า ใช้ได้ทั้ง I2C หรือ SPI ถ้าเป็น I2C จะต้องใช้ address อะไรในการติดต่อกับ sensor และ อื่น ๆ ที่ผู้เขียนอยากจะสื่อสาร รวมทั้งใครเป็นคนเขียน code นี้ขึ้นมา และคนอื่น ๆ มีสิทธิใช้ code นี้แค่ไหนอย่างไร

/***************************************************************************
  This is a library for the BME280 humidity, temperature & pressure sensor

  Designed specifically to work with the Adafruit BME280 Breakout
  ----> http://www.adafruit.com/products/2650

  These sensors use I2C or SPI to communicate, 2 or 4 pins are required
  to interface. The device's I2C address is either 0x76 or 0x77.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit andopen-source hardware by purchasing products
  from Adafruit!

  Written by Limor Fried & Kevin Townsend for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
  See the LICENSE file for details.
 ***************************************************************************/

#include

ส่วนต่อมา เป็นการรวม file ที่เป็น header (filename.h) ที่เป็นของ library ต่าง ๆ ที่ต้องใช้ รวมเข้ามาใน code นี้ด้วย ถ้าไม่รวมเข้ามา เราก็จะใช้ library เหล่านั้นไม่ได้ ในที่นี้ ก็มีการรวม header ของ Wire, SPI, Adafruit_Sensor และ Adafruit_BME280 เข้ามา ซึ่งใน file เหล่านี้จะมีการประกาศค่าต่าง ๆ ที่ใช้ใน library เช่น ค่าคงที่ หรือ function ต่างๆ ที่เราสามารถเรียกใช้ได้

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define

ส่วนต่อมา เป็นเรื่องของการ กำหนดค่าคงที่ต่าง ๆ ในส่วนแรก เป็นการกำหนดชื่อให้ขา 13, 12, 11, และ 10 แล้วหลังจากนี้จะอ้างถึงขาเหล่านนี้โดยใช้ชื้อพวกนี้แทนเลขขา ต่อมาเป็นการกำหนดชื่อให้กับค่าคงที่ตัวหนึ่ง

#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10

#define SEALEVELPRESSURE_HPA (1013.25)

ส่วนต่อมา เป็นการประกาศตัว bme เป็นชนิด Adafruit_BME280 เสมือนว่า กำหนดให้ bme คือ sensor BME280 นั่นเอง โดยรายละเอียดของตัวแปรชนิดนี้จะอยู่ใน library ตรงนี้จะเห็นว่ามี 3 บรรทัด และ active เฉพาะบรรทัดแรก อีกสองบรรทัดถูก comment ไว้ ตรงนี้จะเป็นการเลือกว่า เราจะติดต่อสื่อสารกับ sensor แบบไหน โดยที่บรรทัดแรก เป็นการติดต่อแบบ I2C ซึ่งก็ตรงกับที่ต่อ hardware ไว้ บรรทัดที่สองเป็นแบบ SPI โดยใช้ hardware SPI ส่วนบรรทัดที่สามเป็นการติดต่อแบบ SPI แต่เป็นแบบ software SPI

Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI

ต่อมาเป็นการประกาศตัวแปร ชื่อ “delayTime” ก็น่าจะเอาไว้เก็บค่าเวลาที่จะให้ microcontroller หยุดนี่ง แต่ยังไม่กำหนดว่าให้เป็นค่าเท่าไร

unsigned long delayTime;

setup()

เริ่มด้วย เชื่อมต่อการสื่อสารกับ computer โดยใช้ฟังก์ชั้น Serial ที่ 9600 baud (baud คืออะไร) แล้วรอให้การเชื่อต่อเรียบร้อยก่อนที่จะไปต่อ จากตัวอย่างที่แล้ว “ก้าวที่สอง” ไม่มีคำสั่งนี้ เพราะหลังจากที่เราสั่งให้เริ่มการเชื่อมต่อแล้ว เราให้ microcontroller ทำอย่างอื่นต่อไปก่อนที่เราจะใช้การสื่อสาร จึงไม่จำเป็นต้องรอให้เชื่อมต่อเสร็จ แต่ในที่นี้ ตัวอย่างจะใช้การสื่อสารในคำสั่งต่อไปทันที เราจึงต้องให้ microcontroller หยุดรอให้การเชื่อมต่อเสร็จสิ้นก่อนที่จะเรียกใช้คำสั่งในการสื่อสารต่อไป

    Serial.begin(9600);
    while(!Serial);    // time to get serial running

เมื่อการเชื่อต่อเสร็จสิ้นแล้ว คำสั่งถัดมาคือ สั่งให้ microcontroller ส่งข้อมูล “BME280 test” กลับไปยัง computer ซึ่งจะแสดงที่หน้าต่าง serial monitor

    Serial.println(F("BME280 test"));

ต่อมาเป็น การประกาศตัวแปร status ขึ้นมา แล้วนำตัวแปรที่เพิ่งประกาศมารับค่าที่ส่งกลับมาจาก bme.begin() ซึ่งเป็นการสั่งให้เริ่มเตรียมพร้อมที่จะใช้งาน sensor BME280 (รายละเอียดของ function อยู่ใน library)

    unsigned status;
    
    // default settings
    status = bme.begin();  

แล้วตรวจดูว่า ผลที่ส่งมาให้กับ status เป็นอย่างไร ถ้าเริ่มเตรียมตัวไม่สำเร็จก็จะ ส่งข้อมูลใน if ไปที่ computer แล้วหยุดที่ while(1) delay10; ถ้าเข้าใจ ภาษา C ก็จะทราบว่า microcontroller จะหยุดอยู่ตรงนี้ ไม่ไปคำสั่งอื่นต่อแล้วจนกว่าจะถูก reset

    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
        Serial.print("SensorID was: 0x"); Serial.println(bme.sensorID(),16);
        Serial.print("        ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
        Serial.print("   ID of 0x56-0x58 represents a BMP 280,\n");
        Serial.print("        ID of 0x60 represents a BME 280.\n");
        Serial.print("        ID of 0x61 represents a BME 680.\n");
        while (1) delay(10);
    }

ถ้าการเริ่มต้น ไม่มีปัญหาอะไร ก็จะส่งข้อมูล “–Default Test –” ไปที่แสดงที่ computer

    Serial.println("-- Default Test --");

กำหนดค่า 1000 ให้กับ ตัวแปร delayTime ซึ่งก่อนหน้านี้ไม่ได้กำหนด

    delayTime = 1000;

แล้วส่งบรรทัดว่าง ๆ 1 บรรทัดไปแสดงที่ computer

    Serial.println();

loop()

ใน loop นี้ไม่มีอะไรมากนอกจาก การเรียก function 2 คือ printValue() และ delay(1000) ซึ่งเป็นการสั่งให้หยุดนิ่ง 1000 ms ส่วน printValue() คืออะไร ก็ต้องไปดูรายละเอียดที่ function printValue() ที่อยู่ด้านล่าง

void loop() { 
    printValues();
    delay(delayTime);
}

printValues()

เริ่มแรกเป็นการส่งข้อมูล “Temperature = “ ไปที่ computer 

    Serial.print("Temperature = ");

ต่อมาเป็นการส่งข้อมูล bme.readTemperature() ไปที่ computer (รายละเอียดของคำสั่งนี้ก็อยู่ใน library) ซึ่งข้อมูลนี้ก็คือ ให้อ่านค่าอุณหภูมิจาก sensor ดังนั้นบรรทัดที่สองนี้จึงมีสองคำสั่งซ้อนกันอยู่ คืออ่านค่าอุณหภูมิจากsensor แล้ว ส่งให้ computer ด้วยคำสั่ง Serial.print() 

    Serial.print(bme.readTemperature());

จากนั้นก็ส่ง “ °C” ไปที่ computer 

    Serial.println(" °C");

สั่งเกตว่า คำสั่งสุดท้ายนี้ต่างจากสองคำสั่งก่อนหน้า คือ print() กับ println() โดยที่ print() เป็นการส่งข้อมูลที่อยู่ในวงเล็บเท่านั้น แต่ println() คือส่งข้อมูลในวงเล็บแล้วตามด้วยข้อมูลที่บอกให้ขึ้นบรรทัดใหม่ด้วย (‘\r’ และ ‘\n’) 

คำสั่งสามบรรทัดแรกนี้ เป็นชุดที่เป็นการส่งค่าอุณหภูมิไปให้ computer นั่นเอง แล้วมีการอ่านค่าอื่น ๆ แล้วส่งให้ computer ด้วย รูปแบบคำสั่งที่ซ้ำๆ กัน อีก 3 ชุด แล้วปิดท้ายด้วย การส่งข้อมูลให้ขึ้นบรรทัดใหม่

    Serial.print("Pressure = ");

    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");

    Serial.print("Approx. Altitude = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");

    Serial.print("Humidity = ");
    Serial.print(bme.readHumidity());
    Serial.println(" %");

    Serial.println();

Output

ให้กด upload แล้วดู output ที่ Serial monitor โดยกดที่เครื่องหมายคล้ายแว่นขยายที่มุมบนขวา หรือ ไปที่เมนู Tools -> Serial Monitor ก็จะได้หน้าต่าง Serial Monitor แล้ว ข้อมูลที่ส่งมาจาก microcontroller ก็จะมาแสดงที่นี่

BME280 output

สรุป

คราวนี้เราได้เรียนรู้

  1. เราสามารถใช้ library ช่วยในการติดต่อกับ sensor หรือ อุปกรณ์อื่น ๆ ได้
  2. เราสามารถเรียนรู้การใช้ library ต่าง ๆ จากตัวอย่างของ library เองได้
  3. เราสามารถส่งข้อมูลจาก microcontroller กลับไปที่ computer โดยใช้ Serial function ได้

ทุกคนสามารถใช้แนวทางนี้ในการศึกษาการต่อ sensor อื่น ๆ รวมทั้งอุปกรณ์อื่น ๆ ที่ไม่ใช่ sensorด้วย เช่น display ซึ่งจะทำให้อุปกรณ์ของเราน่าสนใจมากยิ่งขึ้น

อย่าลืมกด like ที่ Facebook Page เพื่อติดตามเรื่องราวใหม่ ๆ จากเรานะครับ