ตัวอย่่างโปรแกรม NodeMCU V2 สั่งเปิดปิดไฟผ่าน GPIO โดยให้ ESP8266 เป็น Web Server
#include <ESP8266WiFi.h>
const char* ssid = "Wifi SSID";
const char* password = "Wifi Password";
int led1=1;
int led2=2;
int led3=3;
int led4=4;
int led5=5;
int led6=16;
// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);
void setup() {
Serial.begin(115200);
delay(10);
// prepare GPIO2
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
pinMode(led4, OUTPUT);
pinMode(led5, OUTPUT);
pinMode(led6, OUTPUT);
digitalWrite(led1, 0);
digitalWrite(led2, 0);
digitalWrite(led3, 0);
digitalWrite(led4, 0);
digitalWrite(led5, 0);
digitalWrite(led6, 0);
// Connect to WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
// Start the server
server.begin();
Serial.println("Server started");
// Print the IP address
Serial.println(WiFi.localIP());
}
void loop() {
// Check if a client has connected
WiFiClient client = server.available();
if (!client) {
return;
}
// Wait until the client sends some data
Serial.println("new client");
while(!client.available()){
delay(1);
}
// Read the first line of the request
String req = client.readStringUntil('\r');
Serial.println(req);
client.flush();
// Match the request
int val;
if (req.indexOf("/gpio/") != -1){
int start = req.indexOf("/gpio/");
int eidx = req.indexOf(" HTTP");
String nval = req.substring(start+6,eidx);
val = nval.toInt();
} else {
val = 0;
// Serial.println("invalid request");
// client.stop();
// return;
}
// Set GPIO2 according to the request
digitalWrite(led1, (((val == 1) !=0))?!digitalRead(led1):digitalRead(led1));
digitalWrite(led2, (((val == 2) !=0))?!digitalRead(led2):digitalRead(led2));
digitalWrite(led3, (((val == 3) !=0))?!digitalRead(led3):digitalRead(led3));
digitalWrite(led4, (((val == 4) !=0))?!digitalRead(led4):digitalRead(led4));
digitalWrite(led5, (((val == 5) !=0))?!digitalRead(led5):digitalRead(led5));
digitalWrite(led6, (((val == 6) !=0))?!digitalRead(led6):digitalRead(led6));
client.flush();
// Prepare the response
String s = "HTTP/1.1 200 OK\r\n";
s += "Access-Control-Allow-Origin:*\r\nAccess-Control-Allow-Methods:POST, GET\r\nAccess-Control-Allow-Headers:AUTHORISED\r\n";
s += "Content-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>";
s += "\r\n<head>";
s += "<style>";
s += "a{";
s += " display:inline-block;text-align:center;height:60px; width:60px; border: black solid 4px;";
s += " font-size:2em; padding:0.6em; border-radius:1.2em;";
s += " magin:0.5em;";
s += "}\r\n";
s += ".on{ background-color:red; }\r\n";
s += ".off{ background-color:white;}\r\n";
s += "</style>";
s+= "\r\n</head>";
s+= "<body>";
s += "<h1>";
s += "<a href=\"/gpio/1\" class=\"" ;
s += digitalRead(led1)?"on":"off";
s += "\">1</a> ";
s += "<a href=\"/gpio/2\" class=\"" ;
s += digitalRead(led2)?"on":"off";
s += "\">2</a> ";
s += "<a href=\"/gpio/3\" class=\"" ;
s += digitalRead(led3)?"on":"off";
s += "\">3</a> ";
s += "<a href=\"/gpio/4\" class=\"" ;
s += digitalRead(led4)?"on":"off";
s += "\">4</a> ";
s += "<a href=\"/gpio/5\" class=\"" ;
s += digitalRead(led5)?"on":"off";
s += "\">5</a> ";
s += "<a href=\"/gpio/6\" class=\"" ;
s += digitalRead(led6)?"on":"off";
s += "\">6</a> ";
s += "</h1>";
s += "\r\nGPIO is now ";
s += (val)?"high":"low";
s += "<br/><br/>[" + req +"]<br/>" + val;
s += "</body>";
s += "</html>\n";
// Send the response to the client
client.print(s);
delay(1);
Serial.println("Client disonnected");
// The client will actually be disconnected
// when the function returns and 'client' object is detroyed
}
วันพฤหัสบดีที่ 27 สิงหาคม พ.ศ. 2558
วันพุธที่ 26 สิงหาคม พ.ศ. 2558
ทำหุนยนต์ ให้เด็กๆเล่นกัน NodMCU คุมผ่าน WiFi ^_^
// เผื่อใครใจร้อนอยากลองก่อนครับ ^_^
//Code by AndronoIDE
// I will explain in detail later ^_^
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
const char* ssid = "Your WiFi SSID";
const char* password = "Your WiFi Password";
const char* host = "IP your computer";
char message[255];
unsigned int localPort = 8080;
WiFiUDP Udp;
// Pin layout is NodeMCU V.2
int pin1_motor_R = 1; //gpio01 --> IN1 motor controller Right Motor
int pin2_motor_R = 2; //gpio02 --> IN2 motor controller Right Motor
int pin1_motor_L = 3; //gpio03 --> IN3 motor controller Left Motor
int pin2_motor_L = 4; //gpio04 --> IN4 motor controller Left Motor
// Direction
int FW=0,BK=1,ST=2;
int dir=FW;
// Turn Status
int t=0;
int TL=0, TR=1;
int tdir=0;
int tcount=0;
void setup() {
Serial.begin(115200);
pinMode(pin1_motor_R , OUTPUT);
pinMode(pin2_motor_R, OUTPUT);
pinMode(pin1_motor_L, OUTPUT);
pinMode(pin2_motor_L, OUTPUT);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(",");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Udp.begin(localPort);
Stop();
Serial.println("Setup done");
delay(2000);
}
void loop(){
int packetSize = Udp.parsePacket();
if (packetSize)
{
IPAddress remoteIp = Udp.remoteIP();
int len = Udp.read(message, 255);
Serial.println(message);
checkcommand();
delay(5);
}
}
void forward()
{
Serial.println("forward");
dir=FW;
digitalWrite(pin1_motor_R, HIGH);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, HIGH);
digitalWrite(pin2_motor_L, LOW);
}
void back()
{
Serial.println("back");
dir=BK;
digitalWrite(pin1_motor_R, LOW);
digitalWrite(pin2_motor_R, HIGH);
digitalWrite(pin1_motor_L, LOW);
digitalWrite(pin2_motor_L, HIGH);
}
void left()
{
Serial.println("left");
digitalWrite(pin1_motor_R, LOW);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, HIGH);
digitalWrite(pin2_motor_L, LOW);
}
void right()
{
Serial.println("Right");
digitalWrite(pin1_motor_R, HIGH);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, LOW);
digitalWrite(pin2_motor_L, LOW);
}
void turnR()
{
Serial.println("Turn0");
digitalWrite(pin1_motor_R, HIGH);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, LOW);
digitalWrite(pin2_motor_L, HIGH);
}
void turnL()
{
Serial.println("Turn1");
digitalWrite(pin1_motor_R, LOW);
digitalWrite(pin2_motor_R, HIGH);
digitalWrite(pin1_motor_L, HIGH);
digitalWrite(pin2_motor_L, LOW);
}
void Stop()
{
Serial.println("Stop");
digitalWrite(pin1_motor_R, LOW);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, LOW);
digitalWrite(pin2_motor_L, LOW);
}
void checkcommand()
{
if (*message=='f')
{
forward();
}else if (*message=='t')
{
if(tdir==TL){
turnR();
}else{
turnL();
}
delay(50);
Stop();
}
else if (*message=='b')
{
back();
}
else if (*message=='r')
{
tdir=TR;
right();
delay(200);
if(dir==FW){
forward();
}else if(dir==BK){
back();
}else{
tcount++;
if(tcount!=5){
Stop();
}else{
turnR();
tcount=0;
}
}
}
else if (*message=='l')
{
tdir=TL;
left();
delay(200);
if(dir==FW){
forward();
}else if(dir==BK){
back();
}else{
tcount++;
if(tcount!=5){
Stop();
}else{
turnL();
tcount=0;
}
}
}
else
{
dir=ST;
tcount=0;
Stop();
}
}
//Code by AndronoIDE
// I will explain in detail later ^_^
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
const char* ssid = "Your WiFi SSID";
const char* password = "Your WiFi Password";
const char* host = "IP your computer";
char message[255];
unsigned int localPort = 8080;
WiFiUDP Udp;
// Pin layout is NodeMCU V.2
int pin1_motor_R = 1; //gpio01 --> IN1 motor controller Right Motor
int pin2_motor_R = 2; //gpio02 --> IN2 motor controller Right Motor
int pin1_motor_L = 3; //gpio03 --> IN3 motor controller Left Motor
int pin2_motor_L = 4; //gpio04 --> IN4 motor controller Left Motor
// Direction
int FW=0,BK=1,ST=2;
int dir=FW;
// Turn Status
int t=0;
int TL=0, TR=1;
int tdir=0;
int tcount=0;
void setup() {
Serial.begin(115200);
pinMode(pin1_motor_R , OUTPUT);
pinMode(pin2_motor_R, OUTPUT);
pinMode(pin1_motor_L, OUTPUT);
pinMode(pin2_motor_L, OUTPUT);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(",");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Udp.begin(localPort);
Stop();
Serial.println("Setup done");
delay(2000);
}
void loop(){
int packetSize = Udp.parsePacket();
if (packetSize)
{
IPAddress remoteIp = Udp.remoteIP();
int len = Udp.read(message, 255);
Serial.println(message);
checkcommand();
delay(5);
}
}
void forward()
{
Serial.println("forward");
dir=FW;
digitalWrite(pin1_motor_R, HIGH);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, HIGH);
digitalWrite(pin2_motor_L, LOW);
}
void back()
{
Serial.println("back");
dir=BK;
digitalWrite(pin1_motor_R, LOW);
digitalWrite(pin2_motor_R, HIGH);
digitalWrite(pin1_motor_L, LOW);
digitalWrite(pin2_motor_L, HIGH);
}
void left()
{
Serial.println("left");
digitalWrite(pin1_motor_R, LOW);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, HIGH);
digitalWrite(pin2_motor_L, LOW);
}
void right()
{
Serial.println("Right");
digitalWrite(pin1_motor_R, HIGH);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, LOW);
digitalWrite(pin2_motor_L, LOW);
}
void turnR()
{
Serial.println("Turn0");
digitalWrite(pin1_motor_R, HIGH);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, LOW);
digitalWrite(pin2_motor_L, HIGH);
}
void turnL()
{
Serial.println("Turn1");
digitalWrite(pin1_motor_R, LOW);
digitalWrite(pin2_motor_R, HIGH);
digitalWrite(pin1_motor_L, HIGH);
digitalWrite(pin2_motor_L, LOW);
}
void Stop()
{
Serial.println("Stop");
digitalWrite(pin1_motor_R, LOW);
digitalWrite(pin2_motor_R, LOW);
digitalWrite(pin1_motor_L, LOW);
digitalWrite(pin2_motor_L, LOW);
}
void checkcommand()
{
if (*message=='f')
{
forward();
}else if (*message=='t')
{
if(tdir==TL){
turnR();
}else{
turnL();
}
delay(50);
Stop();
}
else if (*message=='b')
{
back();
}
else if (*message=='r')
{
tdir=TR;
right();
delay(200);
if(dir==FW){
forward();
}else if(dir==BK){
back();
}else{
tcount++;
if(tcount!=5){
Stop();
}else{
turnR();
tcount=0;
}
}
}
else if (*message=='l')
{
tdir=TL;
left();
delay(200);
if(dir==FW){
forward();
}else if(dir==BK){
back();
}else{
tcount++;
if(tcount!=5){
Stop();
}else{
turnL();
tcount=0;
}
}
}
else
{
dir=ST;
tcount=0;
Stop();
}
}
ป้ายกำกับ:
Diy kids toy,
ESP8266,
NodeMCU,
Robot,
Smart Car
วันจันทร์ที่ 12 มกราคม พ.ศ. 2558
สร้างรายได้ด้วยการเขียน Mobile App ได้จริงป่าว?
หลังจากลองผิดลองถูกมาเป็นะระยะเวลากว่า 6 เดือน ถึงเวลาแล้วที่จะเล่าสู่กันฟัง กับประสบการณ์การพิสูจน์ว่าการสร้างรายได้จากการเขียน Mobile App นั้นเป็นไปได้แค่ไหน ใครๆก็ทำได้จริงรึเปล่า
ออกตัวก่อนว่าแอพที่ผมทำส่วนใหญ่จะเป็นแอพง่ายๆ เน้นทำให้ลูกสาวเล่น (ได้ประโยชน์ทั้งสองทาง) และทำเพื่อพิสูจน์การสร้างรายได้จากการเขียนแอพ เพื่อใช้ในงานสอนด้วย รายได้อาจไม่มากมาย แค่ซื้อกาแฟสตาร์บัควันละ 10 แก้วสบายๆแค่นั้นเอง
การทดลองแรกกับ Native App
แอพตัวแรกๆ ที่เอาขึ้น Play Store ของผมหนีไม่พ้น App มหาชนของคนเริ่มเขียน คือแอพแนว Flash Card ลองดูได้ที่ "ศัพท์เด็ก" ตอนทำไม่ได้คิดอะไรแค่อยากทำให้ลูกสาวคนเล็กเล่นดู ภาพจาก Internet แต่งๆเอา เสียงก็ดึงจาก Google Translate พอเอาขึ้นติดโฆษณาพองามก็มีรายได้นิดส์หน่อย (ประมาณวันละไม่ถึง $1 เหรียญ ก็พอได้แฮะ) หลังจากนั้นก็ทำตัวที่ 2 "ฉันคืออะไร" ได้ไอเดียจากลูกสาวถามตอบปัญหาเล่นกันในรถระหว่างส่งไปโรงเรียน
บทเรียนจาก Native App
จาก 2 App ที่ผ่านมาผมพบว่าค่อนข้างเสียเวลาในการเขียนและการทำ UI (ซึ่งก็ใช่ว่าจะสวย) กับรายได้ที่ได้มาตามยอดดาวน์โหลดที่ไม่เยอะมาก ประกอบกับอยากเอาแอพขึ้นในฝั่งของ App Store ของค่าย iOS บ้างเนื่องจากมีเพื่อนใช้ค่ายนี้กันเยอะคิดว่าน่าจะได้กลุ่มทดสอบแรก สิ่งที่ผมต้องการคือ
1. ต้องใช้เวลาเขียนน้อยและเวลาออกแบบไม่นาน โดยเนื้อหาของ App เรามีอยู่แล้ว
2. ต้องสามารถเอาขึ้นได้ทั้ง Play Store (Android) และ App Store (iOS)
จากที่มีพื้น HTML5, CSS3, Java Script ที่ค่อนข้างแน่นอยู่แล้วและได้ลองใช้ lib และเครื่องมืออยู่หลายตัวจึงมาลงเอยที่วิธีเขียนแบบ Hybrid โดยใช้ HTML5+CSS3+Java Script แล้วให้ jQuery Mobile จัดการ UI สุดท้ายใช้ PhoneGap เป็นตัว Build Project ของทั้ง iOS และ Android
App Hybrid ที่ทำแค่ 2 วันเสร็จ ทำรายได้ ได้จริงเหรอ
"จริงครับ" ผมนี่ยืนขึ้น confirm ตรงนี้เลย ตอนนี้ผมมี App บน Play Store ประมาณ 15 ตัว บน iOS อีก 6 ตัว (ที่มีไม่เท่ากันเพราะไม่ค่อยมีเวลาปรับส่งขึ้น Appstore เพราะพี่แกหลายเรื่องอยู่) ผลัดกันสร้างรายได้ในแต่ละวัน มากบ้าง น้อยบ้าง แล้วแต่ช่วงเวลาและเทศกาล บางตัวผมใช้เวลาทำไม่ถึง 3 ช.ม. แต่ก็อย่างว่าคุณภาพของแอพก็จะออกมากลางๆ คงจะสู้แอพที่เค้าตั้งใจทำกับเป็นเดือนๆไม่ได้
คำถามที่มักมีคนถามผมบ่อยๆ
ใช้อะไรเขียน?
ตอบ: ผมใช้ NetBean , eClipse เขียนครับ จริงๆ HTML5 มันใช้อะไรเขียนก็ได้แล้วแต่สะดวก
แล้วบน iOS ต้องมีเครื่อง Mac รึเปล่า?
ตอบ: ไม่เสมอไปครับ แต่ถ้ามีก็ดี ใครจะลงทุนต่ำๆหน่อยก็ใช้ Mac Mini ราคาประมาณ 2 หมื่นกว่าบาท แต่ของผมใช้รันบน VM ก็สะดวกไปอีกแบบครับ
ใช้ Framework ตัวไหนดี jQuery Mobile, ionic, sencha?
ตอบ: แล้วแต่สะดวก แล้วแต่ชอบครับ ส่วนตัวผมชอบ jQuery Mobile ครบดี ประกอบกับ App หลายๆตัวก็มาปรับ CSS เองอยู่แล้ว
ค่าใช้จ่ายอื่นๆ มีอะไรอีกบ้าง?
ตอบ: ค่าสมัคร Android Developer $25 เหรียญ จ่ายครั้งเดียวจบ, Apple Developer $99 เหรียญต่อปี (อาจจะดูเหมือนแพงแต่ถ้าเอาเข้าจริง วันสองวันก็ได้คืนแล้วถ้ามาถูกทาง)
หาไอเดียเขียนแอพจากไหน?
ตอบ: ก็รอบๆตัวเรานี่แหละครับ ทุกอย่างทำเป็นแอพได้หมด เพียงแค่เราเริ่มทำแค่นั้นเอง เงินมันนอนรอให้เราดึงมาใช้อยู่แล้ว
ใช้ Ads ของค่ายไหน?
ตอบ: ผมใช้อยู่เจ้าเดียวคือ Admob (ของ google) ของเจ้าอื่นเคยลองใช้แล้วไม่ค่อยประทับใจเท่าไหร่
นานไหมกว่าจะได้เงิน?
ตอบ: 3 เดือนแรกของผมรายได้ต่อเดือนจะน้อยกว่ารายได้ต่อ 1 วันในตอนนี้ครับ ทุกอย่างต้องใช้เวลา ฝึกฝนหาความรู้ และวิธีการที่เหมาะกำเรา แค่ทำด้วยความสนุก ทำไปเรื่อยๆ เดี๋ยวเงินมันมาเอง
ส่งท้าย
ผมอยากเผยแพร่ให้คนที่มีเวลา มีความชอบในการพัฒนาโปรแกรม ให้ลองมาสร้างรายได้แบบ Passive ดึงเงินเข้าประเทศเยอะๆ สร้างแอพดีๆ ให้พวกเราใช้กัน คนเรามีเวลาทำอะไรอีกหลายๆอย่างได้นอกเหนือจากงานประจำ ใครที่อยากได้คำแนะนำก็ติดต่อมาได้ครับ ยินดี
(เคยคิดจะเขียน ebook เหมือนกันแต่ไม่แน่ใจว่าจะมีคนสนใจรึเปล่า) ^_^
ออกตัวก่อนว่าแอพที่ผมทำส่วนใหญ่จะเป็นแอพง่ายๆ เน้นทำให้ลูกสาวเล่น (ได้ประโยชน์ทั้งสองทาง) และทำเพื่อพิสูจน์การสร้างรายได้จากการเขียนแอพ เพื่อใช้ในงานสอนด้วย รายได้อาจไม่มากมาย แค่ซื้อกาแฟสตาร์บัควันละ 10 แก้วสบายๆแค่นั้นเอง
การทดลองแรกกับ Native App
แอพตัวแรกๆ ที่เอาขึ้น Play Store ของผมหนีไม่พ้น App มหาชนของคนเริ่มเขียน คือแอพแนว Flash Card ลองดูได้ที่ "ศัพท์เด็ก" ตอนทำไม่ได้คิดอะไรแค่อยากทำให้ลูกสาวคนเล็กเล่นดู ภาพจาก Internet แต่งๆเอา เสียงก็ดึงจาก Google Translate พอเอาขึ้นติดโฆษณาพองามก็มีรายได้นิดส์หน่อย (ประมาณวันละไม่ถึง $1 เหรียญ ก็พอได้แฮะ) หลังจากนั้นก็ทำตัวที่ 2 "ฉันคืออะไร" ได้ไอเดียจากลูกสาวถามตอบปัญหาเล่นกันในรถระหว่างส่งไปโรงเรียน
บทเรียนจาก Native App
จาก 2 App ที่ผ่านมาผมพบว่าค่อนข้างเสียเวลาในการเขียนและการทำ UI (ซึ่งก็ใช่ว่าจะสวย) กับรายได้ที่ได้มาตามยอดดาวน์โหลดที่ไม่เยอะมาก ประกอบกับอยากเอาแอพขึ้นในฝั่งของ App Store ของค่าย iOS บ้างเนื่องจากมีเพื่อนใช้ค่ายนี้กันเยอะคิดว่าน่าจะได้กลุ่มทดสอบแรก สิ่งที่ผมต้องการคือ
1. ต้องใช้เวลาเขียนน้อยและเวลาออกแบบไม่นาน โดยเนื้อหาของ App เรามีอยู่แล้ว
2. ต้องสามารถเอาขึ้นได้ทั้ง Play Store (Android) และ App Store (iOS)
จากที่มีพื้น HTML5, CSS3, Java Script ที่ค่อนข้างแน่นอยู่แล้วและได้ลองใช้ lib และเครื่องมืออยู่หลายตัวจึงมาลงเอยที่วิธีเขียนแบบ Hybrid โดยใช้ HTML5+CSS3+Java Script แล้วให้ jQuery Mobile จัดการ UI สุดท้ายใช้ PhoneGap เป็นตัว Build Project ของทั้ง iOS และ Android
App Hybrid ที่ทำแค่ 2 วันเสร็จ ทำรายได้ ได้จริงเหรอ
"จริงครับ" ผมนี่ยืนขึ้น confirm ตรงนี้เลย ตอนนี้ผมมี App บน Play Store ประมาณ 15 ตัว บน iOS อีก 6 ตัว (ที่มีไม่เท่ากันเพราะไม่ค่อยมีเวลาปรับส่งขึ้น Appstore เพราะพี่แกหลายเรื่องอยู่) ผลัดกันสร้างรายได้ในแต่ละวัน มากบ้าง น้อยบ้าง แล้วแต่ช่วงเวลาและเทศกาล บางตัวผมใช้เวลาทำไม่ถึง 3 ช.ม. แต่ก็อย่างว่าคุณภาพของแอพก็จะออกมากลางๆ คงจะสู้แอพที่เค้าตั้งใจทำกับเป็นเดือนๆไม่ได้
App บน Android ที่ทำขึ้นไป
Blog ที่ใช้ Promote App: http://br3source.blogspot.com/ , http://kidapp.blogspot.com/
ตัวอย่างกราฟแสดงรายได้จากการติดโฆษณาเท่านั้น (ขออนุญาตไม่ลงตัวเลขนะครับ ^_^)
คำถามที่มักมีคนถามผมบ่อยๆ
ใช้อะไรเขียน?
ตอบ: ผมใช้ NetBean , eClipse เขียนครับ จริงๆ HTML5 มันใช้อะไรเขียนก็ได้แล้วแต่สะดวก
แล้วบน iOS ต้องมีเครื่อง Mac รึเปล่า?
ตอบ: ไม่เสมอไปครับ แต่ถ้ามีก็ดี ใครจะลงทุนต่ำๆหน่อยก็ใช้ Mac Mini ราคาประมาณ 2 หมื่นกว่าบาท แต่ของผมใช้รันบน VM ก็สะดวกไปอีกแบบครับ
ใช้ Framework ตัวไหนดี jQuery Mobile, ionic, sencha?
ตอบ: แล้วแต่สะดวก แล้วแต่ชอบครับ ส่วนตัวผมชอบ jQuery Mobile ครบดี ประกอบกับ App หลายๆตัวก็มาปรับ CSS เองอยู่แล้ว
ค่าใช้จ่ายอื่นๆ มีอะไรอีกบ้าง?
ตอบ: ค่าสมัคร Android Developer $25 เหรียญ จ่ายครั้งเดียวจบ, Apple Developer $99 เหรียญต่อปี (อาจจะดูเหมือนแพงแต่ถ้าเอาเข้าจริง วันสองวันก็ได้คืนแล้วถ้ามาถูกทาง)
หาไอเดียเขียนแอพจากไหน?
ตอบ: ก็รอบๆตัวเรานี่แหละครับ ทุกอย่างทำเป็นแอพได้หมด เพียงแค่เราเริ่มทำแค่นั้นเอง เงินมันนอนรอให้เราดึงมาใช้อยู่แล้ว
ใช้ Ads ของค่ายไหน?
ตอบ: ผมใช้อยู่เจ้าเดียวคือ Admob (ของ google) ของเจ้าอื่นเคยลองใช้แล้วไม่ค่อยประทับใจเท่าไหร่
นานไหมกว่าจะได้เงิน?
ตอบ: 3 เดือนแรกของผมรายได้ต่อเดือนจะน้อยกว่ารายได้ต่อ 1 วันในตอนนี้ครับ ทุกอย่างต้องใช้เวลา ฝึกฝนหาความรู้ และวิธีการที่เหมาะกำเรา แค่ทำด้วยความสนุก ทำไปเรื่อยๆ เดี๋ยวเงินมันมาเอง
ส่งท้าย
ผมอยากเผยแพร่ให้คนที่มีเวลา มีความชอบในการพัฒนาโปรแกรม ให้ลองมาสร้างรายได้แบบ Passive ดึงเงินเข้าประเทศเยอะๆ สร้างแอพดีๆ ให้พวกเราใช้กัน คนเรามีเวลาทำอะไรอีกหลายๆอย่างได้นอกเหนือจากงานประจำ ใครที่อยากได้คำแนะนำก็ติดต่อมาได้ครับ ยินดี
(เคยคิดจะเขียน ebook เหมือนกันแต่ไม่แน่ใจว่าจะมีคนสนใจรึเปล่า) ^_^
วันอาทิตย์ที่ 23 พฤศจิกายน พ.ศ. 2557
WebSphere App Server 8.5 มีอะไรน่าใช้บ้าง
ตามธรรมเนียม กันลืม หลังเล่นหรือ update อะไรมาแล้ว ก็มาเล่าสู่กันฟัง
*** คำเตือน ใครยังไม่รู้จัก Application Server หรือไม่รู้จักพวก JBoss, GlassFish, Web Logic ควรหาข้อมูล เกี่ยวกับการทำงานของ App Server ก่อน อ่านบทความนี้แล้วงง ไม่รู้ด้วยนะ ^_^ ***
ตอนนี้ว่ากันด้วย Application Server ที่ใช้ในงาน Enterprise ของค่าย IBM ครับ ซึ่งหา tutorial online ค่อนข้างยาก แถมใน IBM RED Book ก็ไม่ค่อยมีเนื้อหาเชิงปฏิบัติเท่าไหร่
ถ้าจะเริ่มเล่นต้องสมัคร IBM ID ก่อน (ฟรี) แล้วจะสามารถเข้าไปดาวน์โหลด trial version มาลองเล่นได้ ซึ่งมีหลายรุ่นหลายขนาด ตามวัตถุประสงค์การใช้งาน ดังนี้:
Liberty - ตัวเล็กสุด เหมาะสำหรับนักพัฒนา ลงและควบคุมผ่าน eclipse ได้ (มีบน Mac ด้วย)
Express - สำหรับนักพัฒนาและธุรกิจขนาดเล็ก
Base - สำหรับการลงแบบ Single Server รองรับ Application Service หลายรูปแบบ
Network Deployment (ND) - เหมาะกับองค์กรขนาดใหญ่ สามารถทำ Cluster ได้ (pack นี้แลจะใหญ่สุดละ)
รายละเอียดเปรียบเทียบแต่ละรุ่นลองดูตาม link นี้ครับ เปรียบเทียบ Web Sphere Edition
การติดตั้ง ทำได้หลายรูปแบบสรุปๆ ดังนี้
1. ติดตั้งผ่านผ่าน Installation Manager (IM) โดยดาวน์โหลดตรงจาก Internet
ข้อดี : ได้ของที่ update เวลามี fix หรือ patch จะมีตัวเลือกให้
ข้อเสีย: ถ้าเน็ตไม่นิ่ง อาจทำให้เสียเวลา และต้องลงซ่อมหลายรอบ
2. ติดตั้งผ่าน Local Repository (ดาวน์โหลดตัวติดตั้งมาเก็บไว้ก่อนแล้วค่อยติดตั้งทีเดียว)
ข้อดี: เร็ว ไม่ต้องเสียเวลารอ ดาวน์โหลดจาก Internet (ไม่ต้องลุ้นมาก)
ข้อเสีย: ต้องการพื้นที่ในการเก็บไฟล์ (ถ้าจะลงให้ครบทุกอย่างก็ประมาณ 12GB เป็นอย่างน้อย)
** ปกติตัว IM ที่ดาวน์โหลดมาจะมีปัญหา Error หลัง login IBM ID หรือกด cancel เนื่องจากไม่สามารถต่อ online repository ปลายทางได้ ให้ไปแก้ไขไฟล์ install.xml โดย comment tag <feature> ทั้งหมดทิ้งก่อน (ใช้ <!-- --> ในการ comment) แล้วค่อย add repository ใหม่ของเราเองที่ File-> Preference-> Repository ตามรูป**
การเริ่มสร้าง Server (ใน Web Sphere จะเรียก Profile)
การสร้าง Profile จำเป็นต้องรู้โครงสร้างเบื้องต้นที่ WebSphere เรียกก่อน ประกอบด้วย
Cell - ถือเป็น Logical view ใหญ่สุดเอาไว้ครอบ Node สำหรับจัด Cluster
Node - จะมองว่าเป็น 1 Hardware Server ก็ได้ แต่ไม่จำเป็นเสมอไป (บางรูปแบบก็มองเป็น Logical ได้เหมือนกัน) โดยใน 1 Node สามารถมีได้หลาย Server และ ในเครื่อง 1 เครื่อง อาจมีได้หลาย Node (แต่ถ้าให้ง่ายจะเป็น 1 เครื่องต่อ 1 Node)
Server - จัดเป็นหน่วยเล็กสุด โดยถ้ามองง่ายๆ 1 Server ก็จะมีบริการได้หลายบริการ หรือลงได้หลาย App นั่นเอง
โดยรูปแบบของ Profile หลักๆ สามารถสรุปได้ดังนี้
Application Server - ถือเป็นตัวพื้นฐาน (Single Server) โดยการติดตั้งแบบนี้จะเหมาะกับการใช้งานในลักษณะแยกหน่วยงาน และแยกกันบริหารจัดการ เช่น Server ของแต่ละแผนก หรือ Server ของแต่ละบริการ
Deployment Manager - ใช้ในการจัดการกลุ่มของ Cluster โดยจะเหมาะกับ Application ที่ต้องการความน่าเชื่อถือสูง (High Availability) คือให้บริการได้ตลอด 7x24 โดยจะมีพวก Node Agent คอยช่วยในการ deploy application และมีหน้า console ในการจัดการ stop/start กลุ่มของ server ที่อยู่ใน cluster รวมทั้งการใช้งานร่วมกับ IBM HTTP Server เพื่อทำ load balancing สำหรับจัดการเรื่อง load และช่วยดูแลเรื่อง fail over (กรณีที่มี server ตัวใดตัวหนึ่งตาย ตัวที่เหลือจะมาให้บริการแทน พร้อมทั้งโอนข้อมูลที่อยู่ใน session มาให้ด้วย ทำให้ผู้ใช้ไม่รู้สึกว่า server มีปัญหา สามารถทำงานได้ต่อเนื่อง)
Administrative Agent - ใช้สำหรับจัดการกลุ่มของ Single Server (ที่ไม่ใช่ model ของ cluster) เพื่อให้ง่ายในการเข้าถึงโดย admin สามารถใช้งานและเลือกจัดการ server ที่ลงทะเบียนกับ Administrative Agent ได้ในหน้าเดียว (ไม่ต้องนั่งจำ port ของแต่ละ console) ถ้าจะให้ได้ประโยชน์สูงสุดควรใช้งานคู่กับ Job Manager จะทำให้สามารถทำให้งาน admin server หลายๆตัว กลายเป็นเรื่องง่ายไปเลย
Job Manager - ใช้ประโยชน์ในการตั้งเวลาในการ ทำงานต่างๆ เช่น การรันสคริปต์ การก๊อปปี้ไฟล์ การสั่งติดตั้ง หรือถอนการติดตั้งแอพ โดยใช้ได้ทั้งกับ Deployment Manager และ Administrative Agent
สรุปง่ายๆ :
ถ้าต้องคุม Single Server หลายตัวใช้ App Server + Admin Agent + Job Manager
ถ้าเป็นงาน Cluster ใช้ Deployment Manager + Job Manger
เครื่องมืออื่นๆ และสิ่งที่น่าใช้ใน Web Sphere Application Server
Liberty Profile - ขนาดเล็ก ติดตั้งง่าย เหมาะกับงาน Dev มากกกกก
Static vs Dynamic Cluster - เราสามารถจัดการให้ server ที่เพิ่มเข้ามาใหม่เข้ามาอยู่ในกลุ่ม cluster ได้แบบ dynamic (อัตโนมัติ) รวมถึงการจัดการพวก dynamic workload ด้วย
Health Management - ช่วยในการ Monitor และตั้งการเก็บ log หรือรัน script สำหรับรายงานปัญหา
IBM Support Assistant (ISA) - ใช้เปิดไฟล์ Log และไฟล์ binary อื่นๆ ทั้งช่วยในการวิเคราะห์ปัญหาที่เกิดขึ้นกับ Server (ดาวน์โหลดโปรแกรม ISA จาก IBM)
Package ที่ควรลงเพิ่ม - IHS (IBM HTTP Server) สำหรับทำ load balancer , WebSphere Plug-in ดูแลเรื่อง workload แลใช้คู่กับ http server ในการทำ balancer , Web Sphere Customize Tools
wsadmin tool - ใช้ในการบริหารจัดการ Server ผ่าน command-line ซึ่งใช้ภาษา Jython (Java + Python) เป็นภาษาหลัก
Apache JMeter - ใช้ช่วยทำสอบและลองยิง Server (คู่มือการใช้งาน JMeter)
*** ใครสนใจเรื่อง IBM WebSphere Administrator ก็ติดต่อสอบถามมาได้ครับ ยินดีให้คำปรึกษา ***
สอบถาม โทร: ศูนย์แปดหนึ่ง สี่เก้าสี่ สามสองหนึ่งหก
*** คำเตือน ใครยังไม่รู้จัก Application Server หรือไม่รู้จักพวก JBoss, GlassFish, Web Logic ควรหาข้อมูล เกี่ยวกับการทำงานของ App Server ก่อน อ่านบทความนี้แล้วงง ไม่รู้ด้วยนะ ^_^ ***
ตอนนี้ว่ากันด้วย Application Server ที่ใช้ในงาน Enterprise ของค่าย IBM ครับ ซึ่งหา tutorial online ค่อนข้างยาก แถมใน IBM RED Book ก็ไม่ค่อยมีเนื้อหาเชิงปฏิบัติเท่าไหร่
ถ้าจะเริ่มเล่นต้องสมัคร IBM ID ก่อน (ฟรี) แล้วจะสามารถเข้าไปดาวน์โหลด trial version มาลองเล่นได้ ซึ่งมีหลายรุ่นหลายขนาด ตามวัตถุประสงค์การใช้งาน ดังนี้:
App Server Edition (รุ่นสำหรับติดตั้ง)
สามารถดาวน์โหลดได้ที่ link นี้ครับ (IBM WebSphere Application Server)Liberty - ตัวเล็กสุด เหมาะสำหรับนักพัฒนา ลงและควบคุมผ่าน eclipse ได้ (มีบน Mac ด้วย)
Express - สำหรับนักพัฒนาและธุรกิจขนาดเล็ก
Base - สำหรับการลงแบบ Single Server รองรับ Application Service หลายรูปแบบ
Network Deployment (ND) - เหมาะกับองค์กรขนาดใหญ่ สามารถทำ Cluster ได้ (pack นี้แลจะใหญ่สุดละ)
รายละเอียดเปรียบเทียบแต่ละรุ่นลองดูตาม link นี้ครับ เปรียบเทียบ Web Sphere Edition
การติดตั้ง ทำได้หลายรูปแบบสรุปๆ ดังนี้
1. ติดตั้งผ่านผ่าน Installation Manager (IM) โดยดาวน์โหลดตรงจาก Internet
ข้อดี : ได้ของที่ update เวลามี fix หรือ patch จะมีตัวเลือกให้
ข้อเสีย: ถ้าเน็ตไม่นิ่ง อาจทำให้เสียเวลา และต้องลงซ่อมหลายรอบ
2. ติดตั้งผ่าน Local Repository (ดาวน์โหลดตัวติดตั้งมาเก็บไว้ก่อนแล้วค่อยติดตั้งทีเดียว)
ข้อดี: เร็ว ไม่ต้องเสียเวลารอ ดาวน์โหลดจาก Internet (ไม่ต้องลุ้นมาก)
ข้อเสีย: ต้องการพื้นที่ในการเก็บไฟล์ (ถ้าจะลงให้ครบทุกอย่างก็ประมาณ 12GB เป็นอย่างน้อย)
** ปกติตัว IM ที่ดาวน์โหลดมาจะมีปัญหา Error หลัง login IBM ID หรือกด cancel เนื่องจากไม่สามารถต่อ online repository ปลายทางได้ ให้ไปแก้ไขไฟล์ install.xml โดย comment tag <feature> ทั้งหมดทิ้งก่อน (ใช้ <!-- --> ในการ comment) แล้วค่อย add repository ใหม่ของเราเองที่ File-> Preference-> Repository ตามรูป**
การเริ่มสร้าง Server (ใน Web Sphere จะเรียก Profile)
การสร้าง Profile จำเป็นต้องรู้โครงสร้างเบื้องต้นที่ WebSphere เรียกก่อน ประกอบด้วย
Cell - ถือเป็น Logical view ใหญ่สุดเอาไว้ครอบ Node สำหรับจัด Cluster
Node - จะมองว่าเป็น 1 Hardware Server ก็ได้ แต่ไม่จำเป็นเสมอไป (บางรูปแบบก็มองเป็น Logical ได้เหมือนกัน) โดยใน 1 Node สามารถมีได้หลาย Server และ ในเครื่อง 1 เครื่อง อาจมีได้หลาย Node (แต่ถ้าให้ง่ายจะเป็น 1 เครื่องต่อ 1 Node)
Server - จัดเป็นหน่วยเล็กสุด โดยถ้ามองง่ายๆ 1 Server ก็จะมีบริการได้หลายบริการ หรือลงได้หลาย App นั่นเอง
โดยรูปแบบของ Profile หลักๆ สามารถสรุปได้ดังนี้
Application Server - ถือเป็นตัวพื้นฐาน (Single Server) โดยการติดตั้งแบบนี้จะเหมาะกับการใช้งานในลักษณะแยกหน่วยงาน และแยกกันบริหารจัดการ เช่น Server ของแต่ละแผนก หรือ Server ของแต่ละบริการ
Deployment Manager - ใช้ในการจัดการกลุ่มของ Cluster โดยจะเหมาะกับ Application ที่ต้องการความน่าเชื่อถือสูง (High Availability) คือให้บริการได้ตลอด 7x24 โดยจะมีพวก Node Agent คอยช่วยในการ deploy application และมีหน้า console ในการจัดการ stop/start กลุ่มของ server ที่อยู่ใน cluster รวมทั้งการใช้งานร่วมกับ IBM HTTP Server เพื่อทำ load balancing สำหรับจัดการเรื่อง load และช่วยดูแลเรื่อง fail over (กรณีที่มี server ตัวใดตัวหนึ่งตาย ตัวที่เหลือจะมาให้บริการแทน พร้อมทั้งโอนข้อมูลที่อยู่ใน session มาให้ด้วย ทำให้ผู้ใช้ไม่รู้สึกว่า server มีปัญหา สามารถทำงานได้ต่อเนื่อง)
Administrative Agent - ใช้สำหรับจัดการกลุ่มของ Single Server (ที่ไม่ใช่ model ของ cluster) เพื่อให้ง่ายในการเข้าถึงโดย admin สามารถใช้งานและเลือกจัดการ server ที่ลงทะเบียนกับ Administrative Agent ได้ในหน้าเดียว (ไม่ต้องนั่งจำ port ของแต่ละ console) ถ้าจะให้ได้ประโยชน์สูงสุดควรใช้งานคู่กับ Job Manager จะทำให้สามารถทำให้งาน admin server หลายๆตัว กลายเป็นเรื่องง่ายไปเลย
Job Manager - ใช้ประโยชน์ในการตั้งเวลาในการ ทำงานต่างๆ เช่น การรันสคริปต์ การก๊อปปี้ไฟล์ การสั่งติดตั้ง หรือถอนการติดตั้งแอพ โดยใช้ได้ทั้งกับ Deployment Manager และ Administrative Agent
สรุปง่ายๆ :
ถ้าต้องคุม Single Server หลายตัวใช้ App Server + Admin Agent + Job Manager
ถ้าเป็นงาน Cluster ใช้ Deployment Manager + Job Manger
เครื่องมืออื่นๆ และสิ่งที่น่าใช้ใน Web Sphere Application Server
Liberty Profile - ขนาดเล็ก ติดตั้งง่าย เหมาะกับงาน Dev มากกกกก
Static vs Dynamic Cluster - เราสามารถจัดการให้ server ที่เพิ่มเข้ามาใหม่เข้ามาอยู่ในกลุ่ม cluster ได้แบบ dynamic (อัตโนมัติ) รวมถึงการจัดการพวก dynamic workload ด้วย
Health Management - ช่วยในการ Monitor และตั้งการเก็บ log หรือรัน script สำหรับรายงานปัญหา
IBM Support Assistant (ISA) - ใช้เปิดไฟล์ Log และไฟล์ binary อื่นๆ ทั้งช่วยในการวิเคราะห์ปัญหาที่เกิดขึ้นกับ Server (ดาวน์โหลดโปรแกรม ISA จาก IBM)
Package ที่ควรลงเพิ่ม - IHS (IBM HTTP Server) สำหรับทำ load balancer , WebSphere Plug-in ดูแลเรื่อง workload แลใช้คู่กับ http server ในการทำ balancer , Web Sphere Customize Tools
wsadmin tool - ใช้ในการบริหารจัดการ Server ผ่าน command-line ซึ่งใช้ภาษา Jython (Java + Python) เป็นภาษาหลัก
Apache JMeter - ใช้ช่วยทำสอบและลองยิง Server (คู่มือการใช้งาน JMeter)
*** ใครสนใจเรื่อง IBM WebSphere Administrator ก็ติดต่อสอบถามมาได้ครับ ยินดีให้คำปรึกษา ***
สอบถาม โทร: ศูนย์แปดหนึ่ง สี่เก้าสี่ สามสองหนึ่งหก
vittayasak แอดจีเมล์ดอทคอม
วันอาทิตย์ที่ 28 กันยายน พ.ศ. 2557
Jasper ETL เครื่องมือฟรีสำหรับคอ BI
พอดีช่วงที่ผ่านมา ได้มีโอกาสเล่นตัว Jasper ETL เลยเอามาแนะนำครับ ^_^
ใครที่อยู่สาย Data Warehouse หรือพวก BI น่าจะเคยใช้เครื่องมือพวก ETL (Extract Transform Load) แต่ที่เห็นในบ้านเราส่วนใหญ่จะเป็นของ Microsoft หรือค่ายอื่นๆที่ต้องเสียตังค์ซื้อ จึงอาจจะไม่ค่อยเหมาะกับองค์กรที่ต้องการของฟรี และคุณภาพใช้งานได้จริง หลังจากที่ได้ใช้งานตัว Jasper ETL แล้วรู้สึกประทับใจกับความสามารถที่เกินความคาดหวังไว้ตอนแรก ลองมาดูกันว่ามันทำอะไรได้บ้าง
หลายๆคนอาจจะรู้จัก Jasper Report มาก่อนซึ่งใช้ในการสร้างรายงานแบบ Open Source และมักใช้ iReport เป็นตัวออกแบบ แต่เนื่องจากมันเป็นโครงการแบบ Open Source จึงมีหลายบริษัทนำไปต่อยอดและปรับแต่งเป็นผลิตภัณอื่นๆ อีกมากมาย ติดตามดูทั้งหมดได้ที่
ในชุด Jasper BI Suite ประกอบด้วย
- JasperReports Library เป็นตัว Report Engine
- Jaspersoft iReport Designer เป็นตัวออกแบบรายงาน ตอนนี้ใช้บน NetBean
- JasperReport Server เป็น Report Server สำหรับแสดงรายงาน แดชบอร์ด รวมถึง OLAP
- Jaspersoft OLAP ตอนนี้ย้ายไปรวมกับ Report Server แล้ว
- Jaspersoft ETL สำหรับทำ Data Integration
แนวทางการใช้งานตั้งแต่การเข้าถึงฐานข้อมูลที่หลากหลายแหล่งเช่น .csv, xml, xls, ldap รวมถึง database เกือบทุกค่ายและอื่นๆ (ขอบอกว่าเยอะมากๆ ใช้กันไม่หมด) ไปจนถึงตัว BI chart สำหรับผู้บริหารมีแนวทางการใช้งานดังนี้
การ Integrate ข้อมูลด้วย Jasper ETL
เราสามารถออกแบบ Data Warehouse โดยการสร้าง Star-Schema โดยการสร้าง dimension และ fact table จากแหล่งข้อมูลที่แตกต่างกัน โดยใช้ ETL เป็นตัว ดึงข้อมูลจากแหล่งต่างๆ , แปลงข้อมูลให้อยู่ในรูปที่ต้องการ แล้วโหลดเข้าสู่ตารางเป้าหมาย (ใครไม่แม่นแนะนำให้ไปหาอ่านเรื่องการออกแบบ Star-Schema หรือ Snowflake สำหรับ OLAP ก่อน) หน้าตาประมาณนี้
หลังจากได้ Star-Schema ที่ต้องการแล้วก็เริ่มออกแบบ Cube
ในการออกแบบ Cube จะต้องใช้เครื่องมือที่หาโหลดยากนิดหนึ่งที่ชื่อว่า "Jasper Analysis Workbench" (ลองหากันดูก่อนนะ ถ้าไม่เจอจริงๆติดต่อผมได้) การสร้างเริ่มจากการเชื่อม Fact table ก่อนกำหนด measure เช่น จำนวน ยอดสั่งซื้อ รายได้ แล้วจึงสร้าง dimension ย่อยตามมิติที่ต้องการ ส่วนใหญ่ก็เป็น เวลา สินค้า ลูกค้า หน้าตาประมาณนี้ครับ การบันทึกไฟล์จะเป็นรูปแบบ xml ของ Mondrain
หลังจากได้ทุกอย่างครบแล้วสามารถนำไปใช้ใน Jasper Report Server เพื่อให้ผู้บริหารดูได้เลย
ตัว Jasper Report Server นั้นนอกจากจะแสดงรายงาน และตัว OLAP View แล้วยังสามารถกำหนดระดับสิทธิในการเข้าถึงรายงานแต่ละตัวด้วย เหมาะมากสำหรับการใช้งานในระดับองค์กร
คร่าวๆ เอาแค่นี้่ก่อนนะครับ ใครสนใจอยากใช้งานหรือต้องการคำปรึกษา ก็สามารถสอบถามเข้ามาได้ครับยินดี ^_^
สอบถาม โทร: ศูนย์แปดหนึ่ง สี่เก้าสี่ สามสองหนึ่งหก
vittayasak แอดจีเมล์ดอทคอม
สมัครสมาชิก:
บทความ (Atom)
















