กลับไปหน้ารวมไฟล์
developing-an-ir-remote-and-software-controller-4fc954.md

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

เรามีชุดลำโพงติดผนัง Vision AV-1000 ที่ต่อกับโปรเจคเตอร์ EPSON อยู่ รีโมทโปรเจคเตอร์หาง่าย แต่รีโมท AV-1000 นี่เลิกผลิตไปแล้วครับพี่น้อง ลำโพงพวกนี้ไม่มีปุ่มควบคุมอะไรเลยสำหรับเลือกอินพุต ปรับเสียงแหลม/เสียงเบส ฯลฯ มีแค่ปุ่มปรับระดับเสียงด้านหลังเท่านั้น

ทางออกคืออะไรล่ะ?

เราตัดสินใจทำรีโมทลำโพงใช้เองครับ ราคาต้องถูกที่สุดเท่าที่จะทำได้

ไอเดียง่ายๆ คือเราจับสัญญาณอินฟราเรด (IR) จากรีโมทลำโพงตัวที่เหลืออยู่ แล้วบันทึกคำสั่งที่มันส่งออกมา

เราทำโดยการต่อ IR Receiver เข้ากับ Arduino ตัวรีซีฟเวอร์มี 3 ขา จากซ้ายไปขวาคือ GND, +5V, SIGNAL และใช้ไลบรารี Arduino IRRemote

รันตัวอย่าง Examples > IRRemote > IRrecvDumpv2 ดูรูปด้านล่างที่ต่อ IR Receiver กับ Arduino Uno (สำหรับทำโปรโตไทป์ จะใช้ Arduino nano ก็ได้นะ แต่ต้องอัพโหลด/รีเซ็ตสเก็ตช์ทุกครั้งตอนทดสอบ)

ถ้าทำถูกต้องแล้ว ให้เล็งรีโมทลำโพงไปที่ตัวรับสัญญาณแล้วกดปุ่ม – เมื่อ IRReceiver ได้รับข้อมูล มันจะกระพริบด้วย LED สีแดงบนบอร์ด แสดงว่ามันทำงานแล้วจ้า

เปิด Arduino serial monitor ขึ้นมา แล้วคุณควรจะเห็นข้อมูลที่มันได้รับออกมา (ดูรูปด้านล่าง)

โชคดีของเรา โปรโตคอลที่รีโมทลำโพงใช้ถูกระบุว่าเป็นโปรโตคอล NEC 32 บิต ไลบรารี IRRemote มีฟังก์ชันในตัวสำหรับส่งข้อมูลนี้ออกมาใหม่ ตอนนี้สิ่งที่เราต้องทำคือไล่กดปุ่มทุกปุ่มบนรีโมทลำโพงแล้วบันทึกรหัสของมัน (ข้อมูลด้านล่าง)

RESET: 827D58A7

MUTE: 827DD827

INPUT 1: 827D708F

INPUT 2: 827D48B7

TREBLE UP: 827DF20D

TREBLE DOWN: 827DCA35

BASS UP: 827DF00F

BASS DOWN: 827DC837

VOLUME DOWN: 827D40BF

VOLUME UP: 827DC03F

เราก็ได้ข้อมูลที่จำเป็นสำหรับเลียนแบบสัญญาณ IR ของรีโมทแล้ว นั่นคือทั้งหมดที่เราต้องการจาก IR Receiver ครับ เพราะงั้นจากนี้ไปเราจะถอดมันออก แล้วไปต่อกับโปรเจคที่เหลือ

เพื่อให้เราสื่อสารกับมันได้ เราคิดว่าการมีซอฟต์แวร์บน PC เป็นวิธีที่ดีที่สุด เราจะทำรีโมทอีกอันก็ได้ แต่มันก็ต้องหาย/วางผิดที่อยู่ดีแหละ

ขั้นตอนต่อไปคือต่อ IR LED และตัวต้านทาน (Resistor) เข้ากับบอร์ด Arduino ไลบรารี IRRemote จะส่งสัญญาณออกทาง Pin 3 เป็นวงจรพื้นฐานมากๆ ในการต่อพวกนี้เข้าด้วยกัน โดยใช้ Arduino nano ฉันบัดกรีตัวต้านทานเข้ากับขาข้างหนึ่งของ IR LED และอีกข้างต่อตรงไปที่ Pin บนบอร์ด มันทำงานได้สมบูรณ์แบบเลย เพราะคุณสามารถกด LED ลงบนบอร์ดให้แน่นได้

เราจะใช้โปรแกรม VB.NET ง่ายๆ ในการสื่อสารกับ Arduino ซึ่งจะเขียนไปยังพอร์ตอนุกรมและส่งไบต์ – ในสเก็ตช์ Arduino เราจะตรวจสอบพอร์ตอนุกรมสำหรับไบต์ขาเข้าและจับคู่กับรหัสที่จะส่งผ่าน IR ง่ายๆ เอง!

สเก็ตช์ Arduino ของเรา

/*
 * IRremote: AV_Controller
 * An IR LED must be connected to Arduino PWM pin 3.
 * Version 0.1 01/04/16
 */

#include <IRremote.h>

IRsend irsend;
byte byteRead;

void(* resetFunc) (void) = 0; //declare reset function @ address 0

void setup() {
  Serial.begin(9600);
  Serial.println("***********************");
  Serial.println("**   AV-CONTROLLER   **");
  Serial.println("***********************");
  Serial.println("Waiting for commands...");
}

void loop() {
  if (Serial.available()) {
    byteRead = Serial.read();
    processResponse(byteRead);
  }
}

void processResponse(byte byteInput) {
  switch (byteInput) {
   case '4':
      Serial.println("Sending Command: TEST 4"); 
       // irsend.sendNEC(0x827DD827, 32);
        delay(40);
      return;
      break;         
   case 'a':
      Serial.println("Sending Command: RESET");
      irsend.sendNEC(0x827D58A7, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'b':
      Serial.println("Sending Command: MUTE");
      irsend.sendNEC(0x827DD827, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'c':
      Serial.println("Sending Command: INPUT1");
      irsend.sendNEC(0x827D708F, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'd':
      Serial.println("Sending Command: INPUT2");
      irsend.sendNEC(0x827D48B7, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'e':
      Serial.println("Sending Command: TREBLE UP");
      irsend.sendNEC(0x827DF20D, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'f':
      Serial.println("Sending Command: TREBLE DOWN");
      irsend.sendNEC(0x827DCA35, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'g':
      Serial.println("Sending Command: BASS UP");
      irsend.sendNEC(0x827DF00F, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'h':
      Serial.println("Sending Command: BASS DOWN");
      irsend.sendNEC(0x827DC837, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'i':
      Serial.println("Sending Command: VOLUME DOWN");
      irsend.sendNEC(0x827D40BF, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'j':
      Serial.println("Sending Command: VOLUME UP");
      irsend.sendNEC(0x827DC03F, 32);
      delay(40);
      Serial.println("Command Sent.");
      return;
      break;
    case 'x':
      Serial.println("Resetting System...");
      resetFunc();
      return;
      break;

      break;
    case 't':
      Serial.println("OK");
      return;
      break;

      break;
    default: 

    break;
  }
  Serial.println("Unknown command.");
}

เบสิคของโค้ดก็ประมาณนี้แหละน้อง เราตรวจจับข้อมูลที่เข้ามาทางพอร์ต Serial ถ้าตัวอักษรที่เข้ามาตรงกับที่เราตั้งไว้ใน switch case เราก็จะส่งคำสั่ง IR ที่ตรงกันออกไป ถ้าไม่ตรง ก็โยนข้อความ "Unknown command." ออกมาให้ดู

เราสามารถทดสอบโค้ดด้านบนได้โดยการเชื่อมต่อกับพอร์ต COM ของเราด้วยโปรแกรม PuTTy เปิด PuTTy ขึ้นมาแล้วใส่หมายเลขพอร์ต COM ที่เจอ Arduino อยู่ พอเชื่อมต่อแล้วน้องควรจะเห็นข้อความจาก Serial.println ในฟังก์ชัน setup() โผล่มา

ลองพิมพ์ตัวอักษร เช่น 'a' เพื่อส่งคำสั่ง RESET ดูสิ น้องควรจะเห็นผลลัพธ์โผล่มา จริงๆแล้วน้องทดสอบกับลำโพงเซ็ตนั้นได้เลย มันควรจะรีเซ็ตระดับเสียง/เบส/ทริเบิล ฯลฯ กลับไปตั้งต้น ดูผลลัพธ์ที่คาดหวังได้จากภาพด้านล่างเลย

PuTTy

โอเค ตอนนี้เรามีแอปที่ทำงานได้แล้ว แต่หน้าตามันยังไม่น่าใช้เลย เราไปคาดหวังให้คนมานั่งเปิด PuTTy เข้าไปส่งคำสั่งทาง Serial ได้ยังไงล่ะ? ต่อไปเราจะมาจัดการออกแบบแอปพลิเคชันสำหรับเดสก์ท็อปให้มันดูดีกัน สู้งานนะน้อง!

แอปพลิเคชัน .NET

อย่างแรกที่ต้องทำคือคุยกับพอร์ตอนุกรม (Serial Port) จริงๆ เราเพิ่งเขียนบทความเกี่ยวกับเรื่องนี้ไปหยกๆ เอง (แต่ลิงก์หายไปแล้ววว) หลักการคือเราจะตรวจจับอัตโนมัติว่าต้องใช้ COM พอร์ตไหน โดยการส่งสตริงตรวจสอบไปถามหา แอปจะไล่ตรวจสอบ COM พอร์ตที่ใช้ได้ทั้งหมด แล้วส่งข้อความ 't' ไป ถ้าพอร์ตไหนตอบกลับมาว่า 'OK' นั่นแหละพอร์ตที่ถูกต้อง! ช่วยเซฟน้องๆ ไม่ต้องมานั่งเลือกพอร์ตเองให้เมื่อยมือ

การส่งคำสั่งต่างๆ เราจัดให้เป็นฟังก์ชันเล็กๆ ชื่อ sendCommand ด้านล่างเป็นโค้ด .NET (มันยังมีอัพเดตสี Picturebox ด้วย เพื่อจำลองการกะพริบของรีโมท LED)

 Sub sendCommand(data As String)
        PictureBox2.BackColor = Color.Green
        If connected = True Then
            If data <> "" Then
                Try
                    Using com As IO.Ports.SerialPort =
                   My.Computer.Ports.OpenSerialPort(comPort)
                        com.WriteLine(data)
                        com.Close()
                    End Using
                Catch ex As Exception
                    MessageBox.Show(ex.Message)
                End Try
            End If
        End If
        PictureBox2.BackColor = Color.White
    End Sub

ส่วนโค้ดที่เหลือก็ง่ายมาก แค่เรียกใช้ sendCommand('a') หรือสตริงอะไรก็ตามที่น้องอยากส่งไปหา COM พอร์ต ที่เหลือของแอปคือ GUI สวยๆ ฮะๆ ด้านล่างมีโค้ดเต็มและภาพหน้าจอแอปพลิเคชันตอนรัน

โค้ดเต็มของแอปพลิเคชัน .NET อยู่ด้านล่างนี้เลยจ้า พัฒนาด้วย VS 2015 Community

Imports System.Threading

Public Class Form1
    Dim connected As Boolean
    Dim comPort As String

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        portConfig()
    End Sub

    Sub portConfig()
        For Each p As String In My.Computer.Ports.SerialPortNames
            Try
                Using com As IO.Ports.SerialPort =
               My.Computer.Ports.OpenSerialPort(p)
                    com.ReadTimeout = 1000
                    com.WriteLine("t")
                    Dim data As String = com.ReadLine()
                    If InStr(data, "OK") Then
                        lblConnection.Text = "Connected to " & p
                        comPort = p
                        connected = True
                    End If
                    com.Close()
                End Using
            Catch ex As Exception

            End Try
        Next
    End Sub

    Sub sendCommand(data As String)
        PictureBox2.BackColor = Color.Green
        If connected = True Then
            If data &lt;&gt; "" Then
                Try
                    Using com As IO.Ports.SerialPort =
                   My.Computer.Ports.OpenSerialPort(comPort)
                        com.WriteLine(data)
                        com.Close()
                    End Using
                Catch ex As Exception
                    MessageBox.Show(ex.Message)
                End Try
            End If
        End If
        PictureBox2.BackColor = Color.White
    End Sub

    Private Sub ConnectToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ConnectToolStripMenuItem.Click
        portConfig()
    End Sub

    Private Sub ResetDeviceToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ResetDeviceToolStripMenuItem.Click
        sendCommand("x")
    End Sub

    Private Sub ExitToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ExitToolStripMenuItem.Click
        Application.Exit()
        End
    End Sub

    Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
        Label1.Parent = PictureBox1
        Label1.BackColor = Color.Transparent

        Label2.Parent = PictureBox1
        Label2.BackColor = Color.Transparent

        Label3.Parent = PictureBox1

Label3.BackColor = Color.Transparent

    Label4.Parent = PictureBox1
    Label4.BackColor = Color.Transparent

    Label5.Parent = PictureBox1
    Label5.BackColor = Color.Transparent

    Label6.Parent = PictureBox1
    Label6.BackColor = Color.Transparent

    Label7.Parent = PictureBox1
    Label7.BackColor = Color.Transparent

    Label8.Parent = PictureBox1
    Label8.BackColor = Color.Transparent

    Label9.Parent = PictureBox1
    Label9.BackColor = Color.Transparent

    Label10.Parent = PictureBox1
    Label10.BackColor = Color.Transparent
End Sub

Private Sub Label1_Click(sender As Object, e As EventArgs) Handles Label1.Click
    sendCommand("a")
End Sub

Private Sub Label2_Click(sender As Object, e As EventArgs) Handles Label2.Click
    sendCommand("b")
End Sub

Private Sub Label3_Click(sender As Object, e As EventArgs) Handles Label3.Click
    sendCommand("c")
End Sub

Private Sub Label4_Click(sender As Object, e As EventArgs) Handles Label4.Click
    sendCommand("d")
End Sub

Private Sub Label5_Click(sender As Object, e As EventArgs) Handles Label5.Click
    sendCommand("e")
End Sub

Private Sub Label6_Click(sender As Object, e As EventArgs) Handles Label6.Click
    sendCommand("f")
End Sub

Private Sub Label7_Click(sender As Object, e As EventArgs) Handles Label7.Click
    sendCommand("g")
End Sub

Private Sub Label8_Click(sender As Object, e As EventArgs) Handles Label8.Click
    sendCommand("h")
End Sub

Private Sub Label9_Click(sender As Object, e As EventArgs) Handles Label9.Click
    sendCommand("j")
End Sub

Private Sub Label10_Click(sender As Object, e As EventArgs) Handles Label10.Click
    sendCommand("i")
End Sub

Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles Me.Resize
    If Me.WindowState = FormWindowState.Minimized Then
        NotifyIcon1.Visible = True
        NotifyIcon1.Icon = SystemIcons.Application
        NotifyIcon1.BalloonTipIcon = ToolTipIcon.Info
        NotifyIcon1.BalloonTipTitle = "AV Controller"
        NotifyIcon1.BalloonTipText = "Controller is minimized. Click the icon in the tray to open."
        NotifyIcon1.ShowBalloonTip(50000)
        ShowInTaskbar = False
    End If
End Sub

Private Sub NotifyIcon1_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles NotifyIcon1.MouseDoubleClick
    ShowInTaskbar = True
    Me.WindowState = FormWindowState.Normal
    NotifyIcon1.Visible = False
End Sub

End Class


### การถอดรหัสสัญญาณอินฟราเรด (Infrared Protocol Decoding)
โปรเจคนี้คือการเปลี่ยนรีโมททีวีธรรมดาๆ ให้กลายเป็นตัวควบคุมอเนกประสงค์สำหรับ PC หรือระบบอัตโนมัติในบ้านของเราเอง ง่ายๆ แต่มันส์จัดไปวัยรุ่น!

-   **การถอดรหัสความกว้างพัลส์ (Pulse Width Demodulation)**: เริ่มจาก **ตัวรับสัญญาณ IR (เช่น VS1838b)** มันจะจับพัลส์อินฟราเรดความถี่ 38kHz ที่ส่งมาจากรีโมท จากนั้น Arduino ก็จะใช้ไลบรารี่ `IRremote` ในการถอดรหัสช่วงเวลาพัลส์เหล่านั้นให้กลายเป็นรหัสเลขฐานสิบหกเฉพาะตัว (เช่น `0xFFA25D`) เรียบร้อย!
-   **การแมปปุ่มควบคุม (Control mapping)**: Arduino ทำหน้าที่เป็นสะพานเชื่อมต่อ พอเห็นรหัสของปุ่ม "Play" มันก็จะส่งข้อความ (serial string) เฉพาะกลับไปยังคอมพิวเตอร์โฮสต์ ง่ายเหมือนปอกกล้วยเข้าปากเลยน้อง ห้ามช็อตนะตัวนี้

### หน้าต่างแอปพลิเคชันบนวินโดวส์
- **Visual Studio 2015**: เขียนแอปตัวช่วยด้วย C# หรือ VB.NET ให้มันคอยฟังพอร์ต COM อยู่
- **ซอฟต์แวร์ออโตเมชั่น**: แอปบนพีซีจะแปลงรหัสอินฟราเรดเป็นคำสั่งระบบ (เช่น ปรับเสียง, เปิดเบราว์เซอร์, หรือควบคุมพาวเวอร์พอยท์) ทำให้รีโมตอินฟราเรดราคาถูกๆ กลายเป็นอุปกรณ์อินพุตซอฟต์แวร์สุดแกร่งไปเลย

### เรื่องของเคส
ตอนนี้ตัวอุปกรณ์ทำงานได้แล้ว ซอฟต์แวร์ควบคุมก็พร้อม ถึงเวลาคิดแล้วว่าเราจะห่อหุ้มมันยังไง ถ้าปล่อยให้มันโป๊เปลือยแบบนี้ เดี๋ยวก็มีน้ำหกใส่แน่ๆ

เราเลยลงมือออกแบบด้วย 2D Design สร้างเคสแบบง่ายๆ ขึ้นมา

<figure><img src= /><figcaption> </figcaption></figure>

จากภาพสกรีนช็อตโปรแกรม 2D Design ทางซ้ายมือจะเห็นว่าเป็นดีไซน์พื้นฐานมาก ใช้สกรูพลาสติกยึดต่อชิ้นส่วนเข้าด้วยกัน (ลอดผ่านรูพวกนั้น) ส่วนผลงานหลังตัดเลเซอร์เสร็จดูด้านล่างนี้เลย:

### ผลงานชิ้นโบว์แดง

<div id="gallery"><img id="gallery" src= /><figcaption> </figcaption><img id="gallery" src= /><figcaption> </figcaption></div>

สรุปสิ่งที่เราทำสำเร็จในโปรเจกต์นี้:

- จับข้อมูลอินฟราเรดจากรีโมตเดิมของเราได้
- พัฒนาอุปกรณ์ (Arduino & IR LED) สำหรับส่งข้อมูลนั้นกลับออกไปใหม่
- ออกแบบเคสให้มันนั่งอยู่ข้างพีซีได้อย่างสวยงามไม่เกะกะ

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

ต้นทุนรวมของโปรเจกต์ประมาณ 4 ปอนด์เท่านั้น (ส่วนประกอบสั่งจาก eBay เป็นหลัก) ถูกใจวัยรุ่นไหมล่ะ! สู้งานนะน้อง

ข้อมูล Frontmatter ดั้งเดิม

apps:
  - "1x Visual Studio 2015"
author: "hwhacks"
category: "Home & Automation"
components:
  - "1x Laser cutter (generic)"
  - "1x Arduino Nano R3"
  - "1x Resistor 330 ohm"
  - "1x Infrared LED 940nm"
description: "มาสร้างรีโมตอินฟราเรด (IR) แทนตัวสำหรับลำโพงกับโปรเจคเตอร์กัน! งานนี้เราจะใช้ .NET มาควบคุม Arduino ให้ยิงสัญญาณ IR ได้ตามที่เราสั่ง ผ่านแอปในคอมเลย ง่ายๆ แต่เท่มาก แบบว่าจัดไป!"
difficulty: "Easy"
documentationLinks: []
downloadableFiles:
  - "https://github.com/z3t0/Arduino-IRremote"
  - "https://github.com/z3t0/Arduino-IRremote"
encryptedPayload: "U2FsdGVkX19UBI7L9tSE/okD8w3JPgIEt0EeinMRG/4vYEn3TxuK+e++b1OBD1sHwU0x80e/pusgCYGvUrBx6tztcbKF9oXIRKAZKDLgAzc="
heroImage: "https://cdn.jsdelivr.net/gh/bigboxthailand/arduino-assets@main/images/projects/developing-an-ir-remote-and-software-controller-4fc954_cover.jpg"
lang: "en"
likes: 12
passwordHash: "4f62a000178e1eded6f20c82327640ea896d415e1e5d2b362e124561f0f13e04"
price: 1120
seoDescription: "Learn to build an IR Remote for Speakers and Projectors controlled via a .NET application."
tags:
  - "infrared"
  - "home automation"
  - "remote control"
title: "สร้างรีโมต IR + คอนโทรลเลอร์จากโค้ด งานง่ายแต่หล่อ!"
tools: []
videoLinks: []
views: 29955