โครงการ LED แสดงผลภาษาจีน

เมื่อสองเดือนก่อนเกิดปิ๊งไอเดียขึ้นมาว่าอยากทำ LED display ที่แสดงคำศัพท์ภาษาจีนวันละคำ ตั้งไว้ที่หัวเตียง เพื่อทบทวนศัพท์ไปในตัว ก็เลยลุย Arduino เพื่อหาวิธีทำ ซึ่งนับว่าเข็นครกขึ้นภูเขามากๆ กว่าจะพบว่ายังไม่สามารถทำได้ในเวลานี้

เนื่องจากว่า LED display จะแสดงภาษาจีนได้ก็ต่อเมื่อมันแสดง Unicode ได้ ซึ่งในเวลานี้มีแค่ไลบรารี่ที่ชื่อว่า u8g2 เท่านั้น ที่ support แต่ปัญหาต่อมาก็คือ u8g2 ใช้ได้กับ LED Display บางรุ่นเท่านั้น ซึ่งก็คือ ตระกูล MAX7219 (ลองซื้อ RGB LED Matrix Panel มา ก็เลยเสียเงินเปล่า ต้องไปซื้อ MAX7219 8×32 มาใหม่แทน)

แต่ก็พบว่าปัญหายังไม่จบ เพราะแม้ว่าผมจะทำให้ MAX7219 แสดงภาษาจีนได้ก็จริง แต่ฟอนต์ภาษาจีนที่เล็กที่สุด คือ 12 pixel height แต่ว่า MAX7219 สูงแค่ 8 pixel เท่านั้น ก็เลยแสดงภาษาจีนได้แบบแหว่งๆ

สุดท้ายแล้วต้องรอให้ u8g2 สนับสนุน LED Display รุ่นใหม่ๆ ที่สูงกว่า 12 pixel หรือไม่ก็มีฟอนต์จีนตัวใหม่ออกที่มีขนาดเล็กว่า 8 pixel ซึ่งคิดว่าแบบหลังนี่น่าจะยาก เพราะหนังสือจีนอาจไม่สามารถเขียนได้เล็กขนาดนั้น น่าจะต้องรอแบบแรกมากกว่า

สรุปแล้วต้องพับโครงการนี้ไปก่อน

ล่าสุดที่ทำให้แสดงผลภาษาจีนแบบแหว่งๆ ได้นั้น ผมใช้ Arduino Mega กับ u8g2 library โดยใช้ฟอนต์ u8g2_font_wqy12_t_chinese3 โดยใช้ตัวอย่างโค้ดจาก example ของ u8g2>full buffer>PrintUTF นั่นเอง

Reference

https://github.com/olikraus/u8g2/

 

รถหุ่นยนต์หลบสิ่งกีดขวาง

สิ่งหนึ่งที่่ค้างคาใจมาตั้งแต่เด็กคืองานอดิเรกด้านอิเล็กทรอนิกส์ ด้วยความที่เราเรียนจบมาทางนี้ ซึ่งที่ผ่านมาเราก็เคยเล่นๆ เลิกๆ เป็นพักๆ ปัญหาส่วนใหญ่ที่พบคือ พอเราต่อวงจรในแบบที่อยากได้เสร็จแล้ว เราก็จะไม่รู้ว่าจะต่อยอดมันได้ยังไง มันเหมือนเป็นของเล่นที่เล่นแล้วก็หยุดอยู่แค่นั้น หนนี้ก็เป็นอีกครั้งหนึ่งที่เรากลับมาหามันใหม่ ในยุคสมัยที่เปลี่ยนไป คือเข้าสู่โลกของ Robotics และ AI และรวมไปถึง IoT ก็ไม่รู้เหมือนกันว่าจะเล่นๆ เลิกๆ อีกเป็นรอบที่ล้านรึเปล่า เหอๆ

ก่อนหน้านี้ผมเลยเล่น Raspberry Pi อยู่ แต่ส่วนที่ไม่ค่อยชอบเกี่ยวกับมันคือ มันเป็นคอมพิวเตอร์ดีๆ นี่เอง มีระบบปฏิบัติการ ทำให้เวลาปิดเครื่องจะต้องส่งคำสั่งปิด ไม่เหมือนเครื่องใช้ไฟฟ้าที่สามารถเปิดปิดได้ทันที ทำให้มันไม่สะดวก และโดยเฉพาะอย่างยิ่ง เวลาไฟดับ ไฟตก ซึ่งบ้านเราไฟก็ไม่ได้นิ่งตลอดเวลาขนาดนั้น ถ้าเรามี Pi ไว้ทำงานอะไรสักอย่างเปิดทิ้งไว้ตลอดเวลา ก็จะต้องมี UPS ให้มันด้วย ไม่งั้นไม่รอดแน่ๆ ไฟตกทีเดียวไปหมด ทำให้รอบนี้ผมหันมาสนใจ Arduino และผองเพื่อนมากกว่า

โปรเจ็ค Arduino แรกที่อยากทำก็คือรถหุ่นยนต์นี่แหละ ตอนแรกนึกถึงรถที่บังคับไปมาได้ แต่ไปๆ มาๆ อยากให้มันมีความ autonomous สักนิด เหมาะกับยุคสมัย ก็เลยอยากได้รถที่วิ่งแล้วหลบสิ่งกีดขวางไปมาได้เองดีกว่า แม้จะยังไม่ใช่ machine learning อย่างแท้จริง เป็นแค่ logic ง่อยๆ ธรรมดา ก็ถือเป็นจุดเริ่มต้นที่ดี

Arduino Uno R3

ผมเลือกใช้ Arduino Uno R3 เป็นสมองของรถ ไม่ใช่อะไร มันเป็นรุ่นมาตรฐาน ในการขับเคลื่อน จำเป็นต้องมีสิ่งที่เรียกว่า L298N Motor Driver เพื่อให้มีกำลังไฟมาพอที่จะขับมอเตอร์ล้อได้ด้วย และมี ultrasonic sensor ที่ช่วย Detect สิ่งกีดขวางตรงหน้า เพื่อที่จะได้หลบได้ ในส่วนของล้อก็เลือกแบบง่ายที่สุดคือแค่ 2 ล้อ และเป็นล้อที่เลี้ยวง่ายๆ ด้วยการให้ล้อหนึ่งหมุนทวนเข็ม อีกล้อหมุนตามเข็ม ไม่ต้องมีพวงมาลัย หรือเพลาที่เอียงได้ให้ยุ่งยากอะไร โดยมอเตอร์เหลืองที่ใช้สามารถขับได้ด้วยไฟตรงตั้งแต่ 3-12V เลย และส่วนสุดท้ายที่เพิ่มเติมขึ้นมาคือ Sensor Shield ที่ไม่ได้มีประโยชน์อะไรแค่ทำให้ Arduino สามารถเชื่อมต่อกับอุปกรณ์ต่อพ่วงได้เยอะขึ้น เพราะมีพอร์ตมากขึ้น แค่นั้นเอง

DC Motor และล้อ
Sensor Shield V5

อุปกรณ์ที่ใช้

สิ่งที่ต้องเรียนรู้เพิ่มเติมในโลกของ Arduino คือ การเขียนโปรแกรมให้มันผ่าน Arduino IDE ซึ่งเป็นซอฟต์แวร์ สำหรับเขียนคำสั่งง่ายๆ คอมไพล์ แล้วอัพโหลดเข้าไปในตัว Arduino ผ่านสาย USB ทักษะอีกอย่างหนึ่งที่ต้องมีเวลาเล่น Arduino คือ การบัดกรี เพื่อเชื่อมต่อสายไฟต่างๆ เข้าด้วยกันแบบถาวร ซึ่งเลี่ยงไม่ได้ เพราะไม่ใช่ทุกส่วนจะต่อเข้าหากันผ่านสายจัมเปอร์ได้หมด บางส่วนก็ยังจำเป็นต้องบัดกรีอยู่ เช่น ส่วนที่ต่อกับกะบะถ่านไฟฉาย หรือมอเตอร์ และสวิตซ์ปิดเปิดเป็นต้น ดังนั้นควรมีอุปกรณ์บัดกรีไว้ประจำตัว และระมัดระวังเรื่องการสูดดมควัน ควรทำงานในที่มีอากาศถ่ายเทเสมอ และมีพัดลมช่วยเป่าควันที่เกิดขึ้นออกไปในทางที่ห่างตัวเรา รวมทั้งต้องมีคีมปลอกสายไฟ เพื่อให้ทำงานได้สะดวกและดูเรียบร้อยขึ้นด้วย

L298N

L298N Motor Driver

สำหรับตัวขับมอเตอร์ตัวนี้ สามารถขับมอเตอร์ให้เคลื่อนไหวได้ต่างกันสองตัว (Out 1,Out 2) โดยใช้ไฟจากแหล่งจ่ายได้ Vcc  และยังจ่ายไฟออกไปเลี้ยง Arduino ได้ด้วยทางพอร์ต 5V ทำให้ไม่ต้องมีแหล่งจ่ายไฟหลายแหล่ง (ต้องใส่ Jumper 5VEN ไว้เมื่อต้องการให้พอร์ต 5V กลายเป็นแหล่งจ่ายไฟออก) ส่วนการบังคับล้อก็ทำผ่านพอร์ตสัญญาณ (IN 1,2,3,4, ENA, ENB) ซึ่งสามารถปรับได้ทั้งทิศทางและความเร็วรอบ  รายละเอียดของการใช้งานสามารถดูได้จาก ที่นี่

Ultrasonic Sensor 

หลักการของ sensor ตัวนี้คือมันจะส่งคลื่นอินฟาเรดออกไปให้สะท้อนกลับมา แล้วคำนวณหาว่ามีสิ่งกีดขวางอยู่ข้างหน้าหรือไม่ โดยวัดเวลาที่ใช้ในการสะท้อนกลับ ซึ่งเสียงมีความเร็ว 340 เมตรต่อวินาที เดินทางไปกลับ เท่ากับว่า ถ้าใช้เวลาเดินทาง T วินาที ก็เท่ากับมีสิ่งกีดขวางอยู่ข้างหน้าห่างออกไป (34x T)/2  เป็นต้น

ภาพวงจรเชื่อมต่อ

ตรรกของโปรแกรมก็ง่ายๆ หุ่นยนต์จะวิ่งไปข้างหน้าเสมอ แต่เมื่อไรก็ตามที่จับสิ่งกีดขวางตรงหน้าที่ห่างออกไปไม่เกิน 15 ซม.ได้ จะทำการถอยหลัง 15 ซม. แล้วหมุนตัวโดยสุ่มทิศทาง แล้วค่อยวิ่งไปข้างหน้าเหมือนเดิม ถ้าหากเจอสิ่งกีดขวางอีกก็ทำแบบเดิมอีก ไปเรื่อยๆ แค่นั้นเลย

ตัวอย่าง code ที่ใช้

// Motor A connections
int enA = 9;
int in1 = 8;
int in2 = 7;
// Motor B connections
int enB = 3;
int in3 = 5;
int in4 = 4;

int trigPin = 11; // TRIG pin
int echoPin = 10; // ECHO pin

float duration_us, distance_cm;

void setup() {
// Set all the motor control pins to outputs
pinMode(enA, OUTPUT);
pinMode(enB, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);

// Turn off motors – Initial state
digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
digitalWrite(in3, LOW);
digitalWrite(in4, LOW);

Serial.begin (9600);

// configure the trigger pin to output mode
pinMode(trigPin, OUTPUT);
// configure the echo pin to input mode
pinMode(echoPin, INPUT);

}

void loop() {

if (isClear()) {

// Set Speed
analogWrite(enA, 128);
analogWrite(enB, 128);

// Turn on motor A & B
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
digitalWrite(in3, LOW);
digitalWrite(in4, HIGH);

} else {

changeDirection();

}
delay(500);

}

// This function lets you control spinning direction of motors
void changeDirection() {

digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);
delay(1000);

digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
digitalWrite(in3, LOW);
digitalWrite(in4, HIGH);
delay(1500);

}

bool isClear() {
// generate 10-microsecond pulse to TRIG pin
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

// measure duration of pulse from ECHO pin
duration_us = pulseIn(echoPin, HIGH);

// calculate the distance
distance_cm = 0.017 * duration_us;

if (distance_cm<15) {
return false; }
else {
return true;
}

}