ทำไมเน็ตช้า! หาคำตอบด้วย M5Stack และ Raspberry Pi

หลายสัปดาห์ก่อนเพิ่ง Download Dota 2 กลับมาครับ แล้วก็พบว่าเวลามัน patch เน็ตมันช้ามาก แถมรันเป็น background ไม่รู้อีกว่าเน็ตโดนสูบอยู่ นึกว่าคอมมีปัญหาเพราะไม่มีใครใช้เน็ตแล้วมันจะช้าได้ยังไง

ถ้าใช้ router แพงๆ มันมักจะมีจอมาด้วยซึ่งจะแสดง network speed อยู่ ก็เลยคิดว่าจะลองเขียนอะไรแบบนั้นดู

แผนการ

สำหรับแผนการในรอบนี้คือผมจะเอา M5Stack อ่าน SNMP จาก router ผม แล้วมาแสดงบนหน้าจอ

M5Stack

espressif.com

พอดีไปเดินงาน Maker Faire Bangkok ก็เลยยุให้พ่อซื้อ M5Stack ให้ตัวหนึ่ง (บังเอิญเจอพี่ลิ่ว Blognone ด้วย เลยให้พี่ลิ่วช่วยยุ) ซึ่งผมว่ามันเจ๋งมากเพราะมันมีจอ ปุ่มกด และ battery ในตัว (แบตเท่าที่ลองอยู่ได้ประมาณ 10 นาที อย่าคาดหวังอะไรมันมาก และมันจะ reset ตอนเสียบไฟ)

สำหรับการเขียนโปรแกรมนั้น M5Stack ใช้ CPU ESP32 (ตัวพี่ของ ESP8266 ยอดนิยม) สามารถใช้ Arduino IDE เขียนได้ ซึ่งปรากฏว่า Arduino ไม่มี SNMP client library ครั้นจะเขียนเองก็ยุ่งยากไป เลยคิดว่าจะเอา Raspberry Pi มาอ่านแล้วส่งข้อมูลให้ M5Stack อีกที

Router configuration

ก่อนจะไปถึง Raspberry Pi มาตั้งค่า router ให้มีข้อมูลพร้อมอ่านกันก่อน สำหรับ Router Mikrotik ทำได้ดังนี้

  1. เปิด WebFig/WinBox ขึ้นมาแล้ว login ให้เรียบร้อย
  2. ไปที่เมนู IP > SNMP
  3. เลือก Enabled
  4. คลิกที่ Communities ด้านบน กด Add New
  5. ระบุข้อมูลดังนี้
    • Name: อะไรก็ได้
    • Addresses: ไม่ใส่ก็ได้ หรือถ้าต้องการให้ปลอดภัยระบุเป็น IP ของ Raspberry Pi หรือ CIDR ก็ได้
    • Read Access: ติ๊กไว้
    • ผมยังไม่เปิดใช้ security เนื่องจากเป็น read only และ allow เฉพาะ LAN IP ไว้เลยคิดว่าความเสี่ยงต่ำพอแล้ว
  6. ตอนนี้น่าจะพร้อมใช้งาน SNMP แล้ว

ถัดมาเราจะหา OID ของ interface ที่ออกเน็ต ขั้นตอนนี้จะต้องใช้เมนู Terminal แล้วใช้คำสั่ง /interface print oid จะได้ผลดังนี้

 0  R  name=.1.3.6.1.2.1.2.2.1.2.1 actual-mtu=.1.3.6.1.2.1.2.2.1.4.1 mac-address=.1.3.6.1.2.1.2.2.1.6.1 admin-status=.1.3.6.1.2.1.2.2.1.7.1 oper-status=.1.3.6.1.2.1.2.2.1.8.1 bytes-in=.1.3.6.1.2.1.31.1.1.1.6.1 packets-in=.1.3.6.1.2.1.31.1.1.1.7.1 discards-in=.1.3.6.1.2.1.2.2.1.13.1 
       errors-in=.1.3.6.1.2.1.2.2.1.14.1 bytes-out=.1.3.6.1.2.1.31.1.1.1.10.1 packets-out=.1.3.6.1.2.1.31.1.1.1.11.1 discards-out=.1.3.6.1.2.1.2.2.1.19.1 errors-out=.1.3.6.1.2.1.2.2.1.20.1 

 1  RS name=.1.3.6.1.2.1.2.2.1.2.2 actual-mtu=.1.3.6.1.2.1.2.2.1.4.2 mac-address=.1.3.6.1.2.1.2.2.1.6.2 admin-status=.1.3.6.1.2.1.2.2.1.7.2 oper-status=.1.3.6.1.2.1.2.2.1.8.2 bytes-in=.1.3.6.1.2.1.31.1.1.1.6.2 packets-in=.1.3.6.1.2.1.31.1.1.1.7.2 discards-in=.1.3.6.1.2.1.2.2.1.13.2 
       errors-in=.1.3.6.1.2.1.2.2.1.14.2 bytes-out=.1.3.6.1.2.1.31.1.1.1.10.2 packets-out=.1.3.6.1.2.1.31.1.1.1.11.2 discards-out=.1.3.6.1.2.1.2.2.1.19.2 errors-out=.1.3.6.1.2.1.2.2.1.20.2 

 2  RS name=.1.3.6.1.2.1.2.2.1.2.3 actual-mtu=.1.3.6.1.2.1.2.2.1.4.3 mac-address=.1.3.6.1.2.1.2.2.1.6.3 admin-status=.1.3.6.1.2.1.2.2.1.7.3 oper-status=.1.3.6.1.2.1.2.2.1.8.3 bytes-in=.1.3.6.1.2.1.31.1.1.1.6.3 packets-in=.1.3.6.1.2.1.31.1.1.1.7.3 discards-in=.1.3.6.1.2.1.2.2.1.13.3 
       errors-in=.1.3.6.1.2.1.2.2.1.14.3 bytes-out=.1.3.6.1.2.1.31.1.1.1.10.3 packets-out=.1.3.6.1.2.1.31.1.1.1.11.3 discards-out=.1.3.6.1.2.1.2.2.1.19.3 errors-out=.1.3.6.1.2.1.2.2.1.20.3 

แล้วดูเทียบกับเมนู /interface print

 #     NAME                                TYPE       ACTUAL-MTU L2MTU  MAX-L2MTU MAC-ADDRESS      
 0  R  ether1-internet                     ether            1500  1596       2026
 1  RS ether2-celty                        ether            1500  1596       2026
 2  RS ether3-switch                       ether            1500  1596       2026

ซึ่งของผม ether1-internet เป็นตัวที่ต่อกับ router ภายนอก ก็เลยจะต้องดูว่าเบอร์ 0 นั้น oid ที่แสดงข้อมูล internet คืออะไร ก็จะเห็นว่า

  • Bytes in: .1.3.6.1.2.1.31.1.1.1.6.1 (Download)
  • Bytes out: .1.3.6.1.2.1.31.1.1.1.10.1 (Upload)

ซึ่งเราจะจดไว้ใช้ในโค้ดเราต่อไป

Raspberry Pi

สำหรับบน Raspberry Pi นั้นของที่ผมมีอยู่จะเป็น Model B+ ค่อนข้างเก่าแต่ก็ไม่ได้จำเป็นต้องใช้อะไรแรงอยู่แล้ว (งานแค่นี้ Arduino ก็คงทำได้ถ้ามันมี library) โดยใช้ดิสโตรโปรดของผมคือ Arch Linux ARM

โดยโค้ดจะประมาณนี้


import time import socket import struct from easysnmp import Session SLEEP_DURATION = 2 session = Session(hostname='192.168.2.1', community='local', version=2) client = socket.socket(type=socket.SOCK_DGRAM) last_download = 0 last_upload = 0 while True: download = int(int(session.get('.1.3.6.1.2.1.31.1.1.1.6.1').value)/SLEEP_DURATION) upload = int(int(session.get('.1.3.6.1.2.1.31.1.1.1.10.1').value)/SLEEP_DURATION) if last_download != 0: print(download - last_download, upload - last_upload) client.sendto(struct.pack('<2I', download - last_download, upload - last_upload), ('192.168.3.7', 1)) last_download = download last_upload = upload time.sleep(SLEEP_DURATION)

คร่าวๆ ก็คือโปรแกรมจะอ่่านค่า OID ทั้ง 2 ตัวจาก 192.168.2.1 แล้วส่งไปให้ 192.168.3.7 UDP port 1

การนำไปติดตั้งบน Raspberry Pi ก็จะต้องติดตั้ง Python และ easysnmp เพิ่มเติม ดังนี้

# pacman -S python gcc net-snmp python-pip
# pip install easysnmp

M5Stack

ถัดมาเราจะเขียนโปรแกรมสำหรับรับข้อมูลมาแสดงกัน ก็จะประมาณนี้

#include <M5Stack.h>
#include <WiFi.h>
#include <WiFiUdp.h>

const char *WIFI_SSID = "WHS-IoT";
const char *WIFI_PASSWORD = "";
const int SERVICE_PORT = 1;
const int MAX_DOWNLOAD = 35 * 1000000 / 8;
const int MAX_UPLOAD = 6 * 1000000 / 8;
const int GRAPH_WIDTH = 1;

typedef struct netPacket {
  unsigned int download;
  unsigned int upload;
} netPacket;

WiFiUDP udp;
netPacket data;
int graphPos = 0;
int graphOriginY, downloadHeight, uploadHeight;

#define LCD_WIDTH M5.Lcd.width()
#define LCD_HEIGHT M5.Lcd.height()
#define FONT_BASE 31

void wifiConnect(){
  int wifiStatus = WL_IDLE_STATUS;
  bool firstTry = true;

  while(wifiStatus != WL_CONNECTED){
    if(!firstTry){
      switch(wifiStatus){
        case WL_NO_SHIELD:
          M5.Lcd.println("E: No module");
          break;
         case WL_CONNECT_FAILED:
          M5.Lcd.println("E: Connection fail");
          break;
         case WL_CONNECTION_LOST:
          M5.Lcd.println("E: Connection lost");
          break;
         case WL_DISCONNECTED:
          M5.Lcd.print(".");
          break;
         default:
          M5.Lcd.printf("E: %d\n", wifiStatus);
      }
    }else{
      M5.Lcd.printf("Connecting to %s\n", WIFI_SSID);      
    }

    wifiStatus = WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    delay(5000);
    firstTry = false;
  }
  M5.Lcd.println();
  M5.Lcd.println(WiFi.localIP());

  graphPos = 0;
}

void checkWifiConnected(){
  if(WiFi.status() != WL_CONNECTED){
    M5.Lcd.clearDisplay();
    M5.Lcd.setCursor(0, FONT_BASE);
    M5.Lcd.println("Wifi disconnected!");
    wifiConnect();
  }
}

void setup() {
  M5.begin();
  M5.Speaker.mute();
  M5.Lcd.setFont(&FreeSans12pt7b);
  M5.Lcd.setCursor(0, FONT_BASE);
  M5.Lcd.setTextColor(WHITE, BLACK);
  M5.Lcd.setRotation(1);
  M5.Lcd.setTextWrap(false);

  int titleSize = FONT_BASE * 2;
  graphOriginY = titleSize + ((LCD_HEIGHT - titleSize) * 3 / 4);
  downloadHeight = graphOriginY;
  uploadHeight = LCD_HEIGHT - graphOriginY;

  Serial.printf("Graph origin %d\nDownload height %d\nUpload height %d\n", graphOriginY, downloadHeight, uploadHeight);

  wifiConnect();
  udp.begin(SERVICE_PORT);
}

void loop() {
  checkWifiConnected();

  bool drawText = false;
  if(udp.parsePacket()){
    udp.read((char *) &data, sizeof(netPacket));

    Serial.printf("DL %d UL %d\n", data.download, data.upload);
    drawText = true;
  }
  M5.Lcd.fillRect(graphPos, FONT_BASE * 2, GRAPH_WIDTH, LCD_HEIGHT, BLACK);

  int downloadPercent = min(max(1, data.download * downloadHeight / MAX_DOWNLOAD), graphOriginY - (FONT_BASE * 2));
  M5.Lcd.fillRect(graphPos, graphOriginY - downloadPercent, GRAPH_WIDTH, downloadPercent, RED);

  int uploadPercent = max(1, data.upload * uploadHeight / MAX_UPLOAD);
  M5.Lcd.fillRect(graphPos, graphOriginY, GRAPH_WIDTH, uploadPercent, YELLOW);

  if(drawText) {
    M5.Lcd.fillRect(0, 0, LCD_WIDTH, FONT_BASE * 2, BLACK);
     M5.Lcd.setCursor(0, FONT_BASE);
     M5.Lcd.printf("DL %.2f kb/s\nUL %.2f kb/s", (float) data.download/1000.0, (float) data.upload/1000.0);
  }

  graphPos = graphPos + GRAPH_WIDTH;
  if(graphPos > LCD_WIDTH){
    graphPos = 0;
  }

  delay(100);
}

Testing

ทีนี้เราก็จะต้องเทสว่ากราฟของเราขยับได้จริง โดยทำให้เน็ตมันโหลดขึ้นมา วิธีง่ายสุดคงเป็น speedtest

ถ้ากราฟขึ้นมาแบบนี้ก็เป็นอันใช้ได้

(Note: ความเร็วที่แสดงเป็น Kilobytes/s หรือจะตรงกับความเร็วที่แสดงในโปรแกรมดาวน์โหลด แต่โปรแกรม speedtest ส่วนมากจะแสดงผลเป็น Kilobits/s ฉะนั้นจะต้องคูณ 8 จากเลขที่แสดงบนจอด้วย)

สุดท้ายก็ไปแปะหน้า Switch ตามภาพ (M5Stack มีแม่เหล็กในตัว ส่วน power ดึงมาจาก Raspberry Pi เลย) คราวนี้ก็รู้สึกหรูขึ้นมา 10 เท่าเลย 555

Real world use

ติดเจ้าอุปกรณ์นี้มาสักพักแล้วครับ ประสบการณ์ที่เจอมาก็คือ

  • เวลากลับบ้านมาจะเห็น data use ขึ้นสูงเลยเพราะมือถือจะชอบ sync เมื่อชาร์จ + ต่อไวไฟ
  • config VPN failover อยู่ พอจะไปนอนเหลือบไปเห็นกราฟว่า upload วิ่งเต็มตลอด เลยรู้ตัวว่า BGP loop อยู่
  • ชอบดูตอนดู Streaming ถ้าเป็นพวก YouTube จะเห็นว่ามันโหลดหนักแป๊บเดียวแล้วเงียบเลย แต่ถ้าเป็น Twitch มันจะเบาๆ แต่ขึ้นตลอด
  • เมื่อวานเจอ Dota 2 โหลดแพทช์อีกแล้ว คราวนี้ก็รู้แล้วว่าเน็ตใช้อยู่จริงๆ

Future Improvements

ที่คิดว่าอยากทำคือ

  • ตอนนี้โปรแกรมอ่านค่ามีบั๊ก ถ้าค่าที่อ่านได้เกิน uint max (2^32) มันจะ serialize แล้วส่งไปไม่ได้ อาจจะต้องปรับ data size หรือทำท่าอื่นๆ
  • อาจจะมีค่าอื่นๆ ที่น่าเอามาแสดงผลอีก ก็ใช้ปุ่มบนตัว M5Stack เลื่อนเปลี่ยนโปรแกรมได้
  • การวาดกราฟทับดูยาก น่าจะมีวิธีที่ดีกว่านี้ (ตอนแรกในหัวนึกว่ามันจะเท่เหมือนเครื่องพล็อตกราฟชีพจร แต่อันนั้นหัวมันจะสว่างกว่าปกติ)

ปรากฏว่าพ่อเดินมาบอกว่า Raspberry Pi ก็ซื้อจอไว้นะ เอา Model B ไปใช้แทน B+ แล้วถอด M5Stack ไปทำอย่างอื่นเถอะ T_T ลดสเปคทุกอย่างไม่พอแถมต้อง rewrite อีกด้วย

2017: A Year in Review

ปกติแล้วบล็อค Year in review จะ subtitle ด้วยท่อนของเพลงหวานขม แต่ดูเหมือนว่ามันจะจบท่อนฮุคแล้ว ก็พอดีกับ phase ใหม่ในชีวิต ตอนนี้ยังไม่รู้ว่าจะทำธีมอะไรดี ปีนี้คงติดไว้แบบนี้ก่อน

สำหรับปีนี้ไม่รู้ว่าคิดไปเองหรือเปล่า รู้สึกว่าปีนี้เป็นปีที่ดี ถ้าคิดว่า 2014 เป็นจุดสูงสุดของชีวิตแล้ว ตอนนี้ก็คิดว่ากำลังเข้าไปแตะจุดนั้นอีกแล้วนะ

Graduation

เรียนจบแล้ว..

มันควรจะเป็นเรื่องใหญ่ เพราะทั้งชีวิตก็อยู่ในระบบการศึกษามาตลอด แต่พอถึงเวลานั้นจริงๆ เราพบว่ามันเฉยๆ มาก ตอนฝึกงานครั้งแรกยังรู้สึกว่าตื่นเต้นกว่านี้อีก

ถ้ามีน้องสงสัยว่าเรียนจบรู้สึกยังไง สำหรับคนที่ได้งานก่อนเรียนจบแล้วบอกได้ว่ารู้สึกเหมือนปิดเทอมอ่ะ คือไม่มีเรียนแล้ว ว่าง…

ทีนี้ตอนเทอม 1 บอกพ่อว่าจบแล้วจะขอนอนอยู่บ้านสักสามเดือนก่อนแล้วค่อยไปทำงาน ก็ปรากฏว่าวงในรีบจับเราเซ็นสัญญาไว้เลยได้พักอยู่แค่เดือนเดียว ตอนนี้ก็ยังรู้สึกว่าตอนนั้นน่าจะขอ 3 เดือนจริงๆ นะ มีหลายๆ อย่างอยากทำที่มันไม่สามารถ commit เวลายาวๆ ได้อีกแล้ว

Work

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

แต่สุดท้ายก็ยังเลือกที่วงในอยู่ ด้วยหลายๆ เหตุผลว่า

  • บริษัทที่มาชวนคือทีมแทบจะตั้งไข่ แต่เราไม่ใช่ co-founder ก็จะมีสิทธิ์มีเสียงน้อยหน่อย และมีที่หนึ่งจากที่เคยรับงานเค้ามาแล้วก็พบว่า technical co-founder เค้าไม่ค่อยเก่งใน stack ที่เค้าใช้ ซึ่งผมก็บอกเค้าไปตรงๆ แบบนี้ว่าปัญหาเค้าอยู่ที่ co-founder เค้านะ ผมเข้าไปผมก็ไม่รู้จะแก้ให้ยังไง
  • ที่สำคัญคือหลายๆ ที่คาดหวังว่าเราจะ build team ที่ดีได้ ซึ่งถึงเราจะมีประสบการณ์ในคอมพิวเตอร์มาเยอะแล้ว แต่การทำงานกับผู้คนก็ยังเป็นสกิลที่ยังต้องเรียนรู้อีกเยอะ
    • นั่นสิ เลือกเด็กจบใหม่ไปนี่มีความคาดหวังอะไร? คิดว่าค่าตัวถูก? หรือว่าแค่เพราะดึงตัวง่ายกว่าคนมีที่ทำงานแล้ว
  • สุดท้ายพอเราไม่ไป ก็เหมือนว่าหลายๆ ที่ ก็ได้ทีมเกรด C มาซึ่งไม่รู้ว่าเค้าหาไม่ได้เลยต้อง compromise หรือว่าเค้าไม่สามารถให้ข้อเสนอดีๆ แบบที่เราได้กับทั้งทีมได้ (มีเทพในทีมไม่ได้แปลว่าจะอัพเกรดคุณภาพของทีมขึ้นมาเป็น A ได้เลย) ก็เริ่มคิดว่าคิดถูก
    • เป็นคำถามที่ย้อนถามตัวเองเหมือนกัน ถึงจุดนึงถ้าเราทำบริษัทเอง เราจะสร้างทีมที่ดีขึ้นมาได้ยังไง

พูดถึงวงในกันบ้าง

  • ตอนนี้เป็น Junior Architect ซึ่งมันเหมือนเราถูก promote จากตำแหน่งเดิมที่ทำ product ตอนฝึกงานมาแล้ว
    • ในบริษัทมี Junior คนเดียว แต่ถ้าเทียบกับคนอื่นๆ ใน Dev team ก็นับว่าเป็น Senior
  • หลังเข้ามาวงในก็มีบริษัทอื่นๆ รวมถึง recruiter เมลมาอยู่บ้าง แต่ตำแหน่งนี่ก็ลงไปเป็นตำแหน่งธรรมดาๆ เช่น Frontend developer (แต่เราชอบเขียน Django มากกว่านะ), PHP developer (ว่าจะเอาภาษานี้ออกแล้ว ไม่ได้แตะมาหลายปี), DevOps (แต่เราเป็นคนออกข้อสอบ DevOps ของวงในนะ) ตอนนี้ก็เลยคิดว่าคงต้องใช้เวลาอีกสักพักถึงจะมีตำแหน่งสูงกว่านี้มาให้
  • ทีมเราที่วงในดีมาก เวลาประชุมกันแล้วเหมือนคุยภาษาเดียวกัน
  • โอกาสเติบโตของเราที่วงในดีมาก เช่นเราโดนส่งไปพูดข้างนอกบ้าง เขียนบล็อคบ้าง

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

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

เกม

เดี๋ยวนี้พบว่าไม่มีเวลาเล่นเกมแล้ว ถึงจะเล่นเกมอยู่ทุกวันก็ตาม

คือปกติเล่นเกมจะเล่นแบบ single setting (รวดเดียวจบ) ถ้าเป็นไปได้ แต่ก็กลายเป็นว่าเดี๋ยวนี้มีอะไรต้องทำเยอะจนไม่สามารถทำแบบนั้นได้

ที่เล่นเกมอยู่นี่ก็จริงๆ ก็ควรจะทำอย่างอื่นนั่นแหละ

ตอนนี้คิดว่าตอนเช้าๆ จะต้องรอตึกเปิด เลยอาจจะซื้อ Nintendo Switch ไปเล่นดีมั้ย แต่บางวันก็ง่วงจริงๆ นะ เล่น Monument Valley ยังจะหลับคาจอ

ไอดอล

ภาพเต็มบล็อคแล้วไม่ต้องอัพใหม่ก็ได้

รู้สึกว่าไอดอลเป็นอีกสิ่งดีๆ ที่เข้ามาในชีวิต หลังจากอนิเมะ (ถึงจะยังไม่ได้ทำอะไรดีๆ เหมือนตอนทำ CoreAnime ก็ตาม)

ปีนี้เราเห็นงานเดบิวท์ BNK48 ตามด้วย phase ที่ content ออกมาน้อยแล้วอยากได้อีก, การเปิดตัวของตู้ปลาที่ content overload แต่คุณภาพไม่ค่อยมี และสุดท้ายเราก็เลิกตามวงนี้ไป

ในขณะเดียวกันเราก็ไปเจอ Keyabingo 2 แล้วก็นั่งดูจนย้ายมาตาม Keyakizaka46 แทน

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

สำหรับปีหน้ามีแผนว่าจะไปงานจับมือสักครั้งก่อนที่จะแกรด คิดว่าซิงเกิลหน้าต้องไปให้ได้แล้ว

TipMe

ทิปมีเป็นความสำเร็จแบบไม่คาดคิดที่ 2 หลังจาก project Kyou ซึ่งก็เริ่มมาตั้งแต่ปีที่แล้วแล้วล่ะ

ปีนี้เว็บมีอะไรเจ๋งๆ เยอะมาก

  • มันทำให้เริ่มเข้าใจถึงความสำคัญของ marketing เพราะเว็บไม่ได้ทำการตลาดจริงจังเลย เป็น word of mouth ทั้งหมด ถึงจุดนึงอาจจะ flat line ได้ ตอนนี้ก็เริ่มใช้เงินบ้าง
  • มีคนบอกว่า Keep innovating or die ตอนช่วงกลางๆ ปีมารู้ตัวว่ามีคู่แข่ง ตอนนั้นเลยเข็น True Wallet ออกมา ทำแล้วก็โดนก๊อปไปไม่ถึงสามวัน (แต่เค้าทำ fee ถูกเท่าเราไม่ได้) ไปจนกระทั่งว่าเรา launch overlay เองแล้วก็เห็นเค้าเลิกขยับตัวไป ไม่รู้ว่าไม่ว่างทำต่อ หรือว่ายอมแพ้แล้ว
    • ตอนนี้ก็ยังคิดอยู่ว่าถ้ามีรายใหญ่กว่าเราเข้ามาเล่น เราจะไล่แซงเค้ายังไง หรืออาจจะต้องปล่อย เพราะ tech จะนำขนาดไหนก็แพ้งบ marketing มหาศาลอยู่ดี
  • อีกอันหนึ่งที่ปีนี้ได้ทำแล้วคือพยายามเลิกยืมจมูกคนอื่นหายใจ เว็บเกิดมาเป็น mashup ง่ายๆ แต่ถึงเวลานึงก็พบว่าพอลูกค้ามีปัญหาจาก 3rd party เราบอกได้แค่ว่าไปคุยกับทางนั้นเอง ซึ่งไม่ดีเท่าไร รวมถึงว่าถ้าเค้ามีปัญหาทางเทคนิคเราแก้อะไรไม่ได้เลย สุดท้ายก็เลยตัดสินใจว่าต้องเลิกใช้ 3rd party ให้หมด ซึ่งเป็นงานหนักมาก ก็ยังพยายามไล่ทำอยู่แต่คงจะเป็นปี
  • เราเริ่มเข้าใจการลงทุนก็ตอนนี้ คือเว็บมันไม่เคยเข้าเนื้อเลย แต่พอจะขยายแล้วก็ไม่กล้าเอาเงินหลายๆ เดือนเข้ามาใช้ (เรามองว่ามันจะเจ๊ง) ตอนนี้ก็เริ่มเข้าใจและเริ่มลงทุนบ้างแล้ว
  • มีเรื่องที่ยังเล่าไม่ได้ด้วย ถ้ามันจบไปด้วยดีเราอาจจะบล็อคให้อ่าน ตอนนี้มีบล็อค TipMe Year 1 ค้างไว้อยู่ ยังไม่ได้เขียนต่อเลย

Conference

ปีนี้เริ่มมีคนมาชวนไปพูดงานต่างๆ แล้วดันออกมาติดกันช่วงปลายปีด้วยนะ

  • พี่เดียร์ Opstra ชวนไป GDG Cloud Bangkok พูดเรื่อง Kubernetes ที่วงใน รู้สึกว่าทำได้ดีมากๆ
  • สมาคมโปรแกรมเมอร์ก็ชวนไปงาน Codemania 110 ซึ่งเรารู้สึกเป็นเกียรติมากๆ เพราะงานนี้ไม่ได้เข้าฟรี แต่ด้วยธีมงานที่มันบีบๆ ก็เลยหาหัวข้อพูดค่อนข้างยาก
  • Barcamp Bangkhen ก็ยังไปอยู่ทุกปี และเป็นงานที่จะพยายามเก็บหัวข้อที่ดีที่สุดให้ด้วย ก็ปรากฏว่าได้ 3 หัวข้อเลย
    • Kubernetes ที่วงใน ยังเอาออกมาพูดอีกรอบ
    • Internal Chatbot ของวงใน อันนี้พูดแล้วรู้สึกว่าจุดประกายหลายๆ คน แม้แต่คนในทีมมาฟังก็ยังกลับไปเพิ่มฟีเจอร์ให้บอตเรา
    • ของ TipMe พูดเรื่อง Error catching ด้วย Sentry ซึ่งทีแรกสุดจะพูดว่าทำ side project ยังไงไม่ให้กระทบงานประจำ แล้วมันจะมีหัวข้อว่า monitoring ต้องดี แต่พอทำถึงตรงนี้ก็พบว่าหัวข้อนี้หัวข้อเดียวก็เล่าได้แล้วนะ

รู้สึกว่าเริ่มเป็นที่รู้จักในวงการบ้างแล้ว เพราะเค้าไม่ได้เชิญบริษัทเราไป แต่เค้าเชิญเราไป ในขณะเดียวกันก็ต้องบอกว่าหัวข้อพวก Kubernetes นี่ถ้าไม่อยู่ที่วงในเราก็คงไม่ได้มีโอกาสมากขนาดนี้

2017 New Year Resolution

พูดเพราะ

ไม่รู้ว่าจะสรุปข้อนี้ยังไงดี คือรู้สึกว่าพูดกับเพื่อนก็พยายามลดลง แต่ก็ยังหลุดมาบ่อยอยู่ แต่ก็รู้สึกว่าบางคำมันเริ่มลดความหยาบลงแล้วนะ อย่างคำว่าแม่งนี่แต่ก่อนมันไม่ควรใช้พอๆ กับคำว่าควย ปัจจุบันรู้สึกว่ามันเป็น flavor text ที่ไม่ได้มีอะไรแล้ว

แต่ในที่ทำงาน รู้สึกว่าด้วยบรรยากาศ professional แล้วก็ไม่ค่อยได้พูดไปเอง

เอาเป็นว่าเราจะถือว่า SUCCESS เพราะถ้าพูดเพราะแบบไอดอลกับเพื่อนมันจะดูแปลกๆ ไปนะ

Backup

ผ่านไปครึ่งปี ลืมไปเลยว่าจะทำ

สิ่งที่ทำแล้วคือ auto backup บน madoka ซึ่งตอนหลังเราแทบจะเลิกใช้เครื่องนี้ไปแล้วนอกจากเขียนบล็อคกับรัน home automation ในขณะที่บน TipMe ยังไม่ได้ทำเลย มีแต่ manual backup แต่ก็คิดว่าอาจจะใช้เงินแก้ปัญหาอยู่

ส่วนไฟล์ในเครื่อง คุ้นๆ ว่าต้นปีมีโยนไฟล์ archive ไปบน cloud บ้างแล้ว แต่ไม่มี data ล่าสุด

ที่ยังไม่ได้ทำเลยและควรทำคือเครื่อง desktop นี่แหละ ถ้า disk บินปุ๊บนี่ข้อมูลจะหายไปไม่แพ้ตอนสมัย disk 500GB พังตอนม. 4 เลย

ซื้อเพลง

SUCCESS

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

ส่วนเพลงที่ซื้อปีนี้ก็เยอะพอสมควร

  • Namida no Ochiru Sokudo – nano.RIPE Limited Type A อยากได้มาหลายปีแล้ว ปีนี้เลยกัดฟันสั่งญี่ปุ่นมา เสียดายว่า Spotify ไม่มี เลยไม่ค่อยได้เปิดฟัง (ไม่ค่อยได้เปิดไฟล์ในเครื่องแล้ว)
  • BNK48 Aitakatta ซื้อมาทั้งแผ่น ทั้งบน iTunes
  • Masshiro na Mono wa Yogoshitaku naru – Keyakizaka46 อันนี้ซื้อ Type B มา และใน iTunes ด้วย (ตอนหลังมีคนซื้อ Type A มาให้)
  • Kaze ni Fukaretemo – Keyakizaka46 Type D
  • UNDERTALE soundtrack กับ Live at Grillby’s ใน Bandcamp
  • สั่ง BNK48 Koisuru Fortune Cookie ไปแล้ว
  • เพลงอื่นๆ ก็มี Sky & Sea, เพ่ง, เสน่หา, 365 (AKB48), Koisuru Fortune Cookie (AKB48), Jump (Rakuen), Planetarium (Otsuka Ai – ฟังยุยปงร้องแล้วเลยซื้อมา), Koi (Hoshino Gen อันนี้นากาซาว่าคุงร้องไว้), Uchiage Hanabi (DAOKO) อันนี้ไม่คิดว่ามีใน Spotify แต่มีเป็นชื่อญี่ปุ่น

บริจาค

SUCCESS

ตลอดปีนี้ TipMe บริจาคให้

  • python-social-auth ระบบล็อคอินของเว็บ
  • Celery ระบบ task queue ที่ใช้
  • Babel ระบบ Overlay ของเว็บใช้ React ก็เลยต้องโดเนทให้ทั้ง stack ที่ใช้บ้าง
  • Webpack
  • PyPy Python 3 เคยจะ port เว็บไปรันบน PyPy แล้ว แต่พบว่าเว็บใช้ฟีเจอร์ของ 3.6 พอสมควร ก็เลยต้องรอไปก่อน และเพราะอยากให้มันเสร็จเร็วๆ ก็เลยสนับสนุนเป็นเงินไปบ้าง
  • bcachefs อันนีให้ตอน btrfs มีประเด็น ก็หวังว่า bcachefs จะกลายเป็นที่ยอมรับเร็วๆ นี้
  • ก้าวคนละก้าว ของพี่ตูน ยกส่วนที่เป็น True Wallet ให้หมดทุกบาทเลย ตรงนี้ก็เรียกว่าเป็นงบ marketing ของเว็บก็ได้
  • Internet Archive

วิธีจัดก็คือเอารายได้เดือนนั้นๆ ตัด 5% แล้วก็เลือกว่าจะให้ใคร ส่วนมากก็จะเลือกจากว่าเว็บใช้ของเจ้านั้นๆ อยู่ และไม่ได้มีบริษัทหรือมูลนิธิใหญ่หนุนหลังอยู่ (ถึงไม่มีโดเนทให้ Django Software Foundation) อยากสนับสนุนรายเล็กๆ มากกว่า

ยังเหลืออยู่ประมาณสองเดือนที่ยังไม่ได้จัดว่าจะให้ใคร ไว้ต้องคัดเลือกอีกที

สรุปปี 2017

คิดว่าทำได้ตามเป้าแล้วนะ 3/4 อย่างนึงคือไม่ใช่แค่เขียนบล็อคแต่เราแพลนตั้งแต่ต้นปีเลยว่าจะทำยังไงให้เสร็จ พอเป็นอันที่ลืมไปอย่าง backup ก็เลยไม่ได้ทำ

2018 Prediction

พอทำงานแล้วก็รู้สึกว่าชีวิตมันไม่ได้กำหนดได้ด้วยตัวเองเท่าไรแล้ว จะเดาอนาคตก็เป็นเรื่องยากเหมือนกันแฮะ

TipMe

ถ้าไม่มีอะไรผิดคาด (หวังว่าจะไม่มี) ผมว่าปีหน้า TipMe จะโตไปกว่านี้เยอะมาก เผลอๆ อาจจะได้มีคนที่ 2 ในทีม ซึ่งตอนนี้ก็ยังอยากทำอยู่แต่ไม่มีงบ

สิ่งที่บอกวงในไว้ และก็หวังไว้เองด้วยคือคงยังไม่ต้องทำ TipMe full time

Work

โดยความรู้สึกส่วนตัวคิดว่าตำแหน่งงานที่เป็นอยู่เหมาะสมแล้วในด้านต่างๆ แต่คิดว่าบริษัทก็คงจะมี promote ให้ตามปกติอยู่แล้ว ไม่รู้ว่าปีนี้จะปลดคำว่า Junior ออกจากตำแหน่งหรือยัง

หรือจะมีใครส่งข้อเสนอที่ปฏิเสธไม่ได้มาก็ไม่รู้…

2018 New Year Resolution

พอชีวิตเดาอะไรไม่ได้แล้วก็ไม่รู้ว่าจะเขียนอะไรดี…

Inspire

เขียนบล็อคเอาไว้อันนึงยังไม่ตกผลึก เพื่อความรวดเร็วเอามาแปะเลยแล้วกัน

พอเรียนจบ อยู่ดีๆ ก็มีความคิดนึงขึ้นมา ไม่รู้ว่าเพราะอ่านสัมภาษณ์พี่ซุปหรือเปล่า

คือเรารู้สึกว่าเราโชคดีที่มาอยู่ตรงนี้ได้ ด้วยสภาวะแวดล้อมที่มันอำนวยมากๆ

  • หลายคนเขียนโปรแกรมครั้งแรกในมหาวิทยาลัย แล้วก็ไม่ชอบไปทำอย่างอื่น
  • บางคนก็เขียนแล้วไม่รุ่งเลยก็มี
  • บางคนเรียนในโรงเรียนแล้วไม่ชอบตั้งแต่ตอนนั้น เลิกไปทำอย่างอื่นเลยก็น่าจะมีไม่น้อย

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

ยังไม่รู้ว่าต้องทำยังไง คงจะได้แวะกลับไปหาคุณครูที่โรงเรียนบ่อยขึ้น แต่คิดว่าทำตอนช่วงวัยนี้ก็ยังดีอย่างนึงคือเรากับน้องยังอายุไม่ต่างกันมาก ถึงจะต่างกันขนาดว่าน้องๆ เกิดไม่ทัน Floppy disk แล้วก็ตาม

ไปงานจับมือ

พยายามไม่เขียน wishlist เสียเงิน แต่ข้อนี้จะพิเศษหน่อยตรงที่ว่าเราไม่สามารถบอกว่าเก็บเงินสักสองสามปีแล้วไปงานจับมือได้

ถึงตอนนั้นแล้วก็ไม่รู้ว่ายังจะชอบเค้าอยู่หรือเปล่า

ถึงตอนนั้นแล้วก็ไม่รู้ว่าเค้ายังอยู่หรือเปล่า

ฉะนั้นแล้วก็ควรจะไปให้ไวที่สุด ตอนนี้ก็แพลนเรื่องเงินแล้ว เหลือแค่ว่าเราจะพาตัวเองไปถึงเลนจับมือได้ยังไง

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

  1. เรามองเสมอว่าไปเที่ยวคือไปพักผ่อน ถ้าไปแบบ low budget เราไม่รู้สึกว่าพักผ่อน อยู่บ้านเล่นเกมพักผ่อนมากกว่า ฉะนั้นเครื่องบินก็คงเลือกฟูลเซอร์วิสแน่นอน นั่งแคบๆ ไม่สะใจ ที่พักยังไม่แน่ใจว่าคิดยังไงดี อาจจะมีไว้เก็บของ หรือถ้าจะนอนกลิ้งก็คงอยากนอนสัก 3 ดาว
  2. อยากได้คนช่วยอ่านญี่ปุ่นหน่อย ไม่งั้นคงหลงเลน ฉะนั้นก็คงต้องจ่ายให้อีกคนด้วย ถ้าได้คนรู้จักแถวนั้นก็ง่ายหน่อยอาจจะแค่เลี้ยงข้าว แต่ถ้าต้องพาน้องไปก็เยอะเลย
  3. ยังไม่คิดว่าจะใช้ทั้งแสนนึงหมด (แต่ก็ยังไม่ได้ลองคำนวณตามข้อข้างบนจริงๆ) ถ้าเหลือก็ได้เป็นเงินเก็บไปอีก ดีเลย