อยากส่ง int ผ่าน Serial ไว ๆ ทำไงดี :D

      เรื่องของการส่งค่าผ่าน Serial สำหรับใครบางคนคงจะเคยใช้วิธีส่งตัวแปรแบบนี้ใช่มะ "12345" คือใช้ตัวแปรที่เป็น String number ส่งไปยังปลายทาง และพอฝั่งตัวรับปลายทางนั้นก็จะแสดงค่าเป็น 49,50,51,52,53 ถ้าหากใช้ตัวแปร int รับค่าอะนะ 

int a = Serial.read();

      เพราะอะไร? เพราะค่าที่ถูกส่งไปนั้นมันเป็นค่า ascii ต่างหาก ถ้าหากเทียบจากตาราง ascii เลข '1' ที่เป็นตัวอักษรนั้นจะมีค่าเท่ากับ 49 ลองเทียบดูจากตาราง ascii ด้านล่างได้เลย


      และมันจะมีวิธีหนึ่งที่เจ้าของบล็อกคนนึงเคยใช้คือ ให้ส่ง String number มาเลยแล้วกัน แล้วมาวนลูปเก็บค่าเอาประมาณนี้



      ก็คือการวนเก็บค่าไว้ในตัวแปร char แล้วเราก็สร้างตัวแปร String มาอีกเพื่อเก็บค่า char แต่ละตัวที่รับได้มาไว้ที่ String

      สมมติส่งข้อมูลมา "1234" ฝั่งรับก็จะทำการเก็บค่าไว้ในตัวแปร char ซึ่งตัวแปร char นั้นมันเก็บค่าได้แค่ครั้งละอักษรอยู่แล้ว เพราะงั้นจึงต้องสร้างตัวแปร String ที่สามารถเก็บได้แบบเป็นข้อความ (หลาย ๆ ตัวอกษรรวมกัน) มาเก็บตัวแปร char ในแต่ละรอบของข้อมูลที่ส่งมา

      เมื่อเก็บข้อมูลใน while loop เสร็จแล้ว ก็มาดูที่ข้างล่าง while loop คือสร้างตัวแปร integer ขึ้นมา ชื่อ getData แล้วนำตัวแปร String ที่เราเก็บค่า "1234" นั้นมาแปลงล่างจาก String ให้กลายเป็น int (Convert String to int)

int getData = readData.toInt(); 


      toInt() นั้นเป็นฟังชั่นที่ช่วยแปลง String ที่เป็นตัวเลขให้กลายมาเป็น int และตอนนี้จากเดิม "1234" ก็จะกลายมาเป็น 1234 ที่เป็น int แต่ !!!!! ถ้าหากตัวเลขมีมาก ๆ ละ มีหลายชุดละ หลายคนอาจจะคิดว่าก็ใช้วิธีเดิมแบบข้างต้นสิ มันใช้ได้ครับแต่เมื่อส่งข้อมูลมาหลายชุด มันจะเก็บทันหรอ มั่นใจว่าจะเก็บข้อมูลได้ครบทุกตัวที่ส่งมาหรอ ปัญหาคือเก็บไม่ทันครับ ข้อมูลขาดหาย รับมาไม่ครบทั้งหมด หรือตกหล่น ก็เพราะมันวนลูปเก็บไม่ทันอะดิ

      แก้ปัญหาคือใส่ delay() เข้าไปซะ 5555 ใช่ครับใส่ delay() เข้าไปแล้วเก็บได้ครบทุกตัว แต่ผลที่ตามมา ช้ามากๆ กว่าจะส่งข้อมูล กว่าจะรับข้อมูลมาได้ทั้งหมด ถ้าเกิดใช้เทคนิคนี้ในการรับส่งข้อมูลในการ control พวก robot มันจะ delay มาก และมันไม่ควรเป็นอย่างนั้น มันไม่ควรช้า

      เพราะฉะนั้นทั้งหมดที่กล่าวมาข้างต้น ไม่แนะนำสำหรับคนที่ต้องการ control แบบเร็วทันใจนะ แต่ถ้าเป็นพวก control อุปกรณ์ใช้งานภายในบ้าน smart home control ก็โอเคอยู่ delay นิดหน่อยไม่ว่ากัน แต่ยังมีอีกเทคนิคนึงที่เจ้าของบล็อกคิดว่าส่งได้แบบเร็วทันใจมาก

      อันดับแรกเจ้าของบล็อกมีข้อมูลที่ต้องการส่งก่อนละกัน 1 ค่า นั่นก็คือ 1000 สร้างเป็นตัวแปรประเภท integer และเราต้องรู้ก่อนว่าตัวแปร integer นั้นเก็บค่าทั้งหมดกี่ byte ถ้าเปรียบเทียบ c กับ java ภาษา c ตัวแปรประเภท int จะเก็บค่าแค่ 2 byte ส่วน java จะเป็น 4 byte(จริง ๆ ก็ขึ้นกับ OS ด้วยเหมือนกัน) เมื่อรู้แล้วเราจะส่งค่ากันยังไงอ่ะ เพราะการส่งค่าผ่าน Serial มันส่งทีละ byte เพราะฉะนั้น int ที่มี 2 byte จึงไม่สามารถส่งได้ครบหมด

บอกไว้ก่อนเลยนะเรื่อง design เจ้าขอบบล็อกไม่เก่งแฮะ ๆ เลยวาดใส่ MS Word ซะเลย

      จากรูปพอจะเดากันออกมั้ยเอ่ยย เจ้าของบล็อกวาดรูปให้ดูเพื่อมาเปรียบเทียบค่า ถ้าใครได้เรียนเรื่อง digital มาก็จะคุ้นเคยกับเลขฐาน 2 บ้าง ในรูปนี้คือขนาดของตัวแปร integer ครับ มีทั้งหมด 2 byte 


      วิธีนี้เป็นวิธีแปลงจากเลขฐาน 2 เป็นเลข ฐาน 10 ที่เจ้าของบล็อกใช้ (ใครมีวิธีหาที่ง่ายกว่านี้ก็ช่วยแนะนำทีเน้อ คอมเม้นไว้ที่บทความนี้ได้เลย)

      ในรูปก็คือ ค่าที่เราจะใช้ที่บอกไว้ก่อนหน้านี้คือ 1000 ใช่มะ นั่นแหละ พอมามองในรูปแบบ 2 byte ก็จะได้แบบนี้ออกมา เพราะ byte แรกมันเก็บค่าตัวเลขได้สุดแค่ 256 ค่าเอง จึงมี byte ที่สองต่อมา ทำให้เก็บได้ 65536 (ไม่คิดเครื่องหมาย หรือ unsigned)

      วิธีของเจ้าของบล็อกในการหาเลขฐาน 10 ก็คือ ให้กำหนดเลขไว้ก่อนเหมือนแถวบน ตั้งแต่ bit แรกจน bit สุดท้าย (ที่ bit แรกนับจากฝั่งขวา คือ 1 และ bit สุดท้ายคือ 32768) เพราะมันจะค่อย ๆ บวกตัวของมันเองอยู่แล้ว หลังจากนั้นแถวที่ 2 คือให้ใส่เลข 1 เฉพาะค่าที่ต้องการ ค่าที่ไม่ต้องการก็ใส่ 0 ไว้ เอาค่าที่ต้องการทั้งหมดมา + กันทั้งหมด แล้วให้ได้ตัวเลข 1000 ที่เรากำหนดไว้ตั้งแต่แรก เราก็จะได้ 0000 0011 1110 1000 เหมือนในตารางออกมา ซึ่งมีค่าเท่ากับ 1000 แต่ทว่าเวลาส่งผ่าน Serial มันส่งแค่ byte เดียวเท่านั้น


      จากรูปภาพเห็นอะไรมั้ยครับ เพราะการส่งค่าผ่านทาง Serial ได้ทีละ byte มันจึงส่งไปแค่ byte แรก ซึ่ง byte แรกมีแค่ 1110 1000 ซึ่งมีค่าเท่ากับ 232 เอาแล้วไง เราต้องการส่ง 1000 แต่ค่าที่ส่งไปกับเป็น 232 ส่งยังไงก็ไม่ได้ 1000 เพราะ byte เดียวมีแค่ 256 เพราะงั้นเราจะทำอย่างงี้กันครับ


      สร้างตัวแปร int ชื่อ numSend กำหนดมาให้เท่ากับ 1000 ก็คือค่าที่เราต้องการส่งใช่ปะ สิ่งที่เราต้องทำคือสร้างตัวแปร byte มา 2 ตัว เพราะเราต้องการส่ง 2 byte บรรทัดที่ 2 อะ คือเจ้าของบล็อกสร้างตัวแปร byte มาให้เท่ากับ 1000 แล้วเลื่อน bit ไปทางขวา 8 bit

byte sendCount1 = numSend >> 8;


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


      ตอนนี้จากที่เราเลื่อน bit ไปแล้ว ดูจากตารางด้านล่าง คือเลื่อนไปทางขวา 8 bit ก็นับช่องกันเลย จากรูป ตารางที่ 2 bit ตัวที่ 9 ก็เลื่อนมาทางขวา 8 bit ก็จะกลายเป็น bit ที่ 1 ส่วน bit ที่ 10 เลื่อนมาทางขวา 8 bit ก็จะกลายมาเป็น bit ที่ 2 แล้วผู้อ่านลองมาดูจากโค๊ดที่เขียนให้ดูสิ เจ้าของบล็อกสร้างตัวแปรไว้ 2 ตัว ตัวแรกให้เก็บตัวที่เลื่อนบิต ตัวที่ 2 ก็ให้เก็บตัวแปรที่ไม่ได้เลื่อนบิต และตอนนี้เราก็มีค่าของ integer ที่ถูกแบ่งเป็น 2 byte และเก็บไว้ในตัวแปร 2 ตัวที่สร้างเรียบร้อยแล้ว ตอนนี้ตัวแปรก็จะมีค่าอยู่ประมาณนี้

      อะ แล้วเราก็ส่งค่า byte พวกนี้ออกไปเลย


      ส่งออกไปแล้ว แล้ววิธีรับละครับ จะรับยังไงเนี่ยย เอา int รับหรอ? วิธีรับจริง ๆ แล้วแต่วิธีนะ แต่วิธีเจ้าของบล็อกคือสร้างตัวแปร byte มาเหมือนกัน 2 ตัวแล้วก็ใช้ while loop มารับ


      โค๊ดตรงนี้เจ้าของเขียนไว้ใน void loop() นะ พอจะเดาออกมั้ยว่าแต่ละบรรทัดมันคืออะไร อันดับแรกเจ้าของบล็อกสร้างตัวแปร int ชื่อ count มาก่อนเลย เอาไว้นับรอบในการเก็บค่าไว้ในตัวแปร buffer ทำหน้าทีเป็น index ของ array (buffer เจ้าของบล็อกประกาศไว้เป็นตัวแปร byte[] เป็นตัวแปรแบบ Global)

      ต่อมาก็ใช้ while loop ในการเก็บค่าที่อ่านมาได้จาก Serial โดยใช้เงื่อนไขว่า Serial.available() ก็คือเมื่อมีค่าบางอย่างส่งเข้ามา จะเข้าเงื่อนไขและทำงานวนลูปจนกว่าจะไม่มีค่าส่งมา

      ต่อมาเจ้าของบล็อกก็สร้างตัวแปร int มาอีกชื่อ data เพื่อเก็บค่าที่อ่านมาได้จาก Serial.read() มาเก็บไว้ในตัวแปร int 

      เงื่อนไขต่อมาเจ้าของบล็อกจะเช็คว่าตัวแปร data ที่อ่านค่าจาก Serial.read(); มีค่าเท่ากับ 0x0A หรือไม่ (0x0A มันก็คือ '\n' นั่นเองที่เอาไว้ใช้ในการขึ้นบรรทัดใหม่) ถ้าเกิดอ่านค่าได้ก็ไม่ทำอะไร

     ต่อมาก็ใช้ else if ดักไปอีกว่า ถ้าเกิดตัวแปร data มันอ่านค่ามาแล้วเท่ากับ 0x0D หรือไม่ (0x0D มันก็คือ '\r' การรีเทิร์นค่า) ไอ่ตัว 0x0D นี้คือตัวปิดประโยคแล้วว่าข้อมูลส่งมาครบแล้ว แล้วถ้ามีข้อมูลส่งมาอีกมันก็จะเป็นข้อมูลชุดใหม่ เพราะงั้นในเงื่อนไขนี้เจ้าของบล็อกก็เลยให้ตัวแปร count ที่เจ้าของบล็อกบอกทีแรกให้มันเริ่มนับ 0 ใหม่ เพื่อรับข้อมูลชุดใหม่มาเก็บไว้ที่ตัวแปร byte[] เหมือนเดิม 

      เงื่อนไขสุดท้ายคือ else ก็คือถ้ามันไม่ใช่ทั้ง OxOA และ OxOD ก็คือจะให้ทำในเงื่อนไข ภายในเงื่อนไขนี้ก็จะให้ byte[] มาเก็บค่าจากตัวแปร data ที่อ่านค่าจาก Serial.read(); โดยอ้างอิง index จากตัวแปร count ที่กำหนดไว้เบื้องต้น แล้วก็ให้ตัวแปร count + 1 ทุกครั้งที่เข้าเงื่อนไขนี้

      โอเค ตอนนี้จบแล้วในส่วนของรับค่า ต้องขอโทษด้วยครับที่ตัวอักษรเยอะไปหน่อย เนื่องจากไม่ได้ทำภาพอธิบายทฤษฎีประกอบ เพราะส่วนนี้เป็นพื้นฐานอยู่แล้ว สำหรับคนที่พอมีพื่นฐานก็จะอ่านเข้าใจหน่อย


      สุดท้าย.. จำได้มั้ย ตอนส่ง byte มา เราทำการ shift bit ไปทางขวา 8 บิต


      และในส่วนรับนั้นก็ต้องทำการ shift bit กลับเช่นกัน


      เจ้าของบล็อกก็สร้างตัวแปร int มาเก็บค่าไว้ หลังจากที่เราเก็บค่าที่ได้จาก Serial เรียบร้อยแล้ว ก็จะเขียนกันอย่างงี้แหละเป็นวิธีของเจ้าของบล็อกนะ จะทำแบบเจ้าของบล็อกก็ได้ หรือใครมีวิธีทำที่ดีกว่า ง่ายกว่า หรือสั้นกว่า ก็ช่วยแนะนำด้วยเน้ออ :D เรามาแปลความหมายของโค้ดในรูปกัน


      อ่าให้นึกภาพว่าตอนนี้เรามีตัวแปร int ตัวนึงชื่อ b


      ตอนนี้สมมติว่าตัวแปรนี้ยังไม่มีค่าอะไรเลยแล้วกัน ประกาศมาเฉย ๆ แต่ยังไม่กำหนดว่ามันเท่ากับอะไร (แต่จริง ๆ ถ้าเราประกาศตัวแปรไว้แต่ไม่มีการกำหนดค่าตัวแปรนั้น ค่าในตัวแปรจะกลายเป็นค่าขยะ) ดูรูปภาพประกอบ


      จำได้มั้ยครับ ที่ในส่วนของส่งค่าเจ้าของบล็อกได้ส่งเลข 1000 มาแต่เนื่องจากมันส่งได้ทีละ byte เลยแยกส่งมา 2 ส่วน (1000 มี 2 byte เท่ากับ 0000 0011 1110 1000 แล้วแบ่งครึ่งส่งมาทีละ byte) จากรูปภาพนั้นเจ้าของบล็อกนำ byte มาต่อเพิ่มอีก เพราะเนื่องจากเจ้าของบล็อกสร้างตัวแปร int b มาเพื่อเก็บค่าทั้งสองเพราะ int มี 2 byte :D (ถึงจะเพิ่มมา 1 byte แต่ก็ยังค่าเท่าเดิม)


      เอารูปนี้มาดูกันใหม่ มาดูการ shift bit คืนกันนะ จากโค้ด shift bit ไปทางซ้าย 8 bit คืน แล้วหลังจากนั้นก็มา | (or) กับ byte ที่ 2


      ตอนนี้ shift bit เรียบร้อยแล้วครับ ตอนนี้ byte แรกเราก็จะเท่ากับ ตารางข้างล่างละ จาก 3 ก็จะกลายเป็น 768 ละ อ้าาา แล้วต่อมาเราก็มาทำ | (or) กับ byte ตัวที่ 2 ใครที่เรียน digital พวกลอจิกเกตมาบ้างก็จะอ๋อ ว่า | (or) มันทำอะไร

      เพิ่มเติมนิดนึงเมื่อเทียบกับในตารางก็คือการ | (or) เมื่อ A|B ถ้าหากค่าใดค่าหนึ่งเป็น 1 หรือเป็น 1 ทั้งคู่ output ก็จะกลายเป็น 1 เหมือนกัน แต่ถ้าหากเป็น 0 ทั้งคู่ output ก็จะกลายเป็น 0



      นี่ก็คือผลลัพธ์ครับเมื่อเอาค่า byte แรกที่ถูก shift bit คืนแล้วมา | (or) กับ byte ตัวที่สอง ผลที่ได้คือ เราสามารถต่อ byte กันได้แล้ว ค่าตอนนี้ก็จะได้เท่ากับ 1000 เหมือนตอนที่ส่งมาทีแรกแล้วครับ :D ใช้ทริคนี้ในการส่งค่า int จะเร็วมาก แล้วถ้าต้องการจะส่งหลายชุดก็แค่เพิ่มขนาด array มาให้เท่ากับค่าที่เราส่งมานั่นแหละ



      อันนี้ก็จะเป็นโค้ดรวม ๆ ของภาครับที่เอาไว้รับค่าจาก Serial นะครับ ส่วนภาคส่งทีแรกนั้น เจ้าของบล็อกอธิบายแค่หลักการ ส่วนโค้ดก็ปรับแต่งเป็นอย่างอื่นให้เหมาะสมได้

      บทความนี้เป็นแค่อีก 1 วิธีการในการส่งข้อมูลทาง Serial นะครับ ถึงมันจะยังไม่ดีที่สุด แต่เจ้าของบล็อกก็พยายามศึกษาแล้วอธิบายให้ผู้อ่านเห็นภาพถึงหลักการมากที่สุด ถ้าหากอ่านแล้วงง ๆ ก็ลองมาถามเจ้าของบล็อกได้เน้อ facebook หรือข้อมูลติดต่ออื่น ๆ ที่ เกี่ยวกับเจ้าของบล็อก ได้เน้อ

ความคิดเห็น

Unknown กล่าวว่า
วิธีการนี้ยังสามารถนำไปประยุกต์ใช้กับการเก็บค่าตัวแปรขนาด 16่ บิต ลงใน EEPROM ขนาด 8 บิต 2 ไบต์ ได้อีกด้วยครับ
Team กล่าวว่า
ขอบคุณมากครับที่แนะนำ :D
opobmxtong กล่าวว่า
ขอบคุณครับ

โพสต์ยอดนิยมจากบล็อกนี้

ว่าด้วยเรื่องหน่วยความจำ สิ่งที่หลายคนมองข้าม : รู้จักกับ Memory

คณิตศาสตร์กับโปรแกรมมิ่งมันเป็นยังไงนะ ตอนที่ 1 เกริ่นพีทาโกรัส

Pointer กับตัวแปร Array นะจ๊ะ.. [Back to basic แต่ไม่ basic]