Application ควบคุมหลอดไฟ LED ด้วย GSM Module โดยใช้ DTMF

Application ควบคุมหลอดไฟ LED ด้วย GSM Module โดยใช้ DTMF(Dual-Tone Multi-Frequency)

<<< Download Source Code >>  

Application Control LED by GSM Module using DTMF_By ThaiEasyElec_19112013.zip

DTMF เป็นโหมดการใช้งานพื้นฐานที่มีอยู่ใน GSM Module ทั่วไปรวมทั้ง GSM Module รุ่น M10 ของ บริษัท Quectel โหมด DTMF คือ การตรวจจับสัญญาณเสียงการกด Key ของโทรศัพท์ แล้วนำมาถอดรหัส ทำให้ทราบว่าปลายสายกำลังกดหมายเลขใดบนแป้น Key ด้วยความสามารถนี้

เราจึงนำเอามาสร้าง Application ง่ายๆ ในการควบคุมระยะไกล เปิดปิด อุปกรณ์ เครื่องใช้ต่างๆ ได้ การทดลองนี้เป็นการสาธิตการใช้งาน DTMF กับ GSM Module รุ่น M10 เพื่อให้ผู้ศึกษาได้เข้าใจรูปแบบการใช้งานเบื้องต้น

1
032 wm9
3

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

1. บอร์ด Arduino Mega 2560 (EADN015)                        1 บอร์ด 

2. GSM Module M10 (ETEE039A: TEE-EVB-M10 V2.0)     1 โมดูล

3. MP3 Module (ETEE043)                                            1 โมดูล

4. หลอด LED                                                              4 หลอด

5. ตัวต้านทาน 4.7K                                                    4 ตัว

วิธีการต่อวงจร:

m10 dtmf led

เตรียม SD Card ให้กับ Module MP3 >> Copy File ใน Folder Sound ทั้งหมดลงใน SD Card >> ใส่ SD Card เข้ากับ Module MP3 (ETEE043)

5

Code Program: 

#include <SoftwareSerial.h>
char c=0;
char str[40];
char cnt=0;
int busy = 2;
int LED1 = 3;
int LED2 = 4;
int LED3 = 5;
int LED4 = 6;
String password="1234";
char no_carroer=0;
String gen_dtmf[4] ={"AT+QWDTMF=7,0,\"", ",50,50\""};
void setup() 
{
  pinMode(busy, INPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  digitalWrite(LED1,HIGH);
  digitalWrite(LED2,HIGH);
  digitalWrite(LED3,HIGH);
  digitalWrite(LED4,HIGH);
  Serial.begin(9600);//PC
  Serial.flush();
  delay(500);
  Serial.println("DTMF Demo");
  Serial2.begin(9600);//GSM M10
  Serial1.begin(9600);//MP3 Module
  Serial2.flush();
  delay(1000);
  Serial2.println("AT");
  delay(1000);
  Serial2.println("ATE0");//Close Echo M10
  delay(1000);
  Serial2.println("AT+QTONEDET=1");//Open Detect DTMF M10
  Serial1.write(215);//Set volume MP3
  Serial.println("Ready");
}

คำอธิบาย ส่วน Function Setup

        ส่วน Setup ในส่วนแรกเป็นการ ตังค่า Direction ของ Pin ดังนี้ – กำหนดให้ขา Pin Busy(2) เป็น Input และ กำหนดให้ Pin LED1(3), Pin LED2(4), Pin LED3(5), Pin LED4(6) เป็น Output

void loop() // run over and over
{
    wait_Ring();
    wait_password();
    process_led();
}

        ในส่วนต่อมาจะเป็นการตั้งค่า Serial Port โดยให้ Serial ซึ่งต่ออยู่กับ PC รับส่ง Data ด้วย Baud rate 9600 bps    Serial2 ซึ่งต่ออยู่กับ GSM Module M10 รับส่ง Data ด้วย Baud rate 9600 bps   Serial3  ซึ่งต่ออยู่กับ MP3 Module รับส่ง Data ด้วย Baud rate 9600 bps

        ส่วนสุดท้ายเป็นการตั้งค่าเริ่มต้นให้กับ Module M10 ดังนี้

        Serial2.println(“ATE0”); = ปิด Echo เพื่อไม่ให้ M10 ทวนค่าที่เราส่งเข้าไปออกมา

        Serial2.println(“AT+QTONEDET=1”);= เปิดการตรวจจับ DTMF

void loop() // run over and over
{
    wait_Ring();
    wait_password();
    process_led();
}

คำอธิบาย ส่วน Function loop

       Loop เป็น Function ที่ทำงานเป็นวนรอบไม่รู้จบ โดยจะเริ่มทำงานใน Code บรรทัดแรก ไปยังบรรทัดสุดท้าย วนกันไปเช่นนี้ ใน Loop จะมีการเรียก Function อยู่ด้วยกัน 3Function คือ

       wait_Ring();  = รอ User โทรเข้ามาใช้งานระบบ

       wait_password();= รอ User ในรหัสผ่าน 

       process_led(); = ส่วนรับคำสั่ง และ เปิดปิด LED

String rx_m10(void)
{
     if (Serial2.available())
     {
       c = Serial2.read();
       if((c != 0x0D)&&(c != 0x0A))
       {
         str[cnt] = c;
         cnt++;
       }
       else
       {
          str[cnt]=0;
          String data(str);
          for(char i=0;i<40;i++)
         {
           str[i]=0;
         }
         cnt=0;
         return(data);
       }
      }
      return("");
}

คำอธิบาย ส่วน Function rx_m10

          rx_m10  เป็น Function ที่ใช้รอรับ Data ที่ GSM Module M10 ส่งเข้ามายัง Arduino

          ในส่วนแรกจะมีการเช็คว่า มี Data เข้ามาทาง Serial2 และ พร้อมที่จะให้เข้าไปอ่านออกมาแล้วหรือไม่โดยใช้ โดยใช้คำสั่ง Serial2.available() เป็นตัวเช็ค จากนั้นอ่านค่า จาก Serial2 มาเก็บในตัวแปร c

          เช็คว่า Data ที่เข้ามาไม่ตรงกับ 0x0A และ 0x0D จึงเก็บ Data เข้าไปยังตัวแปร char Array ที่ชื่อ str และ เมื่อ Data ที่รับเข้ามาตรงกับ 0x0A และ 0x0D แล้วให้นำเอา Data ที่ถูกเก็บไว้ก่อนหน้านี้ในตัวแปร str แปลงเป็นตัวแปรชนิด String แล้ว Return ค่าออกไป 

          *** เราใช้ 0x0A และ 0x0D เป็นการเช็คเงื่อนไข เนื่องจากทุกครั้งที่ GSM Module M10 ส่งค่าต่างๆออกมา เมื่อจบ Data จะถูกปิดท้ายด้วย 0x0A และ 0x0D หรือ \r\n นั้นเอง…   

void wait_Ring(void)
{
 char RingConunt=0;
 char RingTime=3;
 no_carroer=0;
   while(1)
   {
          String data = rx_m10();
          if(data == "RING")
          {
            Serial.println("Ring");
            RingConunt++;
            if(RingConunt==RingTime)
            {
              Serial.println("operate");
              RingTime=0;
              Serial2.println("ATA");
              delay(500);
              Serial1.write(0x01);
              delay(10);
              while(!digitalRead(busy)){}
              return;
            }
          }
          if(data == "NO CARRIER")
          {
           RingConunt=0;
          }
    }
 }

คำอธิบาย ส่วน Function wait_Ring

           wait_Ring  เป็น Function ที่ทำหน้าที่ รอการโทรเข้ามาของผู้ใช้ ซึ่งจะเขียน เป็น ลูปวนรอบรอ รับค่า และ ใช้ Function rx_m10() คอยรับค่าจาก GSM Module  จากนั้นคอยตรวจเช็คว่าค่าที่ GSM Module ส่งมาตรงกับคำว่า “Ring” หรือไม่ เนื่องจาก เมื่อมีคนโทรเข้ามา M10 จะส่งคำว่า Ring ออกมา หากมีคำว่า Ring ส่งเข้ามาเท่ากับค่าที่กำหนดไว้ในตัวแปร RingTime ก็จะส่งคำสั่ง Serial2.println(“ATA”); เพื่อให้ GSM Module รับสายการโทรเข้า

           จากนั้น ส่งคำสั่ง  Serial1.write(0x01); เพื่อสังงาน MP3 Module ให้เล่นเสียงเพลงใน File ที่ชื่อ 001.mp3 ซึ่งได้บันทึกเสียง คำว่า “ยินดีต้อนรับเข้าสู่ระบบตัวอย่างระบบควบคุมหลอดไฟผ่านGSM Module M10 ค่ะ”

           และ มีการตรวจเช็คขา Busy ของ MP3 Module หากเป็น Logic=Low หมายถึง Module MP3 ได้เล่นเพลงจบแล้ว จึงออกจาก ลูป wait_Ring เพื่อไปทำงานใน ลูป wait_password ต่อไป

           สุดท้ายหากผู้ใช้วางสาย โมดูลจะส่งคำว่า “NO CARRIER” กลับมา เมื่อได้รับ “NO CARRIER” จะทำการ Setตัวแปร  RingConunt=0 เพื่อรอรับการโทรเข้ามาครั้งใหม่

void wait_password(void)
{
  char num_pass[10];
  char pass_cnt=0;
  Serial.flush();
  Serial2.flush();
  Serial1.write(0x02);
   while(1)
   {
          String data = rx_m10();
          if(data.substring(0,11)=="+QTONEDET: ")
          {
            String  pass = data.substring(11,13);
            char ps = pass.toInt();
            String turnDTMF = gen_dtmf[0]+ps+gen_dtmf[1];
            Serial2.println(turnDTMF);
           if(ps == '*')
           {
             pass_cnt=0;
             for(char i=0;i<10;i++)
             {
               num_pass[i]=0;
             }
           }
           else
           {
              num_pass[pass_cnt] = ps;
              pass_cnt++;
           }
if(pass_cnt > 3)
            {
               num_pass[pass_cnt]=0;
               String in_pass(num_pass);
               if(in_pass == password)
               {
                 Serial.print("TRUE");
                 Serial1.write(0x07);
                 Serial2.flush();
                 delay(10);
                 while(!digitalRead(busy)){}
                 return;
               }
               else
               {
                 Serial.print("False");
                 Serial1.write(0x06);
                 Serial2.flush();
                 delay(10);
                 while(!digitalRead(busy)){}
               }
                pass_cnt=0;
                 for(char i=0;i<10;i++)
                 {
                   num_pass[i]=0;
                 }
                Serial.flush();
                Serial.println("clearbuf");
             }
      }
      if(data == "NO CARRIER")
    {
       no_carroer=1;
       return;
    }
   }
} 

คำอธิบาย ส่วน Function wait_password

          wait_password  เป็น Function ที่ทำหน้าที่ รอรับการกดรหัสผ่านเพื่อเข้าไปสู่โหมดการเปิดปิดหลอด LED โดยโปรแกรมจะวนรอบรอรับ Data ที่ GSM Module ส่งเข้ามาและเช็คเงื่อนไข

if(data.substring(0,11)==”+QTONEDET: “) เป็นการเช็คว่า Data ที่รับเข้ามา ที่ตำแหน่ง

0 – 11 ตรงกับข้อความ “+QTONEDET: ” หรือไม่ เนื่องจากหาก GSM Module ตรวจจับได้ว่าปลายสายมีการกดหมายเลขบนแป้นคีย์ จะส่งข้อความ +QTONEDET: xx    ซึ่งค่า xx คือค่าของหมายเลขที่กด เช่น

กด 0 = +QTONEDET: 48

กด 1 = +QTONEDET: 49

กด 2 = +QTONEDET: 50

*** ตัวอย่างค่า 48 เป็นเลขฐาน10 หากแปลงเป็นฐาน 16 จะเท่ากับ 0x30 เมื่อเทียบกับตาราง ASCII จะมีค่าเท่ากับเลข ‘0’

         เมื่อเช็คข้อความ 11 ตัวแรกใน “+QTONEDET: xx” แล้วพบว่าตรงกับ “+QTONEDET: ” แล้ว  ในบรรทัดต่อมา  String  pass = data.substring(11,13); เป็นการตัดเอาข้อความตั้งแต่ตำแหน่งที่ 11 ถึง ตำแหน่งที่ 13 (ตำแหน่งที่เก็บหมายเลขที่ปลายทางกดแป้นคีย์) มาเก็บในตัวแปร ที่เป็น String ชื่อ pass จากนั้นใช้ pass.toInt(); แปลง String ให้กลายเป็นตัวเลขแล้วเก็บลงตัวแปร char ps   จากนั้นนำตัวแปร ps มาสร้างเป็น AT command เพื่อสั่งงาน GSM Module ให้ สร้างสัญญาณ DTMF ออกไปให้ปลายสายได้ยินเสียงโทน การกด key ของตัวเองเพื่อเป็นการตอบว่า ระบบรับรู้การกด Key แล้ว

*** คำสั่ง AT command ที่ใช้สร้างสัญญาณ DTMF คือ AT+QWDTMF=uplink volume,downlink volume,”DTMF Code,mutetime,playcode”

         จากนั้น ตรวจเช็คว่า ค่าในตัวแปร ps ตรงกับเลข * หรือ ไม่ถ้าหากใช่ ให้ทำการเคลีย ค่าตัวแปร  pass_cnt, num_pass เท่ากับ 0 เพื่อรอรับการใส่ password ใหม่ (ใช้ในกรณีที่กด Password ผิด)

         หากค่าในตัวแปร ps ไม่เท่ากับ * ให้เก็บลงในตัวแปร char array num_pass หากมีการกด password เข้ามาครบ 4 ตัวแล้ว แปลง password ที่เก็บในตัวแปร char array num_pass เป็นตัวแปร String in_pass จากนั้น เปรียบเทียบว่า Password ที่รับเข้ามาตรงกับ Password ที่เก็บอยู่ในตัวแปร password หรือไม่ หากตรง ส่งคำสั่ง Serial1.write(0x07); ไปยังโมดูล MP3 เพื่อเล่นไฟล์เสียงที่ชื่อ 007 ซึ่งเป็นเสียง ”รหัสผ่านถูกต้อง” และออกจากลูป  wait_password เพื่อไปทำงานในลูป process_led ต่อไป

         หาก Password ที่รับเข้ามาไม่ตรงกับค่าในตัวแปร password ให้ส่งคำสั่ง Serial1.write(0x06); ไปยัง MP3 โมดูล ให้เล่นไฟล์เสียงที่ชื่อ 006 ซึ่งเป็นเสียง ”รหัสผ่านไม่ถูกต้อง” จากนั้น ทำการเคลีย ค่าตัวแปรpass_cnt และ num_pass เพื่อวนรอบรอรับค่า Password ต่อไป

void process_led(void)
{
  char cmd_cnt=0;
  char cmd_All[5];
  if(no_carroer==1)
  {
    return;
  }
  Serial1.flush();
  delay(500);
  while(!digitalRead(busy)){}
  Serial1.write(0x03);
  delay(100);
  while(!digitalRead(busy)){}
  delay(100);
  Serial1.write(0x04);
 
  while(1)
   {
     String data = rx_m10();
     if(data.substring(0,11)=="+QTONEDET: ")
       {
            String  cmd = data.substring(11,13);
            char c = cmd.toInt();
            String turnDTMF = gen_dtmf[0]+c+gen_dtmf[1];
            Serial2.println(turnDTMF);
            if(c == '0')
            {
                cmd_cnt=0;
                cmd_All[0]=0;
                cmd_All[1]=0;
            }
            else
            {
              cmd_All[cmd_cnt] = c;
              Serial.write(cmd_All[0]);
              Serial.write(cmd_All[1]);
              cmd_cnt++;
            }
            if(cmd_cnt > 1)
            {
              if(cmd_All[0]=='*')//open
              {
                switch(cmd_All[1])
                {
                  case '1':
                  digitalWrite(LED1, HIGH);
                  break;
                  case '2':
                  digitalWrite(LED2, HIGH);
                  break;
                  case '3':
                  digitalWrite(LED3, HIGH);
                  break;
                  case '4':
                  digitalWrite(LED4, HIGH);
                  break;
                }
              }
              if(cmd_All[0]=='#')
              {
                switch(cmd_All[1])
                {
                  case '1':
                  digitalWrite(LED1,LOW );
                  break;
                  case '2':
                  digitalWrite(LED2,LOW);
                  break;
                  case '3':
                  digitalWrite(LED3,LOW);
                  break;
                  case '4':
                  digitalWrite(LED4,LOW);
                  break;
                }
              }
              cmd_cnt=0;
              cmd_All[0]=0;
              cmd_All[1]=0;
            }
       }
    if(data == "NO CARRIER")
    {
       return;
    }
   }
}

คำอธิบาย ส่วน Function process_led 

       process_led  เป็น Function ที่ทำหน้าที่ รอรับคำสั่งการเปิดปิด และ สั่งงานหลอด LED โดยจะวนนับค่าจาก การกด key จากปลายสาย ซึ่งการทำงานในส่วนแรกจะทำงานเช่นเดียวกับ Function wait_password แต่จะรอรับค่าการกด key จากปลายสาย  เพียงแค่สองค่าแล้วตรวจเช็คว่าค่าที่รับมาค่าแรกซึ่งเก็บอยู่ในตัวแปร cmd_All[0] ว่าเป็นค่า ‘*’ หรือ ‘#’

         หากเป็น ‘*’ ให้เข้าไปทำงานใน switch case ที่เป็นการปิด LED และ หากเป็น ‘#’ ให้เข้าไปทำงานใน switch case ที่เป็นการเปิด LED และ ตรวจเช็ค cmd_All[1] ว่าตรงกับ case ของ LED ดวงใด

         หากมีการวางสายจากผู้ใช้ GSM Module จะส่งคำว่า  “NO CARRIER” ออกมา โปรแกรมก็จะออกจากลูป process_led แล้วกลับไปทำงานใน ลูป wait_Ring เพื่อรอรับการโทรเข้ามาอีกครั้ง

ตัวอย่างการใช้งานกับ Relay:

m10 dtmf relay

<<< Download Source Code >>  

Application Control LED by GSM Module using DTMF_By ThaiEasyElec_19112013.zip