วันเสาร์ที่ 23 กรกฎาคม พ.ศ. 2559

มาทำให้ Arduino IDE ใช้ฟังก์ชัน printf แถมใช้ %f ได้ด้วย

      ก่อนอื่นขออธิบายก่อนนะครับว่า Arduino IDE นั้นไม่มีฟังก์ชัน printf นะครับ ในที่นี้ผมจะมาอธิบายการเพิ่มฟังก์ชัน printf ให้กับ Arduino IDE กันครับ มาเริ่มกันเลย!

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

  1. Arduino IDE เวอร์ชั่นไหนก็ได้ (ผมใช้ arduino-1.6.5-r2)
  2. โปรแกรมอะไรก็ได้ที่แก้ไขไฟล์ .txt ได้ฮ่าๆๆๆ
เริ่มกันเลย...

  • เริ่มจากเปิดโฟลเดอร์ของ Arduino IDE ครับจะมีโฟลเดอร์และไฟล์ดังรูปครับ
  • เข้าไปในโฟลเดอร์ "hardware\arduino\avr\cores\arduino" แล้วหาไฟล์ Print.cpp และ Print.h

  • เปิดไฟล์ Print.cpp และ Print.h แล้วเพิ่มโค้ดดังนี้ [หรือจะเอาไฟล์ที่ผมทำไว้ไปวางทับอันเดิมก็ได้ครับโดยโหลดจาก Print.cpp , Print.h]




  • เสร็จแล้วครับสำหรับการทำให้ Arduino IDE ใช้ฟังก์ชัน printf ได้ ทีนี้ทดลองใช้คำสั่งนี้กับไลบารี่ของ Serial, LCD หรือไลบารี่ตัวอื่นที่เรียนใช้งานคลาสของ Print ได้ครับ โดยผมมีคำสั่งตัวอย่างให้ดูดังนี้ครับ



  • เมื่อเราลองใช้คำสั่งดูแล้วจะเห็นว่าส่วนของ %f มันใช้งานไม่ได้นะครับ เนื่องจาก Arduino IDE ได้ตั้งค่าเอาไว้ให้ใช้คำสั่ง printf แบบที่กินพื้นที่น้อย ดังนั้นเราต้องไปปรับแต่งเล็กน้อยครับโดยเข้าไปที่โฟลเดอร์ "\hardware\arduino\avr" แล้วเปิดไฟล์ชื่อ platform.txt ให้เพิ่มคำสั่งในบรรทัดที่เริ่มต้นข้อความว่า "compiler.c.elf.flags" โดยเพิ่มเข้าไปดังรูปครับ จากนั้นปิดโปรแกรม Arduino IDE แล้วเปิดใหม่ ทีนี้เราก็จะสามารถใช้งาน %f ในฟังก์ชัน printf ได้แล้วครับ ^_^


เครดิต :

วันอาทิตย์ที่ 8 มีนาคม พ.ศ. 2558

RPM Measurement by Arduino

RPM Measurement by Arduino


                การใช้ Arduino วัดความเร็วรอบมอเตอร์นั้นสามารถใช้หลักการวัดได้หลายหลักการด้วยกันครับ วันนี้ผมมาเสนออีกหลักการหนึ่ง นั่นคือการใช้ Interrupt ควบคู่กับฐานเวลาในบอร์ด Arduino กัน ซึ่งเป็นวิธีการที่ง่าย และก็ใช้งานได้ดีมากเลยทีเดียวครับ เรามาดูวงจรกันครับ
ภาพ วงจรที่ต่อทดลองวัดความเร็วรอบมอเตอร์ด้วย Arduino

                จะเห็นว่าวงจรผมใช้งานขา 2 เป็น Input เพื่อรับพัลส์จาก Encoder ซึ่งผมกำหนดเอาไว้ที่ 15 PPR นั่นแสดงว่าเมื่อมอเตอร์หมุนไป 1 รอบจะได้พัลส์ออกมา 15 พัลส์นั่นเอง ส่วนเหตุผลที่เลือกขา 2 เป็นขา Input นั้นเนื่องจกขา 2 ของ Arduino นั้นมีคุณสมบัติเป็นขา Interrupt Chanel 0 นั่นเองครับ เพราะเราต้องการคุณสมบัติของขา Interrupt มาใช้งาน จึงเลือกใช้ขานี้ครับ

โค้ดโปรแกรมที่ใช้งานใน Arduino UNO


                จากโค้ดจะเห็นว่าจะใช้ Interrupt ของขา 2 ในขอบขาลง Falling ซึ่งเมื่อพัลส์มาลูกแรกจะเก็บค่า Timer เริ่มต้นเอาไว้โดยการอ่านค่าจากฟังก์ชัน millis() ซึ่งจะคืนค่า milliseconds มาให้ ซึ่งในที่นี้ผมจะใช้นำมาหาค่า Period ของพัลส์ที่ออกมาจาก encoder โดยเก็บค่า Period ไว้ที่ตัวแปร dt ซึ่งมีหน่วยเวลาเป็น milliseconds
                หากเราจะนำมาคำนวณเป็น RPM กชก็สามารถคำนวณได้จาก
                RPM = 60 / (Period Time * PPR)
              60 คือค่าวินาที
                Period Time คือค่า Period ที่อ่านจากพัลส์ที่ออกมาจาก Encoder
              PPR คือค่าจำนวนพัลส์ต่อรอบของ Encoder

ในโค้ดจะเห็นว่าใช้ค่า 60000 milliseconds เนื่องจาก dt มีหน่วยเป็น millisecond

ผลการทำงานของโปรแกรม

ขอบคุณไฟล์ Library Arduino for Proteuse จากเว็ปของครูประภาส สุวรรณเพชร
http://www.praphas.com/index.php/2008-11-03-14-25-25/51-arduino/89-arduino-4-arduino-proteus

ไฟล์โปรแกรม Proteus และ Arduino: Download
หากมีปัญหาหรือว่าต้องการสอบถามอะไรรบกวน Email มาที่ mr.ozuke@gmail.com นะครับ

ขอบคุณครับ
วิทวัส สมพงษ์

วันศุกร์ที่ 1 มีนาคม พ.ศ. 2556

General Servo Controller by PIC (CCS Compiler)

     General Servo Controller by PIC (CCS Compiler)


              คือปกติแล้วในการ Control Servo Motor นั้นเราต้องป้อนพัลส์ไปยังขา Signal ของ Servo Motor ครับ ซึ่งพัลส์นั้นจะมีความกว้างตั้งแต่ 0.5mS - 1.5mS. ครับโดยที่ 0.5mS นั้น Servo Motor จะหมุ่นมาที่ -90 องศาครับส่วน 1.5mS. นั้นจะหมุนไปที่ +90 องศา ถ้าพัลส์ปกติ (1.0mS)ก็จะหมุนมาที่ 0 องศา ครับ ดังนั้นการเขียนโปรแกรมให้ Servo ทำงานได้ต่อเนื่องนั้นต้องเขียนให้การทำงานในส่วนของ Servo เป็น Background Process ครับ คือโปรแกรมของผมจะเขียนโดยให้ Interrupt Timer เป็น Background Process แทนครับ มาดูวีดีโอที่ผมทดลอง Simulate กันครับว่ามอเตอร์นั้นทำงานอย่างไร



              ความสามารถของ Lib คือต่อ Servo เข้าขาไหนก็ได้ของ PIC แค่เป็นขาที่สามารถเป็น output ได้เป็นพอครับ
กำหนดค่าในโปรแกรมนิดหน่อยก็ใช้งานได้แล้วครับ เดี๋ยวมาดูโค้ดกันก่อนครับ
 
/*
Name : General Servo Controller.
Compiler Support : CCS C Compiler.
Designed by : Wittawat Sompong @ RMUTI
Software use timer_1 and CCP1 in MCU.
*/
#include <16f877.h>
#fuses HS,NOLVP,NOWDT,NOPROTECT
#use delay(clock=20M)

//-------------------------General config----------------------------------//
#define SERVO_MAX 5 //Max of Server connector
#define SERVO_MAX_VALUE 2500 //Max of pulse width micro sec(uS)
#define SERVO_MIN_VALUE 500 //Min of pulse width micro sec(uS)

typedef struct tServo{
volatile unsigned pin;
volatile unsigned Active;
volatile unsigned int16 value;
}Servo;

//------------------------Start Prototype-----------------------------------//
void ServoActive(unsigned chanel,unsigned vPin,unsigned int16 vValue);
void ServoValue(unsigned chanel,unsigned int16 value);//value is uS.
//------------------------End of Prototype---------------------------------//

//------------------------Servo gobal variable----------------------------//
signed ServoCnt=0;
Servo nServo[SERVO_MAX];

//------------------------Servo background task use CCP1 interrupt---------//
#INT_CCP1
void ServoTask()
{
//if enc of servo count ,reset timer1.
if(ServoCnt<0){
set_timer1(0);
}else{
//if server pin is active set output servo pin to low.
if(nServo[ServoCnt].Active)
output_low(nServo[ServoCnt].pin);
}
ServoCnt++;
if(ServoCnt<SERVO_MAX)
{
//set interrupt to next servo time.
CCP_1 = get_timer1()+nServo[ServoCnt].value;
//if servo pin is active set output servo pin to high.
if(nServo[ServoCnt].Active)
output_high(nServo[ServoCnt].pin);
}else{
//set fist chanel to start scan.
CCP_1 = get_timer1()+100;
ServoCnt = -1;
}
}

/*
ServoActive(c,p,v)
set pin 'p' to connect servo chanel 'c' and set pulse width 'v' uS.

Example : ServoActive(0,PIN_A0,1500);
set PIN_A0 to connect servo at chanel '0' and set pulse width 1500 uS.
*/
void ServoActive(unsigned chanel,unsigned vPin,unsigned int16 value)
{
nServo[chanel].pin = vPin;
nServo[chanel].value = ((value*5)/8);
nServo[chanel].Active = 1;
}
/*
ServoValue(c,v)
Set servo chanel 'c' to pulse width 'v' uS.
Example : ServoValue(0,2400);
set servo chanel '0' to pulse width 2400 uS. (2.4mS)
*/
void ServoValue(unsigned chanel,unsigned int16 value)
{
if(value>SERVO_MAX_VALUE)
value=SERVO_MAX_VALUE;
if(value<SERVO_MIN_VALUE)
value=SERVO_MIN_VALUE;

if (chanel>=SERVO_MAX)
chanel=SERVO_MAX-1;

nServo[chanel].value = ((Value*5)/8);
}
void main()
{
unsigned i;
//-----------------------------------------Setup timer1 to servo task-----------------------------//
setup_timer_1 (T1_INTERNAL|T1_DIV_BY_8);
setup_ccp1 (CCP_COMPARE_INT);
enable_interrupts(GLOBAL);
enable_interrupts(INT_CCP1);
//-----------------------------------------------------------------------------------------------//
//Add pin_d0 to connect servo chanel '0'
ServoActive(0,PIN_D0,1500);
ServoActive(1,PIN_D1,1500);
ServoActive(2,PIN_B0,1500);
ServoActive(3,PIN_B1,1500);
delay_ms(1000);
while(true)
{
for(i=0;i<4;i++)
{
ServoValue(i,1000);
//delay_ms(200);
}
delay_ms(1000);
for(i=0;i<4;i++)
{
ServoValue(i,1500);
//delay_ms(200);
}
delay_ms(1000);
for(i=0;i<4;i++)
{
ServoValue(i,2000);
//delay_ms(200);
}
delay_ms(1000);
for(i=0;i<4;i++)
{
ServoValue(i,1500);
//delay_ms(200);
}
delay_ms(1000);
}
}


                   หลักๆที่ท่านจะได้ใช้คือ ServoActive() ครับเป็นฟังก์ชั่นที่เอาไว้เพิ่มหรือกำหนดว่าเราจะต่อขาอะไรเข้ากับ Servo
                  ตัวอย่างเช่น ผมต้องการต่อขา PIN_D0 เข้า Servo ช่องที่ 0 และตั้งให้พัลส์ที่ขาออก 1.5mS 
ผมก็เขียนว่า ServoActive(0,PIN_D0,1500); ถ้าผมต้องการเปลี่ยนองศาการหมุนของมัน
ไปเป็น +90 องศา ก็จะเขียน ServoValue(0,1000);

                   การใช้งานเบื้องต้นมีเท่านี้ครับ ใช้ง่ายครับ แต่โค้ดต้นฉบับเองผมได้ดูมาจาก Arduino 
และดัดแปลงมาเขียนบน AVR Studio และก็แปลงมาเป็น CCS อีกที
เดี๋ยวตั้วหน้าจะเอาเวอร์ชั่น AVR มาให้ครับ พอดียังไม่ได้ลงโปรแกรม AVR Studio ครับเลยยังไ่ม่มีเวลาทดสอบ

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


ปล.ผมคอมเม้นในโค้ดเป็นภาษาอังกฤษนะ อาจะดูกระด้างไปหน่อย ครั้งหลังจะปรับปรุงให้ดีกว่านี้ครับ ;)

ขอบคุณครับ
โอ.... ;D

LPC1768-Mini-DK Web Server

LPC1768-Mini-DK Ethernet Web Server



                ช่วงเมื่อปิดเทอมที่แล้วพอดีมีโอกาสอยู่บ้านกับเจ้าบอร์ด LPC1768 ที่สั่งมาจากเว็ป www.micro4you.net ซึ่งสเป็คการใช้งานและราคาถือว่าคุ้มมากครับ และตัวบอร์ดเองก็สามารถต่อกับอุปกรณ์ภายนอกได้เยอะพอสมควรเลยครับ แต่บล็อกนี้ขอพูดถึงการเอาเจ้าบอร์ด LPC1768-Mini-DK มาเชื่อมต่อกับคอมพิวเตอร์ในรูปแบบ Ethernet Web Server นะครับ

               การเชื่อมต่อนั้นง่ายมากครับ อุปกรณ์ที่ต้องใช้ก้จะมีสาย LAN ที่เราทำเองหรือซื้อมาก็ได้นะครับสายที่ผมใช้อยู่เป็นสายไขว้ คือเอาไว้เชื่อมต่อโดยตรงระหว่างบอร์ดกับคอมพิวเตอร์ ส่วนโค้ดที่รันในตัว MCU เองผมเอามาจาก Example ครับใช้สองอันรวมกันโดยใช้ Lib ของ SD-Card+Ethernet ที่แถมมาให้ในแผ่น CD มาแก้นิดหน่อยเดี๋ยวเรามาดูกันครับว่าโค้ดเบื้องต้นนั้นเป็นอย่างไรกันบ้าง

ผมจะอธิบายไฟล์เดียวนะครับคือไฟล์ Easyweb.c ซึ่งสามารถโหลดโปรเจคที่ผมอัพโหลดเอาไว้ได้มีลิ้งค์อยู่ข้างล่างครับ

/******************************************************************
 *****                                                        *****
 *****  Name: easyweb.c                                       *****
 *****  Ver.: 1.0                                             *****
 *****  Date: 07/05/2001                                      *****
 *****  Auth: Andreas Dannenberg                              *****
 *****        HTWK Leipzig                                    *****
 *****        university of applied sciences                  *****
 *****        Germany                                         *****
 *****  Func: implements a dynamic HTTP-server by using       *****
 *****        the easyWEB-API                                 *****
 *****                                                        *****
 ******************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "LPC17xx.h"      // Keil: Register definition file for LPC17xx
#include "ff.h"
#include "./SPI_SD_Card/SPI_MSD_Driver.h"
#define extern            // Keil: Line added for modular project management
#include "easyweb.h"
#include "type.h"
#include "EMAC.h"         // Keil: *.c -> *.h    // ethernet packet driver
#include "tcpip.h"        // Keil: *.c -> *.h    // easyWEB TCP/IP stack
//#include "webpage.h"                             // webside for our HTTP server (HTML)
FATFS fs;         /* Work area (file system object) for logical drive */
FIL fsrc;         /* file objects */  
FILINFO fi;
void getPageDir(char*,char*);
void lastname(char*,char*);
int getType(char *p);
extern void TCPClockHandler(void);
unsigned char mounted=0;
unsigned char getFileActive=0;
char GetFile[100];
volatile DWORD TimeTick  = 0;
/* SysTick interrupt happens every 10 ms */
void SysTick_Handler (void) {
  TimeTick++;
  if (TimeTick >= 20) {
    TimeTick = 0;
    LPC_GPIO3->FIOPIN ^= 1 << 25;
    TCPClockHandler();
  }
}


//void main(void)
int main(void)
{
//InitOsc();              // Keil: No oscillator initialization necessary at this time.
//InitPorts();  // Keil: No port initialization necessary at this time.
  SystemInit();                                      /* setup core clocks */
  SysTick_Config(SystemFrequency/100);               /* Generate interrupt every 10 ms */
  LPC_GPIO0->FIODIR   |= 1 << 21;   LPC_GPIO0->FIOPIN  |= 1 << 21;
  LPC_GPIO3->FIODIR   |= 1 << 25;                    /* P2.0 defined as Output (LED) */
  TCPLowLevelInit();
MSD_SPI_Configuration();
  HTTPStatus = 0;                                // clear HTTP-server's flag register
  TCPLocalPort = TCP_PORT_HTTP;                  // set port we want to listen to

  while (1)                                      // repeat forever
  {
    if (!(SocketStatus & SOCK_ACTIVE)) TCPPassiveOpen();   // listen for incoming TCP-connection
    DoNetworkStuff();                                      // handle network and easyWEB-stack
               
if( _card_insert() == 0 && mounted == 0)
{ f_mount(0,&fs); mounted = 1;
}else if(_card_insert()){
mounted = 0;// events
}
    HTTPServer();
  }
}
// This function implements a very simple dynamic HTTP-server.
// It waits until connected, then sends a HTTP-header and the
// HTML-code stored in memory. Before sending, it replaces
// some special strings with dynamic values.
// NOTE: For strings crossing page boundaries, replacing will
// not work. In this case, simply add some extra lines
// (e.g. CR and LFs) to the HTML-code.
void HTTPServer(void)
{
char lName[4];
unsigned int len,lPut = 0;
int iType;
int a = sizeof(unsigned);
  if (SocketStatus & SOCK_CONNECTED)             // check if somebody has connected to our TCP
  {
    if (SocketStatus & SOCK_DATA_AVAILABLE)      // check if remote TCP sent data
{
getPageDir((char*)TCP_RX_BUF,&GetFile[0]);
      TCPReleaseRxBuffer();                      // and throw it away
}else if (SocketStatus & SOCK_TX_BUF_RELEASED){
      if (!(HTTPStatus & HTTP_SEND_PAGE) && getFileActive == 1)        // init byte-counter and pointer to webside
      { memcpy(TCP_TX_BUF, GetResponse, sizeof(GetResponse) - 1); lPut = sizeof(GetResponse) - 1;
if(mounted)// if called the 1st time
{ if(strcmp(GetFile,"0:/") == 0)
{
if(f_stat("0:/index.htm",&fi)==FR_OK)
{
const char res[]={
"HTTP/1.0 200 OK\r\n"
"Content-Type: text/html\r\n\r\n"
};
f_open(&fsrc,"0:/index.htm",FA_READ);
HTTPBytesToSend = fi.fsize; memcpy(TCP_TX_BUF, res, sizeof(res) - 1); TCPTxDataCount = sizeof(res);   }
else
{
char str2[]="HTTP/1.0 404 Not Found\r\n";
HTTPBytesToSend = 0; TCPTxDataCount = sizeof(str2);
memcpy(TCP_TX_BUF,str2,sizeof(str2)); }
}else{
lastname(&GetFile[0],lName);
iType=getType(lName);
if(f_stat(&GetFile[0],&fi)==FR_OK)
{ f_open(&fsrc,&GetFile[0],FA_READ);
HTTPBytesToSend = fi.fsize;
memcpy(TCP_TX_BUF + lPut,strContent,sizeof(strContent));
lPut += sizeof(strContent)-1;
memcpy(TCP_TX_BUF + lPut,ContentType[iType],strlen(ContentType[iType]));
lPut += strlen(ContentType[iType]);
memcpy(TCP_TX_BUF + lPut,"\r\n\r\n",4);
lPut += 4;
TCPTxDataCount =lPut;  
}
else
{
char str2[]="HTTP/1.0 404 Not Found\r\n";
HTTPBytesToSend = 0; TCPTxDataCount = sizeof(str2);
memcpy(TCP_TX_BUF,str2,sizeof(str2)); } } }
else
{
char str2[]="<html><body>Plase Insert SC-Card.</body></html>\r\n";
HTTPBytesToSend = 0; TCPTxDataCount = sizeof(str2);
memcpy(TCP_TX_BUF,str2,sizeof(str2));
}
TCPTransmitTxBuffer(); HTTPStatus |= HTTP_SEND_PAGE;              // ok, 1st loop executed       }
else if(HTTPBytesToSend)
{
f_read(&fsrc,TCP_TX_BUF,MAX_TCP_TX_DATA_SIZE,&len);
HTTPBytesToSend -= len;
TCPTxDataCount = len;
TCPTransmitTxBuffer();
}
if(HTTPBytesToSend < 1)
{ if(getFileActive == 1)
TCPClose();
getFileActive = 0;
f_close(&fsrc);
HTTPStatus &= ~HTTP_SEND_PAGE;
}
    }
  }
else
{
getFileActive = 0;
f_close(&fsrc);
HTTPStatus &= ~HTTP_SEND_PAGE;
}
}
void getPageDir(char* rxBuff,char *file_dir)
{ if(rxBuff[0]=='G' && rxBuff[1]=='E' && rxBuff[2]=='T')
{
file_dir[0]='0';
file_dir[1]=':';
file_dir[2]='/';
file_dir+=3;
rxBuff+=5;
while(*rxBuff != ' ')
{
*file_dir++ = *rxBuff++;
}
*file_dir = '\0';
getFileActive = 1;
}
}
void lastname(char* filname,char *lastname)
{
while(*filname++ != '.');
while(*filname != '\0')
{
*lastname = *filname++;
if(*lastname>='a' && *lastname<='z')
*lastname++ -= 0x20; }
*lastname = '\0';
}
int getType(char *p)
{
int i=0;
while(strcmp((char*)xContent[i++],p)!=0)
if(i==10)return 10; return i - 1;
}
//EOF

เราจะมาดูกันเป็นส่วนๆนะครับ

1.ส่วนของฟังก์ชั่น main() ครับ
//void main(void)
int main(void)
{
//InitOsc();              // Keil: No oscillator initialization necessary at this time.
//InitPorts();  // Keil: No port initialization necessary at this time.
  SystemInit();                                      /* setup core clocks */
  SysTick_Config(SystemFrequency/100);               /* Generate interrupt every 10 ms */
  LPC_GPIO0->FIODIR   |= 1 << 21;   LPC_GPIO0->FIOPIN  |= 1 << 21;
  LPC_GPIO3->FIODIR   |= 1 << 25;                    /* P2.0 defined as Output (LED) */
  TCPLowLevelInit();
  MSD_SPI_Configuration();
  HTTPStatus = 0;                                // clear HTTP-server's flag register
  TCPLocalPort = TCP_PORT_HTTP;                  // set port we want to listen to

  while (1)                                      // repeat forever
  {
    if (!(SocketStatus & SOCK_ACTIVE)) TCPPassiveOpen();   // listen for incoming TCP-connection
    DoNetworkStuff();                                      // handle network and easyWEB-stack
               
if( _card_insert() == 0 && mounted == 0)
{ f_mount(0,&fs); mounted = 1;
}else if(_card_insert()){
mounted = 0;// events
}
    HTTPServer();
  }
}

          บรรทัดแรกๆก็ไม่มีอะไรมากครับ เป็นส่วนการ Setup กับตั้งค่าส่วนต่างๆของ MCU ครับ มาดูกันครับว่าโค้ดเราเป็นอย่างไรบ้าง

                  - TCPLowLevelInit(); เรียกใช้ฟังก์ชันเพื่อตั้งค่าระบบ Ethernet ของ MCU ครับ
                  - MSD_SPI_Configuration(); เรียกใช้ฟังก์ชั่นเพื่อนตั้งค่า SPI ให้สามารถเชื่อต่อกับ SD-Card ได้
                  - while(1){} ใน Loop นี้จะเห็นว่าโปรแกรมถูกเขียนเป็น State เอาไว้คือทำงานกันตลอดเวลาตามลำดับคือเมื่อมีการเชื่อมต่อจากเครื่องลูกข่ายเข้ามายัง MCU โปรแกรมจะเข้าไปทำงานในส่วน HTTPServer(); ครับ

-----------------------------------------------------------------------------------------------------------
void HTTPServer(){}

เดี๋ยวเรามาดูกันครับว่าผมเขียนอะไรเข้าไปบ้างในฟังก์ชั่นนี้

              - เริ่มเเรกเลยคือโปรแกรมเราจะเช็คว่า มีการเชื่อมต่อและส่งข้อมูลจากลูกข่ายหรือไม่
ถ้ามีการส่งข้อมูลเข้ามา โปรแกรมจะแปรคำสั่งจากลูกข่ายซึ่งใช้ฟังก์ชั่น
getPageDir((char*)TCP_RX_BUF,&GetFile[0]);
ฟังก์ชั่นนี้จะจัดการกับคำสั่งที่ได้รับมาจาก Client ครับคือลูกข่ายจะเรียกข้อมูลหรือไฟล์จาก Server โดยมีคำสั่ง GET (file directory) ครับคือขึ้นต้นด้วย GET หรือ POST ครับ แต่โปรแกรมที่ผมเขียนไว้ให้นั่น มันสามารถรับได้แค่คำสั่ง GET ครับ

              - เมื่อเราอ่านข้อมูลที่ได้รับมาจาก  Client แล้วเราจะมาเช็คว่า ลูกข่ายนั้นต้องการไฟล์หรือข้อมูลอะไรครับ ซึ่งจะทำงานภายใต้กรอบ
}else if (SocketStatus & SOCK_TX_BUF_RELEASED){
                คือเมื่อมีการส่งหรือรับข้อมูลเสร็จแล้ว โปรแกรมจะเข้ามาทำงานในส่วนนี้ครับ มาดูคำสั่งคร่าวๆกันครับ
if (!(HTTPStatus & HTTP_SEND_PAGE) && getFileActive == 1) 
                    เมื่อเริ่มต้นการส่งข้อมูลหรือไฟล์ไปยัง Client เราจะต้องส่ง HEADER ไปก่อนครับว่าเราจะส่งไฟล์แบบไหน ประเภทอะไร ไฟล์เก็บข้อมูลในลักษณะอย่างไร ดูคำสั่งใน easyweb.h นะครับ เมื่อเราได้แยกประเภทการส่งแล้ว เราจะส่ง HEADER ไปหา Client ครับลูกข่ายจะรู้ว่าจะต้องเก็บไฟล์แบบนี้อย่างไร เช่นในกรอบของ
if(strcmp(GetFile,"0:/") == 0)
                    คือลูกข่ายนั้นต้องการไฟล์ Index ของ Server ซึ่งผมได้ตั้งให้เป็น index.htm ผมก็จะเริ่มเปิดไฟล์และอ่านข้อมูลในไฟล์เพื่อส่งไปยังลูกข่ายครับ แต่ถ้าหากไฟล์มีขนาดมากกว่า TX Buffer ของ MCU เราก็จะเป็นจะต้องส่งหลายๆรอบครับ เรียกการส่งแบบนี้ว่าเป็นการส่งข้อมูลแบบ Page คือส่งไปทีละชุดจนกว่าข้อมูลจะหมด แล้วเลิกเชื่อมต่อกับ Client ซึ่งฝั่ง Client จะทราบเองว่าข้อมูลของไฟล์นี้ สิ้นสุดแล้ว

เรามาดูการทำงานเป็นขั้นกันครับ

  1. เช็คว่าลูกข่ายต้องการไฟล์อะไร
  2. เช็คว่าไฟล์ที่ต้องการมีอยู่ในระบบหรือไม่
  • ถ้าไฟล์ไม่มีในระบบ ให้ส่ง HEADER ว่าไม่พบไฟล์ ไปทำงานที่ข้อ 3
  • ถ้าไฟล์มีในระบบ ให้ส่ง HEADER ไปว่าพบไฟล์ และจะส่งข้อมูลในรูปแบบไหนเป็น TEXT ,Binary ฯลฯ
  • ส่งข้อมูลในไฟล์ไปยัง Client และเช็คว่าข้อมูลที่ส่งไปนั่นหมดหรือยัง สมมุติ TX Buffer ผมมี 512 Byte ผมส่งไฟล์ขนาด 800 Byte ผมจะต้องส่งข้อมูล 2 รอบ คือรอบแรก 512 Byte และรอบสอง 288 Byte เป็นต้น
     3.ปิดการเชื่อมต่อกับ Client

           ในการเรียกข้อมูลของ Client ในแต่ละครั้งนั้นจะขึ้นอยู่กับว่าหน้าเว็ปของเรานั้นใช้ไฟล์ในหนึึ่งหน้าเว็ปนั้น กี่ไฟล์ ถ้ามีมากกว่าหนึ่ง ก็จะมีการเชื่อมต่อกับลูกข่ายอีกครั้ง เช่น ลูกข่ายเรียกไฟล์ index ของเว็ปเรา แล้วเรา้ส่งข้อมูลของหน้า index ไปแล้วลูกข่ายแปลคำสั่งในหน้าเว็ปว่ามีไฟล์ที่ต้องใช้อีก 1 ไฟล์ (สมมุติเป็นไฟล์ภาพแล้วกันครับ) ลูกข่ายจะเชื่อมต่อกับ Server อีกครั้งพร้อมเรียกไฟล์จาก Server ซึ่งไฟล์ที่เรียกนั้นก็จะเป็นไฟล์ภาพหรือไฟล์ที่ลูกข่ายนั้นต้องใช้ในการประมวลหรือแสดงผลในหน้าเว็ปนั้นๆครับ
           การทำ Web Server โดยการใช้ MCU เบื้องต้นก็จะมีกันประมาณนี้ครับ ข้อมูลผมมันอาจจะไม่ละเอียดพอ ยังไงถ้าสงสัยหรือมีข้อซักถามยังไง ก็สอบถามเข้ามาได้ครับ หรือส่ง Email ไปที่ mr.ozuke@gmail.com ได้ครับ ถ้าข้อมูลมีความผิดพลาดประการใดก็ขออภัยไว้ ณ ที่นี้ด้วยนะครับ


           สุดท้ายต้องขอบคุณ
           - บอร์ด LPC1768-Mini-DK จากเว็ป www.micro4you.net ด้วยครับ ราคาถูกและคุ้มค่าที่จะซื้อมาศึกษาการใช้งาน MCU ในระดับที่สูงขึ้นครับ
           - www.electoday.com เว็ปบอร์ดที่เป็นแหล่งข้อมูลที่ดีมากๆอีกเว็ปหนึ่งครับ


ไฟล์โปรเจ็ค LPC1768-Mini-DK Web Server สามารถ Download ได้ตามลิ้งค์ข้างล่างนี้ครับ