IoT (Internet Of Things)

เขียนโดย ดร.จักรกฤษณ์  แสงแก้ว วันที่ 20 ธันวาคม 2562

อินเตอร์เน็ตของสรรพสิ่ง (Internet Of Things : IoT)

» Internet Of Things หรือ IoT คือ การเชื่อมโยงอุปกรณ์อิเล็กทรอนิกส์ต่าง ๆ เข้ากับอินเตอร์เน็ต ทำให้สามารถรับค่าจากเซ็นเซอร์และควบคุมอุปกรณ์เหล่านั้นผ่านอินเตอร์เน็ตได้ เช่น การเปิด/ปิดหลอดไฟ แอร์ โทรทัศน์ พัดลม เป็นต้น
» Internet Of Things (IoT) ทำให้เกิดคำศัพท์ต่าง ๆ อาทิ บ้านอัจฉริยะ เมืองอัจฉริยะ เกษตรอัจฉริยะ เป็นต้น

อุปกรณ์อิเล็กทรอนิสก์ทุกอย่างต้องใช้ไฟฟ้า

» อุปกรณ์อิเล็กทรอนิกส์ทุกชนิดต้องการไฟฟ้าเพื่อให้อุปกรณ์เหล่านั้นทำงาน หากไม่มีไฟฟ้าอุปกรณ์อิเล็กทรอนิกส์จะหยุดนิ่งไม่ทำงาน

พลังงานไฟฟ้ามาจากไหน

» พลังงานไฟฟ้าเกิดจากการตัดกันของขดลวดและแม่เหล็กถาวร (Megnatic Field)

» พลังงานไฟฟ้าจากการตัดกันของขดลวดและแม่เหล็กถาวรภายในมอเตอร์แบบ Stepper motor
» พลังงานไฟฟ้าจากโลหะสองชนิดและน้ำเกลือ (แผ่นทองแดงและอลูมิเนียมจากกระป๋องโค๊ก)
» พลังงานไฟฟ้ามาจากแสงแดด (Solar Cell)
» ทดสอบแสงแดดเพื่อสร้างกระแสไฟฟ้าจากแผ่นโซล่าเซลล์ 12V 20W วันที่ 20 ธันวาคม 2562 เวลา 7:30 น. ณ บ้านพักครูจังหวัดมหาสารคาม
» พลังงานไฟฟ้าจากเซลล์เชื้อเพลง (Fuel Cell)
» บริษัท กูเกิ้ล, Ebay, Amazon ฯลฯ เลือกใช้พลังงานไฟฟ้าจากเซลล์เชื้อเพลิง ผลิตภัณฑ์ Bloom Box ขนาด 100KW ต่อตู้แทนโซล่าเซลล์เพราะสร้างไฟฟ้าได้อย่างต่อเนื่องไม่จำกัดเฉพาะกลางวันเหมือนโซล่าห์เซลล์

ไฟฟ้าแรงดันต่ำกระแสสูง

» วัสดุที่ใช้คือตัวเก็บประจุ 2.7 โวลต์ (V) 500 ฟารัด (F) และหลอด LED สีเขียว

กระแสไฟฟ้าผ่านหม้อแปลงโวลต์สูง

» วัสดุที่ใช้คือตัวหม้อแปลงโวลต์สูงจากไฟฉายป้องกันตัว

เมื่อกระแสไฟฟ้าผ่านขดลวดภายในประตูแม่เหล็กไฟฟ้าจะเกิดสนามแม่เหล็ก

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

เมื่อกระแสไฟฟ้าผ่านขดลวดจะเกิดสนามแม่เหล็กไฟฟ้า

» วัสดุที่ใช้คือลวดทองแดง
» เมื่อจ่ายไฟฟ้าเข้าไปยังขดลวดทองแดงจะทำให้เกิดสนามแม่เหล็กและดูดแม่เหล็กและโลหะได้

เมื่อกระแสไฟฟ้าผ่านไส้ดินสอจะร้อนแดง

» วัสดุที่ใช้คือไส้ดินสอ
» เมื่อจ่ายไฟฟ้าเข้าไปยังไส้ดินสอจะทำให้เกิดความร้อน
ปล. ควันที่เกิดขึ้นมาจากการเผาไหม้ไม้ที่ติดอยู่บริเวณไส้ดินสอ

ไฟฟ้ากระแสสลับจากเครื่องเชื่อมโลหะ IGBT

» พันขอลวดรอบแกนเทอรอยด์ 1 รอบ ได้แรงดันไฟฟ้าที่ 10 โวลต์ กระแสสลับ 200 แอมป์

ทรานซีสเตอร์ (Transistor)

» ทรานซีสเตอร์เป็นการประยุกต์ใช้เทคโนโลยีสารกึ่งตัวนำ (Semi Conductor) เพื่อทำงานเป็นสวิตช์ หรือการขยายสัญญาณ
» ทรานซีสเตอร์มี 2 แบบ คือ NPN และ PNP
ในตัวอย่างนี้ใช้ทรานซีสเตอร์เบอร์ bc337 ชนิด NPN ราคาตัวละ 25 สตางค์ มาทดสอบทำหน้าที่เป็นสวิตช์
» ใช้คำค้น BC377 ในกูเกิ้ลจะทราบตำแหน่งของขาทั้ง 3 ขาของทรานซีสเตอร์ และทนแรงดันไฟฟ้าได้ 45v
» ต่อขา C เข้ากับ VCC และขา E เข้ากับ Ground โดยมีโหลดเป็นมอเตอร์
» ต่อไฟบวกของถ่านไฟฉาย 1.5v เพื่อใช้ป้อนเข้ากับขา B เพื่อเป็นสวิตช์

สาธิตการควบคุมไฟฟ้า 220 โวลต์จำนวน 4 ชุดผ่านสมาร์ตโฟน

» เมื่อสามารถควบคุมการเปิดปิดไฟฟ้าได้ สิ่งที่ตามมาคือเกิดความสะดวกสบาย เพราะอยู่ที่ไหนสามารถสั่งเปิดปิดไฟฟ้าได้ผ่านเครือข่ายอินเตอร์เน็ต
» ปัจจุบันมีอุปกรณ์ควบคุมไฟฟ้าในบ้านจำหน่ายเป็นจำนวนมาก มีหลากหลายแบรนด์ทำให้ผู้ใช้เลือกผลิตภัณฑ์ที่เหมาะสมกับความต้องการของตนได้

แนะนำบอร์ดทดลองอิเล็กทรอนิกส์ NodeMCU ESP8266

ในอดีตการเรียนรู้ไมโครคอนโทรลเลอร์ทำได้ยากเพราะอุปกรณ์ต่าง ๆ มีราคาแพง อีกทั้งต้องซื้อคอมไพล์เลอร์อีกด้วย ทำให้จำกัดเฉพาะกลุ่มคนบางกลุ่ม แต่สำหรับวันนี้เราอยู่ในยุคโลกาภิวัฒน์ ที่ข้อมูลข่าวสารสามารถส่งถึงกันได้เพียงเสี้ยววินาที โดยวันนี้เป็นการศึกษาการใช้งาน NodeMCU ราคา 61 บาท ซื้อมาจาก Shopee.com 

- ไมโครโปรเซสเซอร์ คือ หน่วยประมวลผลกลาง เวลาจะใช้งานต้องหาเมนบอร์ด แรม และอุปกรณ์ Input Output มาต่อและยังต้องติดตั้งระบบปฏิบัติการด้วยจึงใช้งานได้
- ไมโครคอนโทรลเลอร์ คือ ไอซีดิจิทัลที่สามารถทำงานด้วยตัวมันเอง โดยในตัวไมโครคอนโทรลเลอร์จะมีหน่วยประมวลผล หน่วยความจำ และหน่วยอินพุตเอาพุต (GPIO : General Purpose Input Output) เวลาจะใช้งานต้องเขียนโปรแกรม เรียกว่า "การโปรแกรม"

สั่งซื้อ NodeMCU ESP8266 จาก Shopee.com

ตำแหน่งขาภายใน ESP8266

การทำงานของหลอด LED

หลอด LED คือ ไดโอดเปล่งแสง (Light-Emitting Diode) พบเห็นได้ทั่วไปในอุปกรณ์อิเล็กทรอนิสก์ โดยหลอดLED สามารถเปล่งแสงได้เมื่อจ่ายกระแสไฟฟ้าเพียงเล็กน้อยแต่ให้ประสิทธิภาพการเรืองแสงที่ดีกว่าหลอดไฟขนาดเล็กทั่ว ๆ ไป

แรงดันไฟฟ้าที่ป้อนให้หลอด LED ประมาณ 3-5 โวลต์ โดยป้อนไฟลบเข้าที่ขา Cathode (ขาสั้น) และป้อนไฟบวกที่ Anode (ขายาว) ถ้าต่อผิดข้างหลอด LED จะไม่สว่าง 


การต่อหลอด LED

ตัวต้านทาน (Resistor)

» ตัวต้านทานมีหน่วยเป็น โอมห์ (Ohm)
» ตัวต้านทานทำหน้าที่ลดกระแสและลดแรงดันไฟฟ้าให้ผ่านได้น้อยลง
» ตัวต้านทานแบบต่าง ๆ

วงจรแบ่งแรงดัน

» สูตรวงจรแบ่งแรงดัน \(v_{out} = \frac{v_{in} \times R_{2}}{(R_{1} + R_{2})}\)
» เมื่อ \(v_{in}\) เท่ากับ 10v และต้องการ \(v_{out} เท่ากับ 10v \) จะได้ \( 10 = \frac{12 \times R2} {(R_{1} + R_{2})} \)
» ให้กำหนดค่าให้ R2 เท่ากับ 500 (เปลี่ยนค่าได้ตามต้องการ) จากนั้นคำนวณหา R1 จะได้ \( 10 = \frac{12 \times 500}{ R_{1} + 500} \) ดังนั้น R1 = 100 โอมห์
» ปล. วงจรแบ่งแรงดันจะจ่ายกระแสได้ต่ำ เนื่องจาก R1 จะต้องทนกระแสได้สูง

ใบงาน 1 : การต่อแหล่งจ่ายไฟจากบอร์ด ESP 8266 ให้กับหลอด LED

» เสียบสาย USB ระหว่างบอร์ด ESP8266 และคอมพิวเตอร์
» เชื่อมต่อสายไฟจาก vcc ไปยังขา + ของหลอด LED (ขายาว)
» เชื่อมต่อสายไฟจาก Ground ไปยังขา - ของหลอด LED (ขาสั้น)
» ผลการทดลอง คือ เกิดการเรืองแสงจากหลอด LED 

ใบงาน 2 : การควบคุมการเปิดปิดหลอด LED ด้วยภาษาไพธอน

1. เสียบสาย USB จากไมโครไพธอนเข้ากับเครื่อง Mac OS
2. กดแป้น Command ตามด้วย Spacebar จากนั้นพิมพ์คำว่า terminal > กด Enter
3. พิมพ์คำสั่ง ls /dev/cu.usbserial-* เพื่อตรวจสอบว่าพบไมโครไพธอนหรือไม่ 
4. พิมพ์คำสั่ง ต่อไปนี้เพื่อเชื่อมต่อเข้าไปยังไมโครไพธอน
screen /dev/cu.usbserial-1420  115200

เมื่อ 115200 คือความเร็วในการสื่อสารระหว่างคอมพิวเตอร์กับไมโครไพธอนบอร์ด
5. ต่อสายสั้น เข้ากราวนด์ และสายยาวเข้า GPIO 4
6. พิมพ์คำสั่ง
import machine
p=machine.Pin(4,1)
p.on()
7. พิมพ์คำสั่ง
p.off()
หมายเหตุ : คำสั่ง toggle() ใช้กลับสถานะปัจจุบันของสัญญาณ เช่น จากเดิมมีสถานะ 0 เมื่อใช้คำสั้่ง toggle() จะมีสถานะเป็น 1 เป็นต้น

ใบงาน 3 : การเขียนโปรแกรมไฟกระพริบ

import machine,time
p=machine.Pin(4,1)
while 1:
   p.on()
   time.sleep_ms(100)
   p.off()
   time.sleep_ms(100)
คำอธิบาย
» คำสั่ง import machine เป็นการขอใช้ Input/Output ของ ESP8266 
» คำสั่ง import time เป็นการขอใช้การนับเวลา
» คำสั่ง p = machine.Pin(4,1) คือ ให้ pin4 เป็น Output โดยเลข 0 คือ input (รับไฟฟ้าจาก pin 4) , 1 คือ output (ส่งไฟฟ้าออกไปยัง pin 4)
» คำสั่ง while 1: คือ วนรอบไม่สิ้นสุด
» คำสั่ง p.on() คือ กำหนดให้ pin 4 มีสถานะเป็นไฟบวก 3 โวลต์ (ทำให้หลอด LED ติด)
» คำสั่ง sleep_ms(100) คือ หน่วงเวลา 100 milli second (1000 เท่ากับ 1 วินาที)
» คำสั่ง p.off() คือ ให้ pin 4 มีสถานะเป็น Ground

ผลลัพธ์การเขียนโปรแกรมไฟกระพริบด้วยไมโครไพธอน

ใบงาน 4 : การเขียนโปรแกรมควบคุมไฟกระพริบโดยกำหนดจำนวนรอบ

import machine, time
pin4=machine.Pin(4, 1)
for i in range(10):
	pin4.on()
	time.sleep_ms(100)
	pin4.off()
	time.sleep_ms(100)

ใบงาน 5 : การเขียนโปรแกรมควบคุมไฟกระพริบ 2 ดวงเปิด/ปิดสลับกัน

import machine, time
pin4=machine.Pin(4, machine.Pin.OUT)
pin5=machine.Pin(5, machine.Pin.OUT)
while True:
	pin4.on()
	pin5.on()
	time.sleep_ms(100)
	pin4.off()
	pin5.on()
	time.sleep_ms(100)

ใบงาน 6 : การหรี่ไฟด้วย PWM

» การสร้าง PWM ต้องกำหนดความถี่และดิวตี้ไซเคิ้ล 
» ความถี่ (frequency) มีค่าระหว่าง 0 - 78125 Hz 
» Duty cycle คือ ระยะเวลาเปิดและปิด ยิ่งมีค่ามากหลอดไฟจะสว่างมาก กำหนดเป็นค่าเปอร์เซ็นต์โดยมี 1024 ค่าในช่วง 0-100% 
from machine import Pin, PWM
from time import sleep
frequency = 5000
led = PWM(Pin(4),frequency)
while True:
     for duty_cycle in range(0, 1024):
         led.duty(duty_cycle)
         sleep(0.005)

การใช้งาน Thonny

» Thonny เป็น Editor สำหรับเขียนโปรแกรมภาษาไพธอน บนบอร์ด Micropython 

ใบงาน 7 : การใช้งาน LCD 16x2 สื่อสารด้วย I2C

1. ต่อสาย 5 Volt เข้า Vcc
2. ต่อสายกราวด์ เข้า Ground 
3. ต่อ GPIO5 เข้ากับ SCL (Clock)
4. ต่อ GPIO4 เข้ากับ SDA (Data)
5. ต่อ Ground ระหว่าง ESP8266 เข้ากับกราวด์ของ LCD 16x2
6. ดาวน์โหลดไลบรารี่ลงบนบอร์ด MicroPython 1) esp8266_i2c_lcd.py 2) lcd_api.py
7. เขียนโค๊ดดังต่อไปนี้
from time import sleep_ms, ticks_ms
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)
lcd = I2cLcd(i2c, 0x27, 2, 16)
lcd.backlight_on()
lcd.clear()
lcd.putstr("Weight: 100%")
lcd.move_to(0,1)
lcd.putstr("Status: Ready !!")


แหล่งข้อมูล : 
- Esp8266 LCD ไลบรารี่ : https://github.com/dhylands/python_lcd
- https://github.com/gsampallo/micropython_examples

การต่อวงจรเพื่อใช้งาน LCD16x2 I2C

» การต่อวงจรเพื่อใช้งาน LCD16x2 I2C

การเขียนข้อความบนจอ LCD 20x4 I2C

» 20x04 IC2 ดาวน์โหลดไลบรารี่ที่นี่ lcd_2004.py
import lcd_2004

lcd=lcd_2004.lcd(0x27,5,4)  # (Address, SCL Pin, SDA Pin)
lcd.lcd_backlight(True)
lcd.lcd_clear()
lcd.lcd_print("Temperature 37",1,0)
lcd.lcd_print("Humidity: 30",2,0)
lcd.lcd_print("PM 2.5: 0.45 ug",3,0)
https://dsdi.msu.ac.th/data/articles/iot/mp4/thonny-01.mp4

ใบงาน 8 : การใช้งานเซ็นเซอร์อุณหภูมิและความชื้น DH11

DHT (Digital Humidity & Temperature) sensor
- DHT เป็นเซ็นต์เซอร์วัดอุณหภูมิและความชื้นในอากาศ ส่งข้อมูลเป็น analog
- DHT มี 2 แบบ คือ 1) DHT11 (สีน้ำเงิน) 2) DHT22 (สีขาว) ทำงานเหมือนกันแต่ DHT22 จะคำนวณซับซ้อนกว่า
- ฟังก์ชั่น temperature() คืนค่าเป็นหน่วยองศาเซลเซียส 
- ฟังก์ชั่น humidity() คืนค่าเป็นเปอร์เซ็นต์

 การต่อสายสัญญาณ 
1. ต่อ 5 โวลต์ เข้ากับ Vcc
2. ต่อกราวด์ เข้ากับ Gnd
3. ต่อ GPIO 16 เข้ากับ Output 
import dht
import machine
d = dht.DHT11(machine.Pin(4))

import dht
import machine
d = dht.DHT22(machine.Pin(4))

d.measure()
d.temperature()
d.humidity()

ใบงาน 9 : การตรวจจับ Gas

1. ต่อ VCC และ GND
2. ต่อ A0 เข้ากับ A0
3. ต่อ Ground เข้ากับ Ground
4. เขียนโค๊ดต่อไปนี้
import machine
adc = machine.ADC(0)
adc.read()

การสื่อสารแบบ Differential Signal

» Differential Signal หรือ Differential Pair  การสื่อสารแบบนี้ ข้อมูลถูกส่งผ่านสองสาย (บางครั้งมีสัญลักษณ์เป็น "+", "-" หรือ "D+", "D-") ซึ่งทำหน้าที่เป็นคู่สายส่งสัญญาณ. ตัวอย่างของการใช้งานแบบนี้ ได้แก่ USB, RS-485, CAN bus และ Ethernet
» คุณสมบัติหลักของการสื่อสารแบบ Differential:
  - สัญญาณเป็นคู่: สัญญาณส่งผ่านสองสายที่มีสัญญาณที่ตรงข้ามกัน (หนึ่งเป็นบวก "+" และอีกหนึ่งเป็นลบ "-"). ตัวอย่างเช่น, ถ้าสายหนึ่งมีสัญญาณแรงดันไฟฟ้า 5V, สายอีกหนึ่งจะมี -5V.
  - ป้องกันการรบกวน: สัญญาณแบบ Differential ทนต่อสัญญาณรบกวน (Noise) จากภายนอกได้ดี เนื่องจากการรบกวนมักจะส่งผลให้ทั้งสองสายได้รับผลกระทบในทิศทางเดียวกัน และเมื่อสัญญาณถูกประมวลผล, การรบกวนนี้จะถูกลบออก.
  - ใช้ในการสื่อสารความเร็วสูง: เทคนิคนี้มักถูกใช้ในระบบที่ต้องการความเสถียรภาพในการสื่อสารแบบ high-speed, เช่น USB หรือ Ethernet.
» การสื่อสารแบบนี้ต้องการอุปกรณ์ที่เหมาะสมทั้งที่ส่งและรับสัญญาณเพื่อจัดการกับคู่สัญญาณแบบ Differential อย่างถูกต้อง.

การเขียนโปรแกรมเชื่อมต่อกับ Wifi และอ่านข้อมูลจากเว็บและนำค่าที่ได้จากเว็บมาทำไฟกระพริบวนรอบตามจำนวนที่อ่านค่าออกมาจากเว็บ

- WiFi 3 โหมด คือ โหมด AP (Access Point) โหมด STA  โหมด AP
1) AP (Access Point) เป็นโหมดปล่อยสัญญาณ WiFi ออกไป เพื่อให้อุปกรณ์ต่าง ๆ มาเชื่อมต่อ โดยรองรับอุปกรณ์ที่เชื่อมเพียง 1 ตัวเท่านั้น  ไม่ต้องใช้งานผ่านอินเตอร์เน็ต
2) STA (Station) เป็นโหมดเชื่อมต่อกับอุปกรณ์ปล่อยสัญญาณอื่น ๆ เช่น เร้าเตอร์ โทรศัพท์มือถือที่เปิดฮอตสปอต การใช้งานในโหมดนี้นิยมใช้กับงานที่ต้องการเชื่อมต่ออินเตอร์เน็ต หรือมีการสื่อสารกับอุปกรณ์หลาย ๆ ในวงแลน ในงานด้าน IoT Smart Home และ Smart Farm มักใช้งานในโหมดนี้เพื่อส่งข้อมูลจากเซ็นเซอร์ขึ้นไปบนระบบคลาวด์และใช้โหมดนี้เพื่อเชื่อมต่อกับระบบคลาวด์รับคำสั่งมีสั่งอุปกรณ์ต่าง ๆ ให้ทำงานผ่านอินเตอร์เน็ต
3 AP+STA เป็นโหมดทำงาน AP และ STA ร่วมกัน

ใบงาน 10 : การเขียนโปรแกรมเชื่อมต่อ Access Point และอ่านข้อมูลจากเว็บไซต์

def connect_to_router():
   import network
   ssid = 'dsdi'
   password = '12345678910'
   sta_if = network.WLAN(network.STA_IF)
   ap_if = network.WLAN(network.AP_IF)
   sta_if.active(True)
   ap_if.active(False)
   sta_if.connect(ssid,password)

import machine,time
pin4=machine.Pin(4, 1) 
pin4.on()

connect_to_router()
time.sleep(1)
pin4.off()

from urllib.urequest import urlopen
data = urlopen("https://dsdi.msu.ac.th/iot/random.php").read().decode('utf-8')
time.sleep(1)
pin4.on()

for i in range(int(data)):
	pin4.off()
	time.sleep_ms(100)
	pin4.on()
	time.sleep_ms(100)

ใบงาน 11 : เขียนโปรแกรมเชื่อมต่อกับ Wifi และส่งค่าจากเซ็นเซอร์ไปยังเว็บไซต์

def connect_to_router():
   import network

   ssid = 'dsdi'
   password = '12345678910'

   sta_if = network.WLAN(network.STA_IF)
   ap_if = network.WLAN(network.AP_IF)

   sta_if.active(True)
   ap_if.active(False)
   sta_if.connect(ssid,password)

import machine,time
pin4=machine.Pin(4, 1) 
pin4.on()

connect_to_router()
time.sleep(1)
pin4.off()

import random
from urllib.urequest import urlopen

for i in range(10):
     sensor1 = random.getrandbits(8)
     pin4.off()
     send = urlopen("https://dsdi.msu.ac.th/iot/sensor.php?name=xxx&id=1&data=%d"%sensor1).read()
     time.sleep(0.25)
     pin4.on()
     time.sleep(0.25)
ปล. ตรวจสอบล็อกไฟล์ที่ลิงค์ https://dsdi.msu.ac.th/iot/esp-log.txt

การสื่อสารกับบอร์ดไมโครไพธอนด้วย uPyLoader

วัตถุประสงค์
1. สามารถสื่อสารและจัดการระบบไฟล์บนบอร์ดไมโครไพธอนด้วย uPyLoader บนวินโดวส์ได้
การดำเนินการ
1. ดาวน์โหลดโปรแกรม uPyLoader ที่ลิงค์ https://github.com/BetaRavener/uPyLoader/releases/tag/v0.1.4
2. เสียบสาย USB และ Micropython board
3. เรียกโปรแกรม uPyLoader
4. จัดการระบบไฟล์และการเอ็กซีคิวส์ผ่าน uPyLoader


สรุปผลการทดลอง
      เนื้อหาส่วนนี้เป็นการจัดการระบบไฟล์บนไมโครคอนโทรลเลอร์บอร์ด ได้แก่ การอัพโหลดไฟล์เข้าไปยังบอร์ด การแสดงรายชื่อไฟล์ การรันสคริปต์ไพธอนผ่านกราฟิกอินเตอร์เฟสด้วย upyloader เป็นต้น พบว่าสามารถจัดการไฟล์บนไมโครคอนโทรลเลอร์บอร์ดด้วยกราฟิกอินเตอร์เฟสได้สะดวก โดยโปรแกรมจะทำการสร้างไฟล์ _upload.py และไฟล์ _download.py เพื่อจัดการสื่อสารระหว่างบอร์ดและโปรแกรม uPyLoader

ใบงาน 12 : ดาวน์โหลดโปรแกรม uPyLoader

1. ดาวน์โหลดไฟล์ uPyLoader
2. เปิด notepad และวางโค๊ดใบงาน 3 และบันทึกตั้งชื่อ ex03.py บันทึกในโฟลเดอร์เดียวกับ uPyload 
3. เชื่อมต่อสาย usb และเชื่อมต่อ 
4. เลือกไฟล์ ex03.py ด้านซ้ายมือ และกด transfer เข้าไปยังไมโครไพธอน
5. เลือกไฟล์ ex03.py ด้านขวามือ และกดปุ่ม execute

ใบงาน 13 : การอ่านค่าจากโมดูลตรวจจับฝุ่น PM 2.5 และส่งค่าเก็บบนเว็บไซต์

» สั่งซื้ออุปกรณ์ไม่ทัน

ใบงาน 14 : การอ่านค่าจากเซ็นเซอร์วัดแก๊ส

» สั่งซื้ออุปกรณ์ไม่ทัน

ใบงาน 15 : การใช้งาน Blynk ควบคุม ESP8266 ด้วยสมาร์ตโฟน Android

การดำเนินงาน ; 
» ติดตั้งโปรแกรม Blynk บนระบบ Android 1) สร้างโปรเจ็คใหม่ 2) ป้อนอีเมล์เพื่อรับค่า token
» การใช้ blynk server จากผู้ให้บริการอื่นให้เลือก custom เช่น blynk.jpnet.co.th ป้อนเลขพอร์ต (9443)


» วาง button ลงบนฟอร์มและกำหนดเป็น switch digital เลือกขา GPIO 4 (เดินสายไฟมายัง GPIO 4)


» โปรแกรม Arduino ให้เขียนโค๊ดดังนี้ 
#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "รหัส token ที่ได้รับจาก blynk server ตอนสร้างโปรเจ็คใหม่";
char ssid[] = "ชื่อ wifi (ssid)";
char pass[] = "รหัส wifi";

void setup()
{
  // Debug console
  Serial.begin(9600);

  //Blynk.begin(auth, ssid, pass,IPAddress(192,168,1,5),8080);
  Blynk.begin(auth, ssid, pass,"blynk.jpnet.co.th",8080);

}

void loop()
{
  Blynk.run();
}

การใช้งาน Blynk ด้วย Micropython บน ESP8266

1. โหลดไลบรารี่ที่ https://github.com/vshymanskyy/blynk-library-python มี 2 ไฟล์ 1) BlynkLib.py 2) BlynkTimer.py โดยสร้างโฟลเดอร์ /lib และนำไฟล์ทั้งสองใส่ลงใน /lib
2. เปิดไฟล์ตัวอย่าง example/esp8266_esp32/
3. แก้ไข ssid/password ของ wifi
4. แก้ไข token 
5. สร้างแอพ วาง button -> เปลี่ยนเป็น switch -> เป็น digital->gpio4

การเปิดปิดไฟฟ้าด้วย MOSFET

» MOSFET นิยมใช้เป็นสวิตช์อิเล็กทรอนิกส์ (การใช้ไฟฟ้าแรงดันต่ำไปเปิดปิดไฟแรงดันสูงด้วยความถี่สูง)
» MOSFET ประกอบด้วย 1) ขา Gate 2) ขา Drain (+) 3) ขา Source (Gnd)
» เมื่อจ่ายแรงดันเข้าขา Gate สูงกว่าแรงดันที่ทำให้มอสเฟตทำงานจะทำให้กระแสไหลผ่านจาก Drain มายัง Source ซึ่งจะนำไปขับโหลด
» Vdss หรือ Vds คือแรงดันตกคร่อมขาด Drain และ Source
» Id คือ กระแสสูงสุดที่ไหลจาก Drain ไปยัง Source
» Rds(on) คือ ค่าความต้านทานระหว่างขา Drain และ Source (ค่า Rds x แรงดันตกค่อม ยิ่งสูงยิ่งทำให้มอเฟสร้อนมาก)
» Vgs คือ แรงดันตกค่อมขา Gate และ Source 
» Vgs(th) คือ Gate Threshold voltage คือแรงดันต่ำสุดที่ทำให้ MOSFET ทำงาน
» มอสเฟตส่วนใหญ่ 90% เรียงขาจากซ้ายไปขวา คือ 1) Gate 2) Drain 3) Source

การควบคุมความเร็วมอเตอร์และความสว่างด้วย PWM

หลักการ PWM (pulse width modulation)
» เมื่อเปิดปิดไฟฟ้ากระแสตรงอย่างรวดเร็ว เช่น เปิด 50% และปิด 50% (Duty Cycle) ด้วยความเร็วที่สูงมาก พบว่าแรงดันจะเหลือ 50% ทำให้ความสว่างของหลอดไฟหรือมอเตอร์ลดความเร็วหรือความสว่างลงมาครึ่งหนึ่ง
» ค่า Duty Cycle 60% หมายถึง การเปิดปิดไฟฟ้ากระแสตรงด้วยความถี่ 60% และปิด 40%
» ควรใส่ Fast Recovery Diode เพื่อป้องกันไฟไหลกลับก่อนมอเตอร์หยุดทำงาน ไม่ให้กระแสไฟวิ่งกลับมาทำลาย Mosfet
» ปล. ควรติดแผ่นระบายความร้อนให้กับ Heatsink เสมอ
from machine import Pin, PWM
from time import sleep

frequency = 5000
led = PWM(Pin(16), frequency)

while True:
  for duty_cycle in range(0, 1024):
    led.duty(duty_cycle)
    sleep(0.005)

การใช้งาน ESP8266 กับ P10

» P10 เป็นแผงป้ายแสดงตัวอักษรขนาด 32x16cm มีหลอด LED 512 ดวง
1. เปิด Arduino IDE
2. เลือกเมนู Sketch -> IncludeLibrary -> Manage Library -> ค้นหา DMD2 by Freetronics -> ติดตั้ง
3. ต่อสัญญาณดังภาพนี้

การใช้งาน WebRPEL

import webrepl_setup

เชื่อมต่อ wifi จาก esp8266-xxxx รหัสผ่าน micropythoN
ตรวจสอบชื่อ SSID ด้วยคำสั่งต่อไปนี้
import network;
ap = network.WLAN(network.AP_IF);
print(ap.config('essid'));

เปลี่ยน SSID และ Password
import network;
ap = network.WLAN(network.AP_IF);
ap.active(True);
ap.config(essid='DSDI8266', authmode=network.AUTH_WPA_WPA2_PSK, password='11111111');
print(ap.config('essid'));

รัน WebREPL
https://github.com/micropython/webrepl/archive/master.zip
ต่อ Wifi จาก DSDI8266 ป้อนรหัสผ่าน
url: ws://192.168.4.1:8266/ -> กด connect
ถ้าสำเร็จจะแสดง Prompt >>>


https://www.srccodes.com/setup-web-repl-esp8266-12e-connect-micro-python-prompt-repl-wifi-access-point-ap-hello-world/

การใช้งาน ESP8266 และ 28BYJ-48

import machine
from time import sleep_ms

IN1 = machine.Pin(16,1)
IN2 = machine.Pin(5,1)
IN3 = machine.Pin(14,1)
IN4 = machine.Pin(12,1)

pins = [IN1, IN2, IN3, IN4]

state = [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]

def step(a):
    IN1.value(a[0]);IN2.value(a[1]);IN3.value(a[2]);IN4.value(a[3])
    
while 1:
    for i in state:
        step(i)
        sleep_ms(2)

อัพเดท firmware

ดาวน์โหลดเฟิร์มแวร์เพื่อติดตั้งไว้ใน ESP8266
ดาวน์โหลดเฟิร์มแวร์ที่นี่ http://micropython.org/download
ปล. การอบรมใช้เฟิร์มแวร์ เวอร์ชั่น esp8266-20190125-v1.10.bin
ปล. ESP8266 เวอร์ชั่น  v1.10 1.19.1 
ปล. แหล่งดาวน์โหลดเฟิร์มแวร์สำหรับ ESP8266 https://micropython.org/download/esp8266/
การติดตั้ง esptool 
1. ติดตั้ง miniconda
2. pip install esptool
3. esptool --port  COM4 erase_flash
4. esptool --port COM4 write_flash --flash_size=detect -fm dio 0 esp8266-20190125-v1.10.bin
1. กรณีติดตั้งสำหรับ ESP8266
esptool --port  erase_flash
esptool --port  write_flash --flash_size=detect -fm dio 0 FIRMWARE.bin
เช่น
esptool --port COM4 write_flash --flash_size=detect -fm dio 0 esp8266-20190125-v1.10.bin
esptool --port COM4 write_flash --flash_size=detect -fm dio 0 esp8266-20220618-v1.19.1.bin

2. กรณีติดตั้งสำหรับ ESP32
esptool --port  erase_flash
esptool --port  --chip esp32 write_flash -z 0x1000 FIRMWARE.bin
เช่น esptool --port  COM4 --chip esp32 write_flash -z 0x1000 esp32-ota-20220618-v1.19.1.bin

หมายเหตุ : หากพบปัญหาให้กดปุ่ม Reset ค้างไว้ (ขณะที่กำลังเขียน micropython) และใช้ความเร็วเพิ่มการรับส่งเร็วขึ้นได้ ดังคำสั่งต่อไปนี้
esptool --port COM5 --baud 962100  --before default_reset --after hard_reset  --chip esp32 write_flash -z 0x1000 esp32-20190907-v1.11-291-gc69f58e6b.bin
ปล. ESP32 และ ulab สำหรับ Fast Fourier Transform เวอร์ชั่น 1.12 : esp32-spiram_idf4_ulab_sp_thread_v1.12-663-gea4670d5a_2020-07-29.bin

การสำรองและกู้คืน micropython firmware

» คำสั่งสำรอง:
  esptool -b 115200 read_flash 0 ALL firmware.bin
» การกู้คืนระบบ ให้ใช้โปรแกรม : flash_download_tool_3.9.3.zip เลือก 80MHz, SPI Mode=DIO , DoNotChgBin(/) 

ESP-IDF และ PlatformIO

» ESP-IDF และ PlatformIO เป็นเครื่องมือที่ใช้สำหรับการพัฒนาโปรแกรมสำหรับไมโครคอนโทรลเลอร์ ESP32 และชิปอื่นๆ. ทั้งสองมีความสำคัญในโลกของการพัฒนาฮาร์ดแวร์และ IoT (Internet of Things). นี่คือรายละเอียดของแต่ละตัว:
  1. ESP-IDF (Espressif IoT Development Framework):
   - ESP-IDF คือเฟรมเวิร์กพัฒนาอย่างเป็นทางการสำหรับ ESP32 และ ESP32-S ที่จัดทำโดย Espressif Systems.
   - มันให้ความสามารถในการเขียนแอปพลิเคชันสำหรับ ESP32 ในระดับต่ำ พร้อมทั้งเครื่องมือและไลบรารีต่างๆ ที่จำเป็น.
   - ESP-IDF มีคุณสมบัติที่เหมาะสำหรับการพัฒนาโปรแกรมที่ต้องการการควบคุมฮาร์ดแวร์อย่างแม่นยำและการใช้งานฟีเจอร์ขั้นสูงของ ESP32.
  2. PlatformIO:
   - PlatformIO เป็นเครื่องมือแบบครอส-แพลตฟอร์มที่ใช้สำหรับการพัฒนาโปรแกรมสำหรับไมโครคอนโทรลเลอร์และบอร์ดการพัฒนาหลายชนิด, รวมถึง ESP32.
   - มันเป็นส่วนเสริมที่ใช้งานได้กับเครื่องมือพัฒนาซอฟต์แวร์อื่นๆ เช่น Visual Studio Code หรือ Atom, และให้คุณสมบัติเช่นการจัดการไลบรารีอัตโนมัติ, การคอมไพล์ข้ามแพลตฟอร์ม, และการดีบัก.
   - PlatformIO มีความยืดหยุ่นและใช้งานได้ง่ายสำหรับนักพัฒนาที่ต้องการทำงานกับหลายๆ แพลตฟอร์มหรือต้องการสภาพแวดล้อมการพัฒนาที่มีความสามารถและมีการสนับสนุนที่กว้างขวาง.
» ESP-IDF และ PlatformIO มีชุมชนผู้ใช้ที่ใหญ่และแหล่งข้อมูลที่มีประโยชน์มากมาย, ทำให้พวกเขาเป็นเครื่องมือที่ดีสำหรับการพัฒนาโปรแกรมสำหรับ ESP32 และอุปกรณ์อื่นๆ.

เครื่องมือที่ใช้งานร่วมกับ LVGL (Light and Versatile Graphics Library) สำหรับการพัฒนาอินเทอร์เฟซผู้ใช้ (UI) บนไมโครคอนโทรลเลอร์ เช่น ESP32

» LVGL’s Built-in Simulator:
   - LVGL มี built-in simulator ที่สามารถใช้งานบน PC สำหรับการพัฒนาและทดสอบ UI. คุณสามารถใช้สิ่งนี้เพื่อสร้างและทดสอบอินเทอร์เฟซของคุณก่อนที่จะย้ายไปบนฮาร์ดแวร์จริง.
» Embedded Wizard:
   - Embedded Wizard เป็นเครื่องมือพัฒนา UI ที่มีความสามารถสูงสำหรับอุปกรณ์ embedded. มันรองรับการสร้าง UI ที่ซับซ้อนและมีฟังก์ชันกราฟิกแบบเต็มรูปแบบ.
» TouchGFX:
   - TouchGFX เป็นเครื่องมือออกแบบ UI สำหรับอุปกรณ์ embedded ที่เน้นที่การใช้งานที่เป็นมิตรกับผู้ใช้และการใช้งานกราฟิกที่มีประสิทธิภาพสูง.
» GuiLite:
   - GuiLite เป็นไลบรารี UI ขนาดเล็กที่สามารถใช้งานได้กับระบบปฏิบัติการและระบบไม่มีระบบปฏิบัติการ (non-OS). มันเหมาะสำหรับอุปกรณ์ที่มีข้อจำกัดด้านหน่วยความจำและประมวลผล.
» Crank Storyboard:
   - Crank Storyboard เป็นเครื่องมือออกแบบ UI ที่มีความสามารถสูงสำหรับอุปกรณ์ embedded. มันเน้นการพัฒนาอย่างรวดเร็วและการทดสอบ UI บนอุปกรณ์จริง.

โครงสร้างของ RP2040

» RP2040 คือ หน่วยประมวลผลกลาง Dual-Core 32-bit สถาปัตยกรรม ARM Cortex-M0+ ความเร็ว 133MHz ขนาดหน่วยความจำ 264kb ขนาด Flash สูงสุด 16MB มี 30GPIO มี 4 ADC, มี เซนเซอร์วัดอุณหภูมิ, มี UART x 2, SPI x 2, PWM x 16, Timer x 1

การใช้งาน WS2812 บนบอร์ด YD-RP 2040

» YD-RP2040 เป็นบอร์ด RP 2040 ที่พัฒนาให้มีหลอด WS2812 บนบอร์ดทดลองจำนวน 1 ดวง มีโครงสร้างดังนี้

การแสดงสี WS2812 บน YD-RP 2040

import neopixel
from time import sleep
n = 1 # จำนวนหลอด LED
d = 23  # สาย data out (หลอด led บนบอร์ดหมายเลข pin 23)
np = neopixel.NeoPixel(machine.Pin(d), n)
np[0] = (255,0,0);np.write()

# ตัวอย่างการใช้งาน: np[3] = (0,255,0); np.write()
import random
neopixel.brightness = 0.5
while 1:
   r = random.randint(0,255)
   g = random.randint(0,255)
   b = random.randint(0,255)
   for i in range(n):
      np[i] = (r,g,b);np.write();sleep(0.2)

การใช้งาน Analot to Digital Converter (ADC)

» ใน RP2040 มี ADC จำนวน 4 ชุด แต่ใช้ได้ 3 ชุด เพราะตัวที่ 4 (ADC4) ใช้อ่านอุณหภูมิ ขนาด 12bits มีค่า 4096 ความแตกต่าง
» ADC0 คือ GP26, ADC1 คือ GP27 และ ADC2 คือ GP28
» ความเร็วของ ADCคือ 2μs ที่ 500kS/s
» RP2040 ทำงานที่ 48MHz 
import machine
import utime
 
analog_value = machine.ADC(28)
 
while True:
    reading = analog_value.read_u16()     
    print("ADC: ",reading)
    utime.sleep(0.2)

การหรี่ไฟด้วย PWM(Pulse Width Modulation)

» สัญญาณ PWM(Pulse Width Modulation) 
import utime as time
from machine import Pin, PWM
pwm = PWM( Pin(25) )
# set PWM freq. to 1kHz
pwm.freq(1000)
# set counter to 0
cnt = 0
try:
    while True:
        if cnt < 256:
            duty = cnt
        else:
            duty = 511-cnt
        pwm.duty_u16( duty * 256 )
        cnt = (cnt+1) % 512
        time.sleep(0.01)
except KeyboardInterrupt:
    pass
# set duty cycle to 0 (ns)
pwm.duty_ns(0)
# deinitialize the GPIO pin used for PWM output
pwm.deinit()

การใช้งาน Timer เพื่อกระพริบด้วยความถี่ 50Hz

» คำสั่ง _thread.start_new_thread() ใช้สร้างเธรด (Thread) ทำหน้าที่สลับสถานะของ LED
» RP2040 ไมได้ทำงานแบบ RTOS (Realtime OS) ดังนั้นจะใช้ Thread ซึ่งทำงานบน CPU core 1 และ Main Thread จะทำงานบน CPU Core 0
» เนื่องจากทำงานพร้อมกันต้องป้องกันด้วย Mutex Lock ด้วยคำสั่ง _thread.allocate_lock() และใช้ตัวแปร lock และใช้ lock.acquire() และ lock.release() เพื่อใช้ทรัพยากรร่วมกันระหว่างเธรด
from machine import Pin, Timer

led = Pin( 5, Pin.OUT )

def tick(timer):
    global led
    led.toggle()

# create the hardware timer object
timer = Timer(-1) 
# configure the timer object, 10Hz tick rate, periodic mode
timer.init( freq=50, mode=Timer.PERIODIC, callback=tick )
try:
    while True:
        pass
except KeyboardInterrupt:
    pass
timer.deinit()

การใช้งาน WDT (Watchdog Timer)

» เมื่อเปิดการทำงานของ WDT (Watchdog Timer) จะต้องส่งค่าในระยะเวลาที่กำหนด หากไม่ส่งวงจรจะทำการ reset 
» ในตัวอย่างต่อกับ GPIO16 จะต้องกดปุ่มค้างไว้ในช่วงเวลาที่กำหนด จึงจะเปิดการทำงาน WDT
import utime as time
from machine import Pin, WDT

print('Press the button on GPIO-16 to enable WDT.')
button = Pin( 16, mode=Pin.IN, pull=Pin.PULL_UP )
time.sleep_ms(1000)
wdt = None
if button.value() == 0:
    # enable WDT with timeout of 2000 msec 
    wdt = WDT(timeout=2000) 
    # Note that once the WDT is running the timeout cannot be
    # changed and it cannot be stopped either.
if wdt is None:
    print('WDT is disabled.')
try:
    while wdt is not None:
        # feed the WDT to prevent it from resetting the system. 
        print('feed WDT @{} ms'.format( time.ticks_ms() ) )
        wdt.feed() 
        time.sleep(1.0)
except KeyboardInterrupt:
    pass

การนับเมื่อกดปุ่มสวิตช์ด้วย Debounching

» ใช้ GPIO5 เมื่อนำ GND แตะที่ GPIO5 จะนับเลขขึ้นทีละ 1
from machine import Pin
import time
counter=0
debounce_time=0
pin = Pin(5, Pin.IN, Pin.PULL_UP)
while True:
    if ((pin.value() is 0) and (time.ticks_ms()-debounce_time) > 300):
        counter+=1
        debounce_time=time.ticks_ms()
        print("Button Pressed")
        print("Count={}".format(counter))

การใช้ Interrupt

» ใช้ GPIO5 เมื่อนำ GND แตะที่ GPIO5 จะนับเลขขึ้นทีละ 1
» Level High คือ อินเตอร์รัปเมื่อ gpio มีสถานะ 1 (Pin.IRQ_HIGH_LEVEL)
» Level Low คือ อินเตอร์รัปเมื่อ gpio มีสถานะ 0 (Pin.IRQ_LOW_LEVEL)
» Rising Edge คือ อินเตอร์รัปเมื่อขอบขาขึ้น (Pin.IRQ_RISING)
» Falling Edge คือ อินเตอร์รัปเมื่อขอบขาลง (Pin.IRQ_FALLING)
» การใช้งานอินเตอร์รัป pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)
from machine import Pin

interrupt_flag=0
pin = Pin(5,Pin.IN,Pin.PULL_UP)
def callback(pin):
    global interrupt_flag
    interrupt_flag=1

pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)
while True:
    if interrupt_flag is 1:
        print("Interrupt has occured")
        interrupt_flag=0

การใช้ Interrupt จากภายนอก

»  ถ้าติดต่อกับอุปกรณ์ภายนอกที่เวลาค่อนข้าง sensitive ควรใช้ interrupt
» ถ้าใช้โปรแกรมทั่วไปและง่าย ๆ สามารถใช้ polling กับ button 
from machine import Pin
import time
interrupt_flag=0
debounce_time=0
pin = Pin(5, Pin.IN, Pin.PULL_UP)
led = Pin("LED", Pin.OUT)
count=0

def callback(pin):
    global interrupt_flag, debounce_time
    if (time.ticks_ms()-debounce_time) > 500:
        interrupt_flag= 1
        debounce_time=time.ticks_ms()

pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)

while True:
    if interrupt_flag is 1:
        interrupt_flag=0
        print("Interrupt Detected")
        led.toggle()
» แหล่งข้อมูลเพิ่มเติม: https://think-embedded.gitbook.io/micropython/

การควบคุมมอเตอร์ : ESP8266

from machine import Pin, PWM
from time import sleep_ms
frequency = 5000

mf = PWM(Pin(14), frequency)
mb = PWM(Pin(12),frequency)


import machine
l=machine.Pin(4,1)
r=machine.Pin(5,1)


w = 400
d = 900
def forward(n):
   global w,d
   mb.duty(0)
   mf.duty(d)
   sleep_ms(n*w)
   mf.duty(0)

def backward(n):
   global w,d
   mf.duty(0)
   mb.duty(d)
   sleep_ms(n*w)
   mb.duty(0)


# ตัวอย่างการใช้งาน 
# 1. เข้าใช้ Wifi ->  DSDI8266
# 2. เปิดโปรแกรม thonny เลือก เมนู Tools -> option -> Interpreter tab -> Micropython ESP8266 -> WebREPL -> กรอก url = ws://192.168.4.1:8266 รหัสผ่าน 11111111
# 3. ตัวอย่างการควบคุม เช่น l.off(); r.on(); forward(5); r.off()

การสื่อสารอนุกรมด้วย RP2040 และ RS458-to-TTL

» RS458 รุ่นนี้ขา Tx, Rx บนแผ่น PCB มีการพิมพ์สลับกัน ดังนั้น ต้องต่อสายส่งข้อมูลที่ช่อง Rx 
» ต่อสาย RS485.Rx <--> RP2040.pin1
» ต่อสาย RS485.Vcc <--> RP2040.Vcc
» ต่อสาย RS485.Gnd <--> RP2040.Gnd
» ต่อสาย RS485.A <--> Sensor.A
» ต่อสาย RS485.B <--> Sensor.B


from machine import Pin, UART
from time import sleep #sleep_us
uart = UART(0,baudrate=9600, rx=Pin(1), bits=8, parity=None, stop=1)

while True:
    if uart.any():
        data = uart.read(4)
        if data[0] == 252:
            np[0] = (255,0,0);np.write();
        else:
            np[0] = (0,255,0);np.write();
        print(data[0], int(data[1]), int(data[2]), int(data[3]))

การควบคุม ws2812b ด้วย RP2040

- WS2812 หรือ NeoPixel คือ หลอดไฟ LED ที่สามารถควบคุมสีได้ด้วยการโปรแกรม (RGB LED). หลอด LED ประเภทนี้มีตัวควบคุมสีที่ฝังอยู่ภายในหลอด, ทำให้สามารถควบคุมสีของแต่ละหลอดได้เป็นแบบอิสระ.
- ค่าที่ใช้ไฟภายใน WS2812 จะขึ้นอยู่กับความสว่างและสีที่ต้องการแสดง. ต่อไปนี้คือค่าที่ใช้ไฟที่ประมาณการ:
- ขณะที่ตั้งค่าสีขาว (Red, Green, Blue ทั้งหมดเป็น 255): WS2812 หนึ่งหลอดใช้พลังงานประมาณ 60 มิลลิวัตต์ (mW) หรือ 0.06 วัตต์.
- สีอื่น ๆ: ใช้พลังงานน้อยลง, ตามส่วนของสีที่สว่างขึ้น. ตัวอย่างเช่น, ถ้าแสดงแค่สีแดง, สีเขียว, หรือสีน้ำเงินด้วยความสว่างเต็มที่, จะใช้พลังงานประมาณ 20 มิลลิวัตต์ (0.02 วัตต์) ต่อหลอด.
- ถ้ามี WS2812 100 หลอด และทำให้ทั้งหมดสว่างด้วยสีขาวเต็มที่ จะใช้พลังงาน: 0.06 วัตต์ × 100 = 6 วัตต์
import neopixel
from time import sleep
n = 24 # จำนวนหลอด LED
d = 2  # สาย data out (หลอด led บนบอร์ดหมายเลข pin 23)
np = neopixel.NeoPixel(machine.Pin(d), n)
np[0] = (255,0,0);np.write()

# ตัวอย่างการใช้งาน: np[3] = (0,255,0); np.write()
import random
neopixel.brightness = 0.5
while 1:
   r = random.randint(0,255)
   g = random.randint(0,255)
   b = random.randint(0,255)
   for i in range(n):
      np[i] = (r,g,b);np.write();sleep(0.02)
ns=0.5
while 1:
   np.fill((0,255,0));np.write();sleep(ns)
   np.fill((255,255,0));np.write();sleep(ns)
   np.fill((255,0,0));np.write();sleep(ns)

การเขียนเลข 8 ลงบน ws2812b ด้วย RP2040
import neopixel
from time import sleep
n = 64
d = 2 

np.fill((0,0,0)); np.write()
p = [2,3,4,5,9,17,14,22,26,27,28,29,33,41,38,46, 50,51,52,53]
for i in p:
   np[i] = (255,0,0); np.write()

การสังเคราะห์รหัสภาพขนาด 8x8










p = []

การสังเคราะห์รหัสภาพขนาด 32x8










p = []

ตัวอย่าง RP2040: มมส. IS

import neopixel
from time import sleep
n = 8*32
d = 2 

np.fill((0,0,0)); np.write()
p=[9,10,13,14,17,18,19,20,21,22,26,33,34,35,36,37,38,49,50,53,54,57,58,59,60,61,62,69,73,74,75,76,77,78,89,90,91,94,97,99,102,108,110,112,113,114,115,116,117,118,145,146,147,148,149,150,153,154,155,156,157,158,177,178,179,182,185,188,190,193,195,196,198,201,202,203,206,226,227,228,229,234,235,236,237,]

for i in p:
   np[i] = (255,0,0); np.write()

ตัวอย่าง ESP32: มมส. IS

from machine import Pin
from neopixel import NeoPixel
from time import sleep
np = NeoPixel(Pin(15), 8*32)  # WS2812.Data Input ใช้ ESP32.GPIO15 และขนาด led 8x32 pixel

np.fill((0,0,0)); np.write()
p=[9,10,13,14,17,18,19,20,21,22,26,33,34,35,36,37,38,49,50,53,54,57,58,59,60,61,62,69,73,74,75,76,77,78,89,90,91,94,97,99,102,108,110,112,113,114,115,116,117,118,145,146,147,148,149,150,153,154,155,156,157,158,177,178,179,182,185,188,190,193,195,196,198,201,202,203,206,226,227,228,229,234,235,236,237,]

for i in p:
   np[i] = (255,0,0); np.write()
มาเรียน IS
p=[8,9,10,13,18,19,20,21,22,23,25,34,35,36,37,38,39,50,56,57,58,59,60,61,72,73,74,75,76,77,81,87,91,92,93,94,97,98,100,103,104,105,106,107,109,110,111,120,121,122,123,124,125,130,132,135,136,146,147,148,149,150,151,162,168,169,170,171,172,173,182,184,185,186,187,188,189,197,198,199,216,217,218,219,220,221,232,235,236,237,242,244,247,248,249,250,251,253,]
สารสนเทศ
p=[2,5,6,7,8,11,13,18,20,24,25,26,27,28,29,30,45,50,51,52,53,54,55,67,68,71,72,75,76,77,82,84,85,86,87,98,101,102,103,104,107,109,114,116,120,121,122,123,124,125,126,141,146,147,148,149,150,151,154,162,163,164,165,166,167,168,169,184,185,186,187,188,189,200,201,202,203,204,205,210,216,217,218,219,220,221,232,233,234,235,236,237,242,244,248,249,250,251,252,253,254,]

การรับค่าจาก switch และแก้ปัญหา debounce ด้วย RP2040

import machine

class Switch():
    """Switch Class
 
    Class for defining a switch. Uses internal state to debounce switch in
    software. To use switch, check the "new_value_available" member and the
    "value" member from the application.
    """
    def __init__(self, pin, checks=3, check_period=100):
        self.pin = pin
        self.pin.irq(handler=self._switch_change,
                     trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING)
 
        self.debounce_timer = machine.Timer(-1)
        self.new_value_available = False
        self.value = None
        self.prev_value = None
        self.debounce_checks = 0
        self.checks = checks
        self.check_period = check_period
 
    def _switch_change(self, pin):
        self.value = pin.value()
 
        # Start timer to check for debounce
        self.debounce_checks = 0
        self._start_debounce_timer()
 
        # Disable IRQs for GPIO pin while debouncing
        self.pin.irq(trigger=0)
 
    def _start_debounce_timer(self):
        self.debounce_timer.init(period=self.check_period, mode=machine.Timer.ONE_SHOT,
                                 callback=self._check_debounce)
 
    def _check_debounce(self, _):
        new_value = self.pin.value()
 
        if new_value == self.value:
            self.debounce_checks = self.debounce_checks + 1
 
            if self.debounce_checks == self.checks:
                # Values are the same, debouncing done
 
                # Check if this is actually a new value for the application
                if self.prev_value != self.value:
                    self.new_value_available = True
                    self.prev_value = self.value
 
                # Re-enable the Switch IRQ to get the next change
                self.pin.irq(handler=self._switch_change,
                             trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING)
            else:
                # Start the timer over to make sure debounce value stays the same
                self._start_debounce_timer()
        else:
            # Values are not the same, update value we're checking for and
            # delay again
            self.debounce_checks = 0
            self.value = new_value
            self._start_debounce_timer()

การใช้งานบอร์ด ESP-32 NodeMCU ESP-WROOM-32 Wi-Fi and Bluetooth Dual Core

» บอร์ด ESP-32 NodeMCU ESP-WROOM-32 Wi-Fi and Bluetooth Dual Core ราคาประมาณ 116 บาท (Usb Type C) สามารถรับแรงดันได้ 3.7-12V โดยจะมีวงจรปรับแรงดันให้กับ ESP32 ที่ระดับ 3.3v 
» เขียนโปรแกรมควบคุมได้แก่ Micropython, Javascript, C/C++ เป็นต้น

โครงสร้าง ESP-Wroom 32

» โมดูล ESP-WROOM-32 ประกอบด้วย  1) Wifi  2) Bluetooth 4.2 3) เซนเซอร์ Touch/Temperature 4) ซีพียู Dual core 160MHz 5) หน่วยความจำหลัก 512k 6) Flash 16MB 7) GPIO 36 pin  8) ADC 12bit

โครงสร้าง ESP32 และ UART Interface

» UART0 RX(GPIO3), TX(GPIO1), CTS(N/A), RTS(N/A)
» UART1 RX(GPIO9), TX(GPI10), CTS(GPIO6), RTS(GPIO11)
» UART2 RX(GPIO16), TX(GPI017), CTS(GPIO8), RTS(GPIO7)

สายสัญญาณการสื่อสารแบบ SPI

» CLK หรือ SCLK คือสัญญาณนาฬิกา
» MISO หรือ Data Output
» MOSI หรือ Data Input

บอร์ด ESP32 มีหลายยี่ห้อ ดังนี้

» DoIt Devkit v1, ESP32 Devkit, ESP-32s NodeMCU, ESP32 Thing, Wemos Lolin32, WeMos OLED , HuzzaH32 และ Wroom32 เป็นต้น

การตรวจสอบเวอร์ชั่นและสถาปัตยกรรมของฮาร์ดแวร์

import sys
if sys.platform != 'esp32':
    print("esp32 only!")
    sys.exit(0)
    
import gc
import os
import esp
import esp32
import time
import machine as mc
import ulab

def show_hw_info():
    uname = os.uname()
    mem_total = gc.mem_alloc()+gc.mem_free()
    free_percent = "("+str((gc.mem_free())/mem_total*100.0)+"%)"
    alloc_percent = "("+str((gc.mem_alloc())/mem_total*100.0)+"%)"
    stat = os.statvfs('/flash')
    block_size = stat[0]
    total_blocks = stat[2]
    free_blocks  = stat[3]
    rom_total = (total_blocks * block_size)/1024
    rom_free = (free_blocks * block_size)/1024
    rom_usage = (rom_total-rom_free)
    rfree_percent = "("+str(rom_free/rom_total*100.0)+"%)"
    rusage_percent = "("+str(rom_usage/rom_total*100.0)+"%)"
    print("ID ............:",mc.unique_id())
    print("Platform ......:",sys.platform)
    print("Version .......:",sys.version)
    print("Memory")
    print("   total ......:",mem_total/1024,"KB")
    print("   usage ......:",gc.mem_alloc()/1024,"KB",alloc_percent)
    print("   free .......:",gc.mem_free()/1024,"KB",free_percent)
    print("ROM")
    print("   total ......:", rom_total,"KB" )
    print("   usage ......:", rom_usage,"KB",rfree_percent )
    print("   Free .......:", rom_free,"KB",rusage_percent )
    print("system name ...:",uname.sysname)
    print("node name .....:",uname.nodename)
    print("release .......:",uname.release)
    print("version .......:",uname.version)
    print("machine .......:",uname.machine)

def show_ulab():
    print("ulab version {}.".format(ulab.__version__))

if __name__=="__main__":
    show_hw_info()
    show_ulab()
ผลลัพธ์
ID ............: b'\xc0I\xef\xe5>\xb0'
Platform ......: esp32
Version .......: 3.4.0
Memory
   total ......: 108.5625 KB
   usage ......: 71.70312 KB (65.57283%)
   free .......: 36.84375 KB (34.54231%)
ROM
   total ......: 2048.0 KB
   usage ......: 12.0 KB (99.41406%)
   Free .......: 2036.0 KB (0.5859375%)
system name ...: esp32
node name .....: esp32
release .......: 1.12.0
version .......: v1.12-663-gea4670d5a on 2020-07-29
machine .......: ESP32 module (spiram) with ESP32
ulab version 0.54.0.

การเขียนโปรแกรมอ่านและเขียน GPIO (General Purpose Input Output) : ESP32

from machine import Pin
p = Pin(13, Pin.OUT)
p.value(1)
p.value(0)
หมายเหตุ: ESP32 จะต้องระบุ input/output ให้ชัดเจน เช่น Pin(13,Pin.OUT) จะใช้เหมือนใน ESP8266 ด้วยคำสั่ง Pin(13,1) ไม่ได้ (1 คือ output) และการกำหนดค่าให้ pin ใช้ .value(0 หรือ 1 ต่างจาก Esp 8266 ใช้คำสั่ง .on() และ .off()

การเขียนโปรแกรมแสดงสีหลอด led neopixel : ESP32

from machine import Pin
from neopixel import NeoPixel
from time import sleep
np = NeoPixel(Pin(15), 8*32)  # WS2812.Data Input ใช้ ESP32.GPIO15 และขนาด led 8x32 pixel

r = [ (126,1,0), (114,13,0), (102,25,0), (90,37,0), (78,49,0), (66,61,0), (54,73,0), (42,85,0),  (30,97,0), (18,109,0), (6,121,0), (0,122,5), (0,110,17), (0,98,29), (0,86,41), (0,74,53), (0,62,65), (0,50,77), (0,38,89), (0,26,101), (0,14,113), (0,2,125), (9,0,118), (21,0,106),  (33,0,94), (45,0,82), (57,0,70), (69,0,58), (81,0,46), (93,0,34), (105,0,22), (117,0,10)]

r=r*16

for i in range(256):
    np[i] = r[i]
np.write()

ผลลัพธ์



ปล. การแสดงผลหลอด led neopixel แสดงสีแบบ true color นำไปประยุกต์ใช้ได้อย่างกว้างขวาง เช่น ป้ายไฟจราจร ทีวีกลางแจ้งด้วยจอ led 

การเชื่อมต่ออินเตอร์เน็ตและอ่านข้อมูลจากเว็บด้วย Esp32 LOLIN32

» 
import network

def connect_to_wifi(ssid, password):
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('Connecting to', ssid)
        sta_if.active(True)
        sta_if.connect(ssid, password)
        while not sta_if.isconnected():
            pass
    print('Network config:', sta_if.ifconfig())

connect_to_wifi("ssid","password")
import urequests
data = urequests.get('http://dsdi.msu.ac.th/iot/random.php')
print(data.text)
ปล. ไม่สามารถเข้าถึงเว็บ https://dsdi.msu.ac.th ด้วย HTTPS แต่เข้าถึงด้วย HTTP ได้

การแสดงรายชื่อ Hotspot ด้วย Esp32 LOLIN32

» การแสดงรายชื่อ Hotspot ด้วย Esp32 LOLIN32
import network

sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
networks = sta_if.scan()

for ssid, bssid, channel, rssi, authmode, hidden in networks:
    print(ssid.decode('utf-8'))

การแสดงรายชื่อ Hotspot ที่ไม่ต้องป้อนรหัสผ่านด้วย ESP32

import network

sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
networks = sta_if.scan()

SSIDs = []
for ssid, bssid, channel, rssi, authmode, hidden in networks:
    if authmode == network.AUTH_OPEN:
        if ssid.decode('utf-8') not in SSIDs:
            SSIDs.append(ssid.decode('utf-8'))

print(SSIDs)

การเชื่อม Hotspot ในเครือข่ายมหาวิทยาลัยมหาสารคามและส่งข้อมูลขึ้นเว็บเซิฟเวอร์ด้วย Esp32 LOLIN32

» ถ้าภายในมหาวิทยาลัยมีเครือข่าย wifi และไม่ต้องใส่รหัสผ่าน เราสามารถส่งข้อมูลขึ้น server ได้ด้วยเครือข่ายไร้สาย
import network

sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
networks = sta_if.scan()

SSIDs = []
for ssid, bssid, channel, rssi, authmode, hidden in networks:
    if authmode == network.AUTH_OPEN:
        if ssid.decode('utf-8') not in SSIDs:
            SSIDs.append(ssid.decode('utf-8'))
            
def connect_to_wifi(ssid, password):
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('Connecting to', ssid)
        sta_if.active(True)
        sta_if.connect(ssid, password)
        while not sta_if.isconnected():
            pass
    print('Network config:', sta_if.ifconfig())

print(SSIDs)
connect_to_wifi(SSIDs[0],"")
import urequests
data = urequests.get('http://dsdi.msu.ac.th/iot/random.php?a=1&b=2')
print(data.text)

การใช้อินเตอร์เน็ตด้วย ESP32 ร่วมกับโมดูล GSM เช่น SIM900A

» GSM ย่อมาจาก "Global System for Mobile Communications" ซึ่งเป็นระบบมาตรฐานสำหรับการสื่อสารโทรศัพท์เคลื่อนที่ดิจิทัล. เริ่มพัฒนาในยุโรปตอนต้นของยุค 1980s และในปัจจุบันเป็นมาตรฐานที่ได้รับการยอมรับและใช้งานอย่างแพร่หลายทั่วโลก.

คุณลักษณะหลักของ GSM ได้แก่:
1. การเชื่อมต่อโทรศัพท์เสียง เช่นการโทรหาผู้อื่นและการรับสาย.
2. ข้อความ SMS (Short Message Service): ส่งข้อความข้อความสั้นๆ ไปยังโทรศัพท์เคลื่อนที่อื่น.
3. บริการข้อมูล: เช่น GPRS (General Packet Radio Service) ซึ่งให้บริการการเชื่อมต่ออินเทอร์เน็ตความเร็วต่ำ.
4. การรองรับหลายความถี่: GSM ใช้หลายความถี่ต่างๆ เช่น 900 MHz หรือ 1800 MHz ขึ้นอยู่กับภูมิภาค.
5. การเข้ารหัสและความปลอดภัย: สื่อสารทางโทรศัพท์และข้อมูลจะถูกเข้ารหัสเพื่อป้องกันการต่อต้านและการเข้าถึงโดยไม่ได้รับอนุญาต.

GSM ใช้เครือข่ายเซลลูล่าร์ที่ประกอบด้วยเซลล์หลายๆ เซลล์ ซึ่งแต่ละเซลล์จะมีเสาอากาศ (Base Station) ที่จัดการการสื่อสารในเซลล์นั้น การที่เมื่อผู้ใช้งานหรืออุปกรณ์เคลื่อนที่ (เช่น โทรศัพท์เคลื่อนที่) ย้ายตัวข้ามเขตของเซลล์หนึ่งเข้าสู่เซลล์อื่น, การสื่อสารของผู้ใช้งานนั้นจะถูกโอนย้าย (หรือ "Handover") จากเสาอากาศของเซลล์แรกไปยังเสาอากาศของเซลล์ใหม่โดยไม่มีการขาดการเชื่อมต่อหรือการสัมผัสที่เป็นอย่างต่อเนื่อง
»  การตั้งค่า GPRS:
» กำหนดค่า APN ของผู้ให้บริการเครือข่ายของคุณ (ในตัวอย่างนี้ฉันใช้ "internet" ซึ่งเป็นค่าทั่วไป):
uart.write("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"\r\n")
uart.read()
uart.write("AT+SAPBR=3,1,\"APN\",\"internet\"\r\n")
uart.read()
เชื่อมต่อ GPRS:
uart.write("AT+SAPBR=1,1\r\n")
response = uart.read()
if "OK" in response:
    print("Connected to GPRS")
การเชื่อมต่อกับ HTTP หรือ HTTPS:
uart.write("AT+HTTPINIT\r\n")
uart.read()
กำหนด URL:
uart.write("AT+HTTPPARA=\"URL\",\"http://example.com\"\r\n")
uart.read()
ส่ง HTTP GET request:
uart.write("AT+HTTPACTION=0\r\n")
response = uart.read()
if "+HTTPACTION: 0,200" in response:
    print("HTTP request successful!")
อ่านผลลัพธ์:
uart.write("AT+HTTPREAD\r\n")
data = uart.read()
print(data)
ปิดการเชื่อมต่อ HTTP:
uart.write("AT+HTTPTERM\r\n")
uart.read()
การตัดการเชื่อมต่อ GPRS:
uart.write("AT+SAPBR=0,1\r\n")
uart.read()

การเขียน ESP32 ติดต่อกล้อง บันทึกลง SD Card และส่งไฟล์ขึ้นเว็บ

» การเขียนโปรแกรม Python (โดยใช้ MicroPython) บน ESP32 สำหรับการควบคุมกล้อง, บันทึกไฟล์ลง SD Card, และอัพโหลดไฟล์ขึ้นอินเตอร์เน็ตจำเป็นต้องใช้หลายๆ ส่วนประกอบและมีหลายขั้นตอนที่ต้องทำ. ข้างล่างนี้คือขั้นตอนการทำแบบย่อ:

ติดตั้ง MicroPython บน ESP32: ขั้นแรกคือติดตั้ง MicroPython firmware บน ESP32. คุณสามารถทำตามขั้นตอนใน MicroPython documentation ได้.

เชื่อมต่อกล้อง: ขึ้นอยู่กับรุ่นและประเภทของกล้องที่คุณใช้. คุณจำเป็นต้องตรวจสอบว่ามีไลบรารีสำหรับการควบคุมกล้องที่คุณใช้บน MicroPython หรือไม่.

การบันทึกไฟล์ลง SD Card: ต้องต่อ SD Card กับ ESP32 ผ่าน SPI และใช้ไลบรารี sdcard.py สำหรับการควบคุม.

อัพโหลดไฟล์ขึ้นอินเตอร์เน็ต: คุณสามารถใช้ไลบรารี urequests สำหรับการส่ง HTTP request เพื่ออัพโหลดไฟล์.

ข้างล่างนี้คือรหัสตัวอย่าง (ย่อ) ที่รวมทุกอย่างเข้าด้วยกัน:
import network
import sdcard
import machine
import os
import urequests

# ตั้งค่า WiFi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('your_ssid', 'your_password')
while not wlan.isconnected():
    pass

print('Connected to WiFi')

# ตั้งค่า SD Card
spi = machine.SPI(1, sck=machine.Pin(18), mosi=machine.Pin(23), miso=machine.Pin(19))
sd = sdcard.SDCard(spi, machine.Pin(4))
os.mount(sd, "/sd")

# ถ่ายภาพ (ขึ้นอยู่กับไลบรารีกล้องที่คุณใช้)
# ...

# บันทึกภาพลง SD Card
with open('/sd/image.jpg', 'wb') as f:
    f.write(image_data)

# อัพโหลดภาพขึ้นอินเตอร์เน็ต
url = 'https://your_server/upload_endpoint'
with open('/sd/image.jpg', 'rb') as f:
    response = urequests.post(url, data=f.read())

print(response.text)

การถ่ายภาพและอัพโหลดโดยไม่บันทึกลง SD Card ด้วย ESP32 และ Python

» การอัพโหลดไฟล์ขึ้นเว็บโดยไม่ต้องบันทึกภาพสามารถทำได้โดยการส่งข้อมูลภาพที่ได้จากกล้องโดยตรงไปยังเว็บเซิร์ฟเวอร์โดยไม่ต้องบันทึกลง SD Card ซึ่งขั้นตอนนี้จะช่วยประหยัดเวลาในการอัพโหลดภาพและประหยัดทรัพยากรของบอร์ด ESP32 ที่ใช้สำหรับการทำงานอื่น ๆ
import network
import urequests

# การติดตั้ง WiFi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('your_ssid', 'your_password')
while not wlan.isconnected():
    pass

print('Connected to WiFi')

# ถ่ายภาพโดยใช้กล้องที่เชื่อมต่อกับ ESP32
# สมมติว่าคุณมีฟังก์ชั่น capture_image() ที่จะถ่ายภาพและคืนค่าข้อมูลภาพ
image_data = capture_image()

# อัพโหลดภาพโดยตรงไปยังเว็บเซิร์ฟเวอร์
url = 'https://your_server/upload_endpoint'
headers = {'Content-Type': 'image/jpeg'}
response = urequests.post(url, headers=headers, data=image_data)

print(response.text)

การเชื่อมต่อโมดูล GPS กับ ESP32

» เมื่อเราใช้ ESP32 ร่วมกับโมดูล GPS โดยใช้ MicroPython, ขั้นตอนที่จำเป็นคือ:

1. เชื่อมต่อโมดูล GPS กับ ESP32
2. โปรแกรม MicroPython เพื่ออ่านข้อมูลจากโมดูล GPS

### 1. เชื่อมต่อโมดูล GPS กับ ESP32:

โดยทั่วไป, โมดูล GPS มักจะมีการสื่อสารผ่าน UART (Serial). ดังนั้น, เราต้องเชื่อม RX และ TX ของโมดูล GPS กับ TX และ RX ของ ESP32 ตามลำดับ.

ตัวอย่างการเชื่อมต่อ:

- GPS VCC -> ESP32 3.3V
- GPS GND -> ESP32 GND
- GPS TX -> ESP32 RX (e.g., GPIO16)
- GPS RX -> ESP32 TX (e.g., GPIO17)

### 2. โปรแกรม MicroPython:
from machine import UART
import time

# สร้าง UART object โดยใช้ UART2 และระบุค่า baudrate
uart = UART(2, baudrate=9600, tx=17, rx=16)

def read_gps():
    while True:
        if uart.any():
            gps_data = uart.readline()
            print(gps_data.decode('ascii'))

# เรียกใช้ฟังก์ชัน
read_gps()
**หมายเหตุ**: โมดูล GPS แต่ละรุ่นอาจมีค่า baudrate ที่แตกต่างกัน จึงต้องตรวจสอบในเอกสารของโมดูล GPS ว่าใช้ค่า baudrate เท่าไร
ตัวอย่างข้างต้นจะเรียกใช้ข้อมูล GPS ที่ถูกส่งมาทาง UART และแสดงผลบน console. ท่านจะเห็นข้อมูลในรูปแบบ NMEA ซึ่งเป็นรูปแบบมาตรฐานของข้อมูล GPS. 
หากต้องการเปลี่ยนข้อมูล NMEA เป็นข้อมูลพิกัดที่เข้าใจง่าย, คุณอาจต้องใช้ library หรือฟังก์ชันเพิ่มเติมเพื่อแปลงข้อมูลนี้.

การเขียน ESP32 ด้วย Arduino IDE (ภาษา C/C++)

» เลือก File -> Preference -> ที่ Additional Board Manager URLs: ใส่ลิงค์ต่อไปนี้ https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
» เลือก Tools -> Board -> Board Manager -> พิมพ์ ESP32 -> OK
» เลือก Tools -> Port -> เลือกพอร์ต
» เลือก Tools -> Programmer -> เลือก USBTinyISP -> OK

การเชื่อม WiFi ด้วย ESP32 + Arduino IDE (C/C++)
#include <WiFi.h>

const char* ssid     = "Your_SSID";      // Your WiFi SSID
const char* password = "Your_PASSWORD";  // Your WiFi Password

void setup() {
  Serial.begin(115200);                  // Start the serial communication
  delay(10);

  // Connect to Wi-Fi
  Serial.println();
  Serial.println("Connecting to WiFi...");
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // Print local IP address
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  // Nothing to do here
}
เลือก Tools -> Serial Monitor 
» ใน ESP32 ด้วย Arduino IDE, ไลบรารี WiFi.h มีฟังก์ชั่นสำหรับการจัดการกับการเชื่อมต่อ WiFi หลายฟังก์ชั่น เช่น:
» การเริ่มต้นการเชื่อมต่อ WiFi: คำสั่ง WiFi.begin(ssid, password); เป็นการเริ่มต้นการเชื่อมต่อ WiFi ด้วย SSID และรหัสผ่าน
» การตรวจสอบสถานะของการเชื่อมต่อ: คำสั่ง WiFi.status(): รับสถานะของการเชื่อมต่อ WiFi
» การจัดการ IP Address: คำสั่ง WiFi.localIP(): รับ IP address ของ ESP32 ที่ได้รับจากเราเตอร์
» WiFi.gatewayIP(): รับ IP address ของ Gateway
» WiFi.subnetMask(): รับ Subnet mask
» การจัดการ DNS: คำสั่ง WiFi.dnsIP(): รับ IP address ของ DNS server
» การจัดการ MAC Address: คำสั่ง WiFi.macAddress(): รับ MAC address ของ ESP32
» การสแกน SSID ที่มีอยู่รอบ ๆ: คำสั่ง WiFi.scanNetworks(): สแกนและรายงานจำนวนของเครือข่าย WiFi ที่พบ
» การตัดการเชื่อมต่อ: คำสั่ง WiFi.disconnect(): ตัดการเชื่อมต่อจาก WiFi
» การจัดการโหมดการทำงาน: คำสั่ง WiFi.mode(): ตั้งหรือรับโหมดการทำงานของ WiFi (เช่น WIFI_AP, WIFI_STA, ฯลฯ)
» การจัดการเซิร์ฟเวอร์และไคลเอ็นต์: คำสั่ง WiFiServer และ WiFiClient: คลาสสำหรับการจัดการการเชื่อมต่อแบบเซิร์ฟเวอร์และไคลเอ็นต์
» การจัดการ RSSI: คำสั่ง WiFi.RSSI(): รับค่า RSSI ของการเชื่อมต่อ WiFi ปัจจุบัน

การเขียน ESP32 ด้วย Arduino IDE (ภาษา C/C++) แสดงรายชื่อ SSID ที่ไม่ต้องป้อนรหัสผ่าน

» การสแกน SSID ที่ไม่มีรหัสผ่านบน ESP32 ด้วยภาษา C/C++ โดยใช้ Arduino IDE สามารถทำได้ด้วยการตรวจสอบ encryptionType ของแต่ละเครือข่ายที่สแกนพบ เพื่อดูว่ามีการเข้ารหัสหรือไม่ ดังตัวอย่างโค้ดด้านล่าง:
#include 

const int MAX_NUM_SSID = 10;  // กำหนดจำนวน SSID สูงสุดที่จะเก็บ
char* openSSIDs[MAX_NUM_SSID];
int openSSIDCount = 0;

void setup() {
  Serial.begin(115200);
  
  // เริ่มการสแกน SSID
  int numNetworks = WiFi.scanNetworks();

  for (int i = 0; i < numNetworks; i++) {
    // ตรวจสอบว่าเครือข่ายนี้ไม่มีการเข้ารหัส
    if (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) {
      openSSIDs[openSSIDCount] = strdup(WiFi.SSID(i).c_str());
      openSSIDCount++;

      // หยุดเก็บข้อมูลเมื่อถึงจำนวนที่กำหนด
      if (openSSIDCount >= MAX_NUM_SSID) {
        break;
      }
    }
  }

  // พิมพ์รายชื่อ SSID ที่ไม่มีรหัสผ่าน
  for (int i = 0; i < openSSIDCount; i++) {
    Serial.println(openSSIDs[i]);
  }
}

void loop() {
  // ทำงาน
}

การเชื่อมต่อ ESP32 กับ WiFi และอ่านค่าจากเว็บด้วย HTTP

» การอ่านค่าจากเว็บจะมีแบบ HTTP และ HTTPS
#include 
#include 

const char* ssid = "DSDI";
const char* password = "11111111111";
const char* url = "http://dsdi.msu.ac.th/iot/random.php";

void setup() {
    Serial.begin(9600);
    Serial.println("Connecting to WiFi...");

    // เชื่อมต่อ WiFi
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println();
    Serial.println("Connected to WiFi!");
}

void loop() {
    if (WiFi.status() == WL_CONNECTED) { // ตรวจสอบว่ายังเชื่อมต่ออยู่
        HTTPClient http;
        
        http.begin(url); 
        int httpCode = http.GET(); // รับค่าจากเว็บ

        if (httpCode > 0) { 
            String payload = http.getString(); // ดึงข้อมูลที่ได้จากเว็บ
            Serial.println(payload);
        }

        http.end(); // ปิดการเชื่อมต่อ
    }
    delay(5000); // รอ 5 วินาทีก่อนเรียกข้อมูลใหม่
}

การเชื่อมต่อ ESP32 กับ WiFi และอ่านค่าจากเว็บด้วย HTTPS

» เพื่อเชื่อมต่อ ESP32 กับ WiFi และอ่านค่าจากเว็บ, คุณต้องใช้ WiFi.h และ HTTPClient.h ซึ่งเป็น libraries ที่มากับ Arduino core สำหรับ ESP32.
» ด้านล่างเป็นตัวอย่างโค้ดสำหรับการทำเช่นนั้น:
#include 
#include 

const char* ssid = "DSDI";
const char* password = "11111111111";
const char* url = "https://dsdi.msu.ac.th";
const char* test_endpoint = "/iot/random.php";

// ใส่ fingerprint ของเว็บไซต์
const char* fingerprint = "A8:98:5D:3A:65:E5:E5:C4:B2:D7:D6:6D:40:C6:DD:2F:B1:9C:54:36"; 

void setup() {
    Serial.begin(9600);
    Serial.println("Connecting to WiFi...");

    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println();
    Serial.println("Connected to WiFi!");

    WiFiClientSecure client;
    if (!client.connect(url, 443)) {
        Serial.println("Connection failed!");
        return;
    }

    // ตรวจสอบ fingerprint
    if (client.verify(fingerprint, url)) {
        Serial.println("Connection secured.");
    } else {
        Serial.println("Connection unverified! Disconnecting...");
        client.stop();
        return;
    }

    client.print(String("GET ") + test_endpoint + " HTTP/1.1\r\n" +
               "Host: " + url + "\r\n" + 
               "Connection: close\r\n\r\n");

    while (client.connected()) {
        String line = client.readStringUntil('\n');
        if (line == "\r") {
            break;
        }
    }

    while (client.available()) {
        String line = client.readStringUntil('\n');
        Serial.println(line);
    }

    client.stop();
}

void loop() {
    // Nothing in loop
}
» fingerprint หรือ SHA-1 certificate fingerprint คืออักขระเฉพาะของ SSL certificate ที่ใช้บนเว็บไซต์. ESP32 ใช้ fingerprint นี้เพื่อตรวจสอบว่าเว็บไซต์ที่มันกำลังเชื่อมต่อนั้นเป็นแท้จริงและมีความปลอดภัย.
» การรับ fingerprint สำหรับเว็บไซต์:
» ใช้เบราว์เซอร์ firefox บนคอมพิวเตอร์ของคุณเพื่อเปิดเว็บไซต์ที่คุณต้องการ (ในกรณีนี้คือ https://dsdi.msu.ac.th).
» คลิกที่ 🔒 หรือสัญลักษณ์ความปลอดภัยที่อยู่ฝั่งซ้ายของ URL.
» คลิกที่ Certificate (valid) หรือคำสัมมนาที่คล้ายกัน.
» ไปที่แท็บ Details.
»  ค้นหา SHA-1 fingerprint หรือ Thumbprint ในภาษาอังกฤษ.
» คัดลอกค่า fingerprint นั้นและใส่ลงในโค้ดของคุณ.
» เมื่อได้ fingerprint แล้ว, คุณสามารถใส่ค่านั้นในตัวแปร fingerprint ในโค้ด ESP32 ของคุณ.
» หมายเหตุ: สิ่งที่ควรระวังคือ fingerprint จะเปลี่ยนแปลงเมื่อ SSL certificate ของเว็บไซต์หมดอายุและได้รับการต่ออายุ. ดังนั้น, หากโค้ดของคุณเชื่อมต่อกับเว็บไซต์แล้วไม่ทำงานในอนาคต, คุณอาจจะต้องตรวจสอบและอัปเดต fingerprint ใหม่.

การอ่านและถอดรหัส QR Code ด้วย ESP32 + Arduino IDE

» 2 บริษัท 1) ธนาคารกรุงเทพ โอนครั้งละ 1 บาท โอนในวันถัดไป 2) GB Prime Pay ค่าธรรมเนียมร้อยละ 0.8 บาท ระยะเวลาโอนเงิน 7 วัน เช็คหลังบ้านได้ว่ามีเงินเข้า สั่งตรงมาจาก server เพื่อเปิดปิดเซอร์โวเลย
» แอ็ปแม่มณีและพร้อมเพย์ ต้องมาดักจับข้อความ
» หลังจากโอนเสร็จแล้ว จะมีรูป QR code แนบมาด้วย ให้นำไปสแกนจากกล้อง ESP32 camera และถอดข้อความออกมาจึงจะตอบได้ว่าโอนมาแล้ว รหัสคำนี้คือคำว่าอะไร ?
» EMV QRCPS Merchant Presented Mode เป็นมาตรฐานของระบบ QR Code Payment ที่ถูกพัฒนาโดย EMVCo, ซึ่งเป็นบริษัทที่ก่อตั้งขึ้นโดยบริษัทบัตรเครดิตหลักๆ เช่น MasterCard, Visa, American Express และอื่นๆ เพื่อการพัฒนามาตรฐานสำหรับการชำระเงินด้วยบัตรเครดิตและเดบิต

» ภายในมาตรฐาน EMV QR Code Payment Specification (QRCPS) มีการกำหนดสองโหมดหลักๆ คือ:
1. **Merchant Presented Mode (MPM)**: ซึ่งเป็นโหมดที่ผู้ขาย (Merchant) แสดง QR code ให้ลูกค้า (Customer) สแกนเพื่อดำเนินการชำระเงิน. กล่าวคือ, ร้านค้าหรือผู้ขายจะแสดง QR Code และลูกค้าจะใช้แอปฯ บนสมาร์ทโฟนของตนเพื่อสแกนและยืนยันการชำระเงิน.
2. **Consumer Presented Mode (CPM)**: ซึ่งเป็นโหมดที่ลูกค้าแสดง QR code ให้ผู้ขายสแกนเพื่อดำเนินการชำระเงิน.

» EMV QRCPS Merchant Presented Mode (MPM) จึงหมายถึงการที่ผู้ขายแสดง QR Code ที่ได้รับการสร้างขึ้นตามมาตรฐานของ EMVCo และลูกค้าจะสแกน QR Code นี้เพื่อดำเนินการชำระเงิน. การใช้มาตรฐานนี้จะช่วยให้การชำระเงินด้วย QR Code มีความปลอดภัย, เพราะว่ามันถูกออกแบบมาในรูปแบบที่ยากต่อการปลอมแปลง และสามารถรองรับการทำรายการทั่วโลกได้.
» มาตรฐาน EMVco คือ หมายเลขประจำฟิลด์ข้อมูล (เป็น 00-99), ความยาวของข้อมูลในฟิลด์นั้น (เป็น 01-99), และตัวข้อมูลจริงๆ การอ่านข้อมูลตัวอย่างจะทำเป็นขั้นได้ดังนี้
  - หมายเลขเวอร์ชั่น ฟิลด์ 00 ความยาว 02 ข้อมูลตอนนี้คือ 01 เสมอ ดังนั้นข้อมูลชุดแรกคือ "000201"
  - ประเภทของ QR ฟิลด์ 01 ความยาว 02 ข้อมูล "11" แปลว่า QR นี้สร้างขึ้นเพื่อใช้สำหรับการขายหลายครั้ง เช่นระบุผู้ขาย หรือระบุราคาสินค้า ถ้าเป็น 12 คือ QR สร้างขึ้นเพื่อใช้ครั้งเดียว เช่น เป็นการจ่ายสำหรับใบเสร็จใบเดียว
  - ข้อมูลผู้ขาย (merchant account information) ฟิลด์ 29 ความยาว 37 ข้อมูล "0016A00000067701011101130066000000000" โดยข้อมูลนี้แบ่งออกเป็นสองฟิลด์ย่อย
  - หมายเลขแอปพลิเคชั่น (application ID - AID) เป็นหมายเลขที่ปกติแล้วใช้อ้างอิงประเภทบัตรสมาร์ตการ์ดแบบต่างๆ ตั้งแต่บัตรประชาชนไปจนถึงบัตรเครดิตทั้งหลาย ในกรณีนี้มีการนำหมายเลขนี้มาใช้ใน QR เพื่อระบุว่า QR นี้เป็น PromptPay หมายเลขฟิลด์ย่อย 00 ความยาว 16 ข้อมูล "A000000677010111"
  - หมายเลขบัญชี เป็นหมายเลข PromptPay โดยตรง ตอนนี้มีหมายเลขฟิลด์ เช่น
  - 01 หมายเลขโทรศัพท์ ความยาว 13 นำหน้าด้วย 00 แล้วตามด้วยรหัสประเทศ 66 แล้วจึงเป็นหมายเลขโทรศัพท์ตัดศูนย์นำหน้าออก 00-000-0000
  - 02 หมายเลขบัตรประชาชนไม่มีขีดคั่น
  - ประเทศ ฟิลด์ 58 ความยาว 02 ข้อมูล "TH" หมายถึงประเทศไทย
  - สกุลเงินที่ใช้งาน ฟิลด์ 53 ความยาว 03 ข้อมูล "764" โดยหมายเลข 764 เป็นหมายเลขประจำค่าเงินบาทตาม ISO 4217
  - ค่า check sum ฟิลด์ 63 ความยาว 04 ข้อมูล "8956" ข้อมูลนี้ต้องอยู่ท้ายสุดเสมอ โดยค่า check sum เป็นการคำนวณจากข้อมูลทั้งหมด รวมถึงหมายเลขฟิลด์ของ check sum และความยาวของฟิลด์ check sum เอง กระบวนการหาค่า check sum ใช้ CRC-16 และ ค่าคงที่ polynomial 0x1021 (XMODEM) พร้อมกับค่าเริ่มต้น 0xFFFF (อันนี้ต้องระวังเพราะไลบรารีส่วนมากมักใส่ค่าเริ่มต้นเป็น 0x0000) ตัวไลบรารีสามารถใช้ pycrc16 ได้โดยตรง
» มาตรฐานในธุรกรรมการชำระเงินด้วย Thai QR Code  
» การสร้าง QR Code สำหรับ PromptPay ด้วย Python นั้นสามารถทำได้โดยใช้ไลบรารีต่าง ๆ ที่มีพร้อมใช้งาน ต่อไปนี้เป็นตัวอย่างโค้ด Python สำหรับการสร้าง QR Code สำหรับ PromptPay:
» การติดตั้งจากซอร์สโค๊ด
    git clone https://github.com/jojoee/promptpay
    cd promptpay
    python setup.py install
» ติดตั้ง pip install promptpay==1.1.7
from promptpay import qrcode
# generate a payload
id_or_phone_number = "0841234567"
payload = qrcode.generate_payload(id_or_phone_number)
payload_with_amount = qrcode.generate_payload(id_or_phone_number, 1.23)

# export to PIL image
img = qrcode.to_image(payload)

# export to file
qrcode.to_file(payload, "./qrcode-0841234567.png")
qrcode.to_file(payload_with_amount, "/Users/joe/Downloads/qrcode-0841234567.png") 
» การสร้าง QR Code สำหรับ PromptPay ด้วย Python นั้นสามารถทำได้โดยใช้ไลบรารีต่าง ๆ ที่มีพร้อมใช้งาน ต่อไปนี้เป็นตัวอย่างโค้ด Python สำหรับการสร้าง QR Code สำหรับ PromptPay:
» ติดตั้ง pip install promptpay-qr
import promptpay_qr
from PIL import Image

# กำหนดข้อมูลสำหรับการโอนเงิน
recipient = "0987654321"  # แทนด้วยหมายเลขโทรศัพท์หรือหมายเลขประจำตัวประชาชน
amount = 123.45  # แทนด้วยจำนวนเงินที่ต้องการโอน

# สร้าง payload สำหรับ QR Code
payload = promptpay_qr.generate_payload(recipient, amount)

# สร้าง QR Code จาก payload
qr_code = promptpay_qr.generate_qr_code(payload)

# แสดง QR Code
qr_code.show()

» การสังเคราะห์ QR Code PromptPay -> https://genpromptpay.web.app
» https://promtpay.io/เบอร์โทรศัพท์ -> ภาพจะเป็น <img src="https://promptpay.io/เบอร์โทรศัพท์.png">
» ฟรีค่าธรรมเนียมเมื่อโอนไม่เกิน 5,000 บาท และสามารถโอนได้ไม่จำกัดจำนวนครั้ง การโอนที่ยอดเกินกว่า 5,000 บาท/ครั้ง จะมีค่าธรรมเนียมเป็นขั้นบันได เริ่มตั้งแต่ 2 บาท/ครั้ง
» ธนาคารอะไรเข้าร่วมบ้าง ธนาคารพาณิชย์ทุกแห่งในประเทศไทย เช่น ธนาคารกสิกรไทย ธนาคารไทยพาณิชย์ ธนาคารกรุงไทย ธนาคารกรุงเทพ ธนาคารกรุงศรี
» PromptPay.io ให้บริการฟรีและจะฟรีตลอดไป โดยทีมงานเบื้องหลัง Page365 ผู้เชี่ยวชาญเรื่องร้านค้าออนไลน์
» https://github.com/fustyles/Arduino/tree/master/ESP32-CAM_QRCode_Recognition
» ป้อนเบอร์โทรศัพท์ เลขบัตรประชาชน จำนวนเงินมันจะสังเคราะห์ QR Code
» https://promptpay2.me/

การติดต่อ Lora E220-400T22D และ ESP32

» 433/470MHz 22dBm LoRa Wireless Module ระยะทาง 5km
» https://github.com/xreef/EByte_LoRa_E220_micropython_library
» E220-400T22D เป็นโมดูล LoRa รับส่งข้อมูลไร้สายระยะไกล 5 กม.  สื่อสารผ่านพอร์ตอนุกรม (UART) ทำงานในช่วงความถี่ 410.125 ถึง 493.125 MHz ใช้แรงดัน 3.3v และ 5v สำหรับ IO 
» คุณสมบัติ 
 - สื่อสารได้ไกลขึ้นและทนสัญญาณรบกวน
- รองรับการตั้งรหัสสำหรับการสื่อสารช่วยเพิ่มความปลอดภัยของข้อมูล
- รองรับฟังก์ชั่น LBT เพื่อตรวจสอบสัญญาณรบกวนในช่องสัญญาณก่อนการส่งข้อมูล
- รองรับฟังก์ชั่นแสดงความแรงของสัญญาณ RSSI เพื่อประเมินคุณภาพสัญญาณและปรับปรุงระบบการสื่อสาร
- รองรับฟังก์ชั่นการเริ่มทำงานด้วยสัญญาณไร้สาย และบริโภคพลังงานต่ำมาก เหมาะสำหรับแอพลิเคชั่นที่ใช้พลังงานจากแบตเตอร์รี่
- รองรับการหลับลึกซึ่งบริโภคพลังงานต่ำมากเพียง 5uA
- มี PA+LNA ภายในทำให้สื่อสารได้ไกล 5km 
- พารามิเตอร์ถูกบันทึกหลังจากปิดเครื่องและโมดูลจะทำงานตามพารามิเตอร์ที่ตั้งไว้เมื่อเริ่มต้นทำงาน
- การออกแบบ watchdog ที่มีประสิทธิภาพ หากมีข้อผิดพลาดเกิดขึ้นโมดูลจะเริ่มทำงานใหม่และดำเนินการต่อตามค่าพารามิเตอร์ที่ตั้งไว้ก่อนหน้า
- รองรับอัตราบิต 2.4k ถึง 62.5 kbps
- รองรับแหล่งจ่ายไฟ 3.0 - 5.5v
- รองรับการทำานที่ 40 - 85℃
- มีอินเตอร์เฟสสำหรับเสาอากาศแบบ SMA
การออกแบบฮาร์ดแวร์
- แหล่งจ่ายไฟควรใช้ไฟที่เรียบและไม่มีการกระชาก
- โปรดระวังในการเชื่อมต่อขั้วบวกและขั้วลบให้ถูกต้อง หากต่อสลับขั้วอาจทำให้โมดูลเสียหาย
- โปรดต่อแหล่งจ่ายไฟด้วยแรงดันที่กำหนดไว้ คือ 3.0-5.5v
- โมดูลควรอยู่ห่างจากแหล่งจ่ายไฟ ขดลวดและส่วนอื่น ๆ ที่มีสัญญาณแม่เหล็กรบกวน

การติดต่อ Lora module

»  ใช้การสื่อสารอนุกรม
» AT # คือคำสั่งตอบกลับว่า OK นั่นคือโมดูลพร้อมใชังาน
» AT+ADDRESS=2      #การตั้งเลขเลขแอดเดรสให้กับโมดูล (ถ้ากำหนดเป็น 0 จะส่งไปยังทุกโหนด) ทำทุก ๆ โหนด
» AT+BAND=923000000  #ตั้งแบรด์ ทุก ๆ โมดูล
» AT+NETWORKID=5    #Network ID คือ เลขหมู่บ้าน ใช้ค่าได้จาก 3-15 และเลข 18 ทำทุก ๆ โหนด
» AT+SEND=2,11,HELLO_WORLD    #เลข 2 คือ Address ที่ส่งไป ส่วน 11 คือจำนวนข้อมูลที่ส่งออกไป และ HELLO_WORLD (ส่งได้ ประมาณ 250 ไบต์)

การตรวจสอบว่ามีโมดูลอะไรบ้างใน Micropython

» คำสั่ง help('modules')
__main__          framebuf          socket            upip
_boot             gc                ssl               upip_utarfile
_onewire          hashlib           struct            upysh
_thread           heapq             sys               urandom
_webrepl          inisetup          time              ure
apa106            io                ubinascii         urequests
array             json              ucollections      uselect
binascii          machine           ucryptolib        usocket
btree             math              uctypes           ussl
builtins          micropython       uerrno            ustruct
cmath             neopixel          uhashlib          utime
collections       network           uhashlib          utimeq
dht               ntptime           uheapq            uzlib
ds18x20           onewire           uio               webrepl
errno             os                ujson             webrepl_setup
esp               random            umqtt/robust      websocket
esp32             re                umqtt/simple      websocket_helper
flashbdev         select            uos               zlib
Plus any modules on the filesystem

การใช้งาน Network และ URL Request ด้วย Micropython บน ESP32

def do_connect():
    import network
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('connecting to network...')
        wlan.connect('ชื่อ wifi (ssid)', 'รหัสผ่าน wifi')
        while not wlan.isconnected():
            pass
    print('network config:', wlan.ifconfig())
   
do_connect()

import urequests as requests
import ujson
import time
res = requests.get(url = 'https://dsdi.msu.ac.th/iot/random.php').text

การใช้งาน Fourier Transform ด้วย Micropython บน ESP32

» ulab อ่านว่า micro lab เป็นไลบรารี่คำนวณคณิตศาสตร์ มีฟังก์ชั่น fft (Fast Fourier Transform) เพื่อแปลงข้อมูลเวลาเป็นความถี่
» Micropython และ ulab เฟิร์มแวร์ https://gitlab.com/rcolistete/micropython-firmwares/-/tree/master/ESP32
# ulab version 0.5
import ulab as np
a = np.array([1, 2, 3, 4, 1, 2, 3, 4], dtype=np.float)
np.fft.fft(a)
# ผลลัพธ์:
# array([20.+0.j,  0.+0.j, -4.+4.j,  0.+0.j, -4.+0.j,  0.+0.j, -4.-4.j,  0.+0.j])

การใช้งาน Fast Furier Transform

import  ulab as np
x = np.linspace(0, 10, num=1024)
from math import sin
y = list(map(lambda xi:sin(xi),x))
z = np.zeros(len(x))

a,b=np.fft.fft(x)
print('real: ',a, '\nimaginary:',b)
#real:  array([5119.996, -5.004725, -5.004798, ..., -5.005443, -5.005628, -5.006485], dtype=float) 
#imaginary: array([0.0, 1631.333, 815.659, ..., -543.7639, -815.6587, -1631.333], dtype=float)

c,d=np.fft.fft(x,z)
print('real: ',c, '\nimaginary:',d)
#real:  array([5119.996, -5.004725, -5.004798, ..., -5.005443, -5.005628, -5.006485], dtype=float) 
#imaginary: array([0.0, 1631.333, 815.659, ..., -543.7639, -815.6587, -1631.333], dtype=float)

การใช้งาน Invert Fast Furier Transform

import  ulab as np
x = np.linspace(0, 10, num=1024)
from math import sin
y = list(map(lambda xi:sin(xi),x))
y=np.array(y)
a, b = np.fft.fft(y)
print('original vector:\t', y)
#original vector:	 array([0.0, 0.009775016, 0.0195491, ..., -0.5275068, -0.5357859, -0.5440139], dtype=float)
y, z = np.fft.ifft(a, b)
print('\nreal part of inverse:\t', y)
# real part of inverse:	 array([8.940697e-08, 0.009775251, 0.01954916, ..., -0.5275063, -0.5357856, -0.5440133], dtype=float)

print('\nimaginary part of inverse:\t', z)
#imaginary part of inverse:	 array([-1.466833e-08, -2.501297e-07, 7.005331e-08, ..., -1.597692e-07, 5.291355e-08, 9.805394e-08], dtype=float)
» คำสั่ง print(np.eye(5)) คือสร้างเมตริกซ์เอกลักษณ์ขนาด 5x5
» คำสั่ง print(np.eye(4, M=6, k=-1, dtype=np.int16)) คือสร้างเมตริกเอกลักษณ์ขนาด 4x6
» คำสั่ง np.ones(6, dtype=np.uint8) คือ สร้างเมตริกซ์ 1x6 มีค่า [1,1,1,1,1,1]
» คำสั่ง a=np.array(range(4), dtyle=np.uint8); a.reshape((2,2)) แปลงเมตริกซ์เป็น 2x2
» คำสั่ง transpose() เช่น a.transpose() 
» คำสั่ง  a = np.array([1, 2, 3, 4, 5, 6, 7, 8], dtype=np.uint8); print(a < 5) จะได้ [True, True, True, True, False, False, False, False]

มอเตอร์ไฟฟ้ากระแสตรงแบบไม่ใช้แปลงถ่าน (Brushless DC Motor: BLDC Motor)

» มอเตอร์ BLDC ให้ประสิทธิภาพสูง แรงบิตสูง เรียกอีกอย่างว่า ECM หรือ EC motor (Electronically Commutated motor) หรือ Synchronous DC motor)
» Hall effect sensor ใช้ตรวจจับการหมุนด้วยสนามแม่เหล็ก โดยจะตรวจจับทิศการหมุน 
» การควบคุม BLDC นิยมใช้ Closed Loop Control โดยใช้เซนเซอร์  Hall Effect IC บอกตำแหน่งโรเตอร์ให้กับคอนโทรลเลอร์
» ปล. ระบบที่ไม่ใช้ hall sensor จะกินไฟมากกว่า

การใช้งานคอนโทรลเลอร์ STM32

» บริษัท STMicroelectronic หรือบริษัท ST หรือ STMicro เป็นบริษัทผลิตไอซี มีสิทธิบัตร 16,000 ชิ้น และการออกแบบ 9000 ชิ้น มีทีม R&D มากกว่า 11,000 คน 
» สินค้าได้แก่ ทรานซีสเตอร์ แอมป์ ไดโอด ตัวควบคุมแรงดันไฟฟ้าและมอสเฟส 
» ในอดีต STM32 ไม่มี IDE ต้องคอมไพล์จากของบริษัทอื่น เช่น IAR Embedded Workbench ,  µVision IDE (ARM Keil) และ ARMmbed
» ปัจจุบัน STMicroelectronics พัฒนา STM32CubeIDE เป็นคอมไพล์เลอร์และ IDE -> ดาวน์โหลด  https://www.st.com/en/development-tools/stm32cubeide.html
» ดาวน์โหลดไฟล์โดยตรงที่นี่ -> https://www.st.com/content/ccc/resource/technical/software/sw_development_suite/group0/e8/14/a7/4e/fe/8a/4e/a0/stm32cubeide-win/files/st-stm32cubeide_1.12.1_16088_20230420_1057_x86_64.exe.zip/jcr:content/translations/en.st-stm32cubeide_1.12.1_16088_20230420_1057_x86_64.exe.zip
» การโหลดไฟล์ลงไมโครคอนโทรลเลอร์ เรียกว่า การโปรแกรม จะใช้อุปกรณ์ ST-Link v2 มี 4 ขา SWD, SWC, 3.3v และ GND
» แหล่งข้อมูล: https://www.artronshop.co.th/article/108/stm32-getstarted-with-stm32cubeide
» กิจกรรม 1: ติดตั้ง cube-ide  จะเก็บไว้ใน C:\ST\STM32CubeIDE_1.12.1 -> โปรแกรม stm32cubeide.exe
» กิจกรรม 2: เขียนโปรแกรมไฟกระพริบ -> เปิดโปรแกรม -> File -> New -> Stm32 Project
  - Part Number: เลือก STM32G030C6T6 เนื่องจากเอาไปจำลองใน preteus ได้
  - ถ้า generate code ไม่สำเร็จให้ลองใหม่ โดยเลือก Project -> Generate code อีกรอบ 
  - จะได้ไดอะแกรมแสดงขาไอซีให้เลือก PB1 เป็น GPIO_Output
» กิจกรรม 3: จำลองการทำงานใน proteus 
   - เลือก STM32F103C6 และ Led Yellow
   - วาง power เปลี่ยนค่าเป็น +3.3v ลากต่อกับ LED และลากหัวลูกศรชี้ออกไปยัง PA0-WKUP
   - วาง power และ ground ลากสายออกไปและตั้งชื่อ VDDS และ VSSA
   - เลือกที่ ไอซีคอนโทรลเลอร์ -> program file -> เลือกไฟล์ .hex -> กด Run
» ปล. การเปลี่ยนสีพื้น เลือกเมนู Template -> Set Colors -> เลือกสีพื้นตามต้องการ

บอร์ดควบคุม BLDC และ Hall Sensor

» แรงดัน 12-36v 500w 15A 
» ความเร็วมอเตอร์เขียนด้วยโปรแกรมหรือใช้ potentiometer โดยสายกลางต่อกับ vr speed port และอีกสองเส้นต่อ 5v และ GND
» ทิศทางกำหนดโดย 5v หรือ GND
» ควรใส่ Heatsink
» ความเร็ว ใช้แรงดัน 0.1 - 5v

การประกอบใช้งาน



การใช้งาน STM32F411 และ Micropython

» STM32F11 เป็นคอร์ Cortext-M4 ทำงานที่ 100MHz ประกอบด้วย UARTx3 SPIx5  I2Cx2 USB Host OTG I2Sx5 ADC12bit Flash Size 
» ไมโครไพธอนสามารถทำงานได้กับชิป STM32 ในรุ่น Seriea 411 ขึ้นไป ไม่สามารถใช้กับเวอร์ชั่นต่ำกว่าเนื่องจากพื้นที่เก็บข้อมูลไม่เพียงพอ
» เครื่องโปรแกรม รุ่น ST Link v2 
» ดาวน์โหลดโปรแกรม ST Link ที่นี่
» ดาวน์โหลด Micropython  ที่นี่
» เปิดโปรแกรม ST Link -> เลือกเมนู Target -> Connect
» เลือกเมนู Target -> Erase
» เลือกเมนู Target -> Program & Verify -> เลือกไฟล์ firmware_internal_rom_stm32f411_v1.12-35.hex
» ดาวน์โหลดโปรแกรม Thonny เลือกเมนู Run -> Conigure Interpreter -> เลือก Generic Python -> เลือก Port: Try to detect automatically

โครงสร้าง STM32F411

โปรแกรมไฟกระพริบ STM32F411 และ Micropython

import machine
import time

LED_OFF = 1
# use the onboard Blue LED
led = machine.Pin('PC13', machine.Pin.OUT)
try:
    while True:
        led.value( not led.value() )
        time.sleep_ms( 100 )
except KeyboardInterrupt:
    pass
finally:
    led.value( LED_OFF )
    print('Done')

โปรแกรมรับค่าจากการกดสวิตช์ด้วย STM32F411 และ Micropython

import utime as time
import pyb
sw  = pyb.Switch() # user push button
led = pyb.LED(1)   # on-board LED (blue), PC13 pin
    
try:
    last_time = time.ticks_ms() # save timestamp
    while not sw.value():       # is button pressed ?
        now = time.ticks_ms()   # read current timestamp
        time_diff = time.ticks_diff( now, last_time )
        if time_diff >= 500:
            led.toggle()        # toggle LED
            last_time = now     # update timestamp
except KeyboardInterrupt:
    pass
finally:
    led.off() # turn off LED
    print('Done')

โปรแกรมกดสวิตช์มากกว่า 2 วินาทีให้ยุติการทำงานด้วย STM32F411 และ Micropython

import pyb
import utime as time

sw  = pyb.Switch() # user push button
led = pyb.LED(1)   # on-board LED (blue), PC13 pin

# set callback function for the push button.
# toggle the LED if the button switch is pressed.
sw.callback( lambda: led.toggle() )

try:
    last_time = time.ticks_ms() # save timestamp
    while True: # main loop
        now = time.ticks_ms() # get current time (msec)
        if sw.value(): # button hold pressed
            if time.ticks_diff( now, last_time ) >= 2000:
                print( 'button long pressed' )
                break # exit the while loop
        else:
            last_time = now # update timestamp
except KeyboardInterrupt: # interrupted by Ctrl+C
    pass
finally:
    sw.callback(None) # disable callback for button
    led.off()         # turn off LED
    print('Done')

การใช้ Hardware Timer ด้วย STM32F411 และ Micropython

คลาส pyb.Timer ใช้ไทมเมอร์ 11 ตัว จาก TIM1 ถึง TIM11 ขนาด 16 บิต (ยกเว้น TIM2 และ TIM5 มีขนาด 32 บิต) 
import utime as time
from machine import Pin
import pyb

led = pyb.LED(1) # on-board LED (blue)
# create Timer (select from TIM1..TIM11),
# set timer frequency = 10 Hz (for fast LED blink)
tim = pyb.Timer( 2, mode=pyb.Timer.UP, freq=10 )
tim.callback( lambda t: led.toggle() )
try:
    while True: # main loop
        pass # do nothing in main loop
except KeyboardInterrupt:
    pass
finally:
    tim.callback(None) # disable timer callback
    tim.deinit() # turn off the timer
    led.off()    # turn off the LED
    print('Done')

การใช้ Software Timer ด้วย STM32F411 และ Micropython

Software Timer ใช้จากคลาส machine.Timer ใช้ FreeRTOS และกำหนดหมายเลข Timer ID = -1 
import pyb
from machine import Timer
import utime as time

sw  = pyb.Switch()  # user push button
led = pyb.LED(1)    # on-board LED (blue)
# create a software timer in periodic mode 
tim = Timer(-1)
tim.init( mode=Timer.PERIODIC,
          period=500, # period in msec
          callback=lambda t: led.toggle() )
try:
    while True:
        if sw.value(): # check the button's state
            break
except KeyboardInterrupt:
    pass
finally:
    sw.callback(None)
    led.off()
    tim.deinit()
    print('Done')

การใช้ PWM ด้วย Hardware Timer บน STM32F411 และ Micropython

Software Timer ใช้จากคลาส machine.Timer โหมด One Shot แทน Periodic โดยจะเรียกฟังก์ชั่น callback เพียงครั้งเดียวเท่านั้น
ความถี่ของซีพียู (CPU) หรือ SysClk เท่ากับ 96 MHz
ความถี่ของการอินเทอร์เฟสด้วยบัส AHB เท่ากับ 96 MHz = SysClk/1
ความถี่ของการอินเทอร์เฟสด้วยบัส APB1 จะได้ 24 MHz = SysClk/4
ความถี่ของการอินเทอร์เฟสด้วยบัส APB2 จะได้ 48 MHz = SysClk/2
ความถี่ของ Timer (TIM4) ได้ตั้งค่าให้นับด้วยความถี่เท่ากับ 5 Hz
ตัวหารความถี่ (Prescaler) เท่ากับ 624 และคาบ (Period) เท่ากับ 15359
import utime as time
from machine import Pin
import pyb

# print system frequencies
freq = pyb.freq() 
print( 'CPU  freq. [Hz]:', freq[0] )  # 96 MHz
print( 'AHB  freq. [Hz]:', freq[1] )  # 96 MHz
print( 'APB1 freq. [Hz]:', freq[2] )  # 24 MHz
print( 'APB2 freq. [Hz]:', freq[3] )  # 48 MHz

# create Timer (use TIM4)
tim = pyb.Timer( 4, freq=5 ) # 5 Hz (for LED blink)
# Choose PB8 pin for TIM4_CH3 or PB9 pin for TIM4_CH4
pwm = tim.channel( 3, pyb.Timer.PWM,
         pin=pyb.Pin.board.PB8, pulse_width=0 )
pwm.pulse_width( tim.period()//2 ) # 50% duty cycle
print( 'prescaler   : {:>8}'.format( tim.prescaler()) )
print( 'frequency   : {:>8} [Hz]'.format( tim.freq()) )
print( 'source freq.: {:>8} [Hz]'.format( tim.source_freq()) )
print( 'period      : {:>8} [us]'.format( tim.period()) )
print( 'pulse width : {:>8} [us]'.format( pwm.pulse_width()) )

try:
    while True:
        pass # do nothing in the main loop
except KeyboardInterrupt:
    pass
finally:
    tim.deinit()
    print('Done')

การปรับค่า PWM ตามค่าในตัวแปรอาร์เรย์ ด้วย STM32F411 และ Micropython

กำหนดค่า duty cycle ลงในอาร์เรย์
นำค่าในอาร์เรย์แต่ละตัวมาสร้าง pwm
import utime as time
from machine import Pin
import pyb
import math

# create a hardware Timer (use TIM4)
tim = pyb.Timer( 4, prescaler=47, period=999 ) # TIM4
# Freq.(Hz) = APB2 freq. (Hz)/(prescaler+1)/(period+1)
#           = 48 MHz /48 /1000 = 1 kHz or 1000 Hz
# choose PB8 pin for TIM4_CH3, or PB9 pin for TIM4_CH4
pwm = tim.channel(4, pyb.Timer.PWM,
         pin=pyb.Pin.board.PB9, pulse_width=0)
print( 'PWM period   :', tim.period() )
print( 'PWM frequency:', tim.freq()   )
try:
    P = tim.period() # get PWM period
    N = 16
    steps = [int(P*math.sin(math.pi*i/N)) for i in range(N)]
    while True:
        for pw in steps:
           pwm.pulse_width( pw ) # change pulse width
           time.sleep_ms( 100 )
except KeyboardInterrupt:
    pass
finally:
    tim.deinit() # disable timer
    print('Done')

การใช้ software timer สร้างความถี่ที่แตกต่างกัน ด้วย STM32F411 และ Micropython

สร้าง 3 ความถี่ คือ 1hz, 2hz และ 4hz ต่อกับ PB7, PB8 และ PB9 และ active low ( 0  คือ led on หรือ 1 คือ led off)
import utime as time
from machine import Pin, Timer
from micropython import const
import pyb

LED_ON  = const(0)
LED_OFF = const(1)
pin_names = ['PB7', 'PB8', 'PB9'] # output pins
leds   = []
timers = []

def timer_cb(t): # timer callback function
    for i in range(len(leds)):
        if t is timers[i]:
             # toggle: read-modify-write
             x = leds[i].value()
             leds[i].value( not x )
             break
             
for pin in pin_names:      # create Pin objects
    leds.append( Pin(pin,mode=Pin.OUT_PP,value=LED_OFF) )
for i in range(len(leds)): # create Timer objects
    timers.append( Timer(-1, freq=(1<<i), callback=timer_cb) )

try:
    while True:
        pass # do nothing in the main loop
except KeyboardInterrupt:
    pass
finally:
    for led in leds:   # turn off all LEDs
        led.value(LED_OFF)
    for tim in timers: # turn off all timers
        tim.deinit() 
    print('Done')

การใช้งาน ulab บน RP 2040

» ulab เป็นไลบรารีที่เพิ่มการทำงานทางด้านคณิตศาสตร์และวิทยาศาสตร์ของ Python ในแพลตฟอร์มที่มีทรัพยากรจำกัด, เช่น microcontroller ที่ใช้ MicroPython หรือ CircuitPython. ulab มีความคล้ายคลึงกับไลบรารี numpy และ scipy ที่ใช้ใน Python แบบปกติ, ทำให้ผู้พัฒนาที่มีประสบการณ์ในการทำงานกับ numpy สามารถใช้งาน ulab ได้โดยง่าย.
» คุณสมบัติหลักของ ulab:
   1. เร็วและทรัพยากรน้อย: ulab ได้รับการพัฒนาเพื่อให้การทำงานที่รวดเร็วและใช้ทรัพยากรต่ำสุด.
   2. ทำงานใน MicroPython และ CircuitPython: ไลบรารีนี้สามารถทำงานในแพลตฟอร์มที่ใช้ MicroPython และ CircuitPython, ทำให้เหมาะสมสำหรับ microcontroller.
   3. มีฟังก์ชันคล้าย numpy: คุณสามารถทำ array operations, ใช้ฟังก์ชันทางคณิตศาสตร์, ทำ Fourier transforms, และอื่น ๆ ได้.
   4. ใช้งานง่าย: หากคุณคุ้นเคยกับ numpy, คุณจะพบว่า ulab ใช้งานได้ง่ายและคุ้นเคย
1. กดปุ่ม boot + เสียบสาย usb เข้าคอมพิวเตอร์
2. จะมีไดรว์ RP 2040 เกิดขึ้น ให้คัดลอกไฟล์  ADAFRUIT_FEATHER_RP2040.uf2 ลงใน ไดรว์ RP2040
3. หลังจากนั้นโปรแกรมจะบูตอัตโนมัติ
4. เปิด Thonny -> เลือก RP2040 -> จะสามารถใช้งาน ulab ได้
>>> import ulab
>>> dir(ulab)
['__class__', '__name__', '__dict__', '__version__', 'dtype', 'numpy', 'scipy', 'utils']
ปล. ดาวน์โหลดเฟิร์มแวร์ https://github.com/v923z/micropython-builder/releases
from ulab import numpy as np
x = np.linspace(0, 10, num=1024)
y = np.sin(x)
a, b = np.fft.fft(y)
print('original vector:\t', y)
y, z = np.fft.ifft(a, b)
# the real part should be equal to y
print('\nreal part of inverse:\t', y)
# the imaginary part should be equal to zero
print('\nimaginary part of inverse:\t', z)
original vector:	 array([0.0, 0.009775011, 0.01954908, ..., -0.5275068, -0.535786, -0.5440211], dtype=float32)

real part of inverse:	 array([8.940695e-08, 0.009778291, 0.01955077, ..., -0.527504, -0.5357838, -0.5440156], dtype=float32)

imaginary part of inverse:	 array([2.095475e-08, 6.65376e-07, 4.454848e-07, ..., 6.856771e-06, 6.02012e-06, 9.188228e-06], dtype=float32)

อ่านเสียง

» Raspberry Pi Pico (RP2040) สามารถใช้เพื่ออ่านสัญญาณเสียงด้วยการใช้โค้ด MicroPython แต่ต้องมีอุปกรณ์ที่ช่วยในการแปลงสัญญาณเสียงเป็นสัญญาณอิเล็กทรอนิกส์ที่สามารถวัดได้ (เช่น microphone module). ตัวอย่างโค้ดที่ใช้กับ microphone module สามารถเป็นดังนี้:
import machine
import array
import utime

# สร้าง object ADC (Analog-to-Digital Converter)
adc = machine.ADC(26)  # ค่า 26 ต้องถูกเปลี่ยนตาม pin ที่ microphone เชื่อมต่อ

# สร้าง array เพื่อเก็บข้อมูล
buffer = array.array('H', [0] * 1000)

# อ่านข้อมูลจาก microphone
for i in range(1000):
    buffer[i] = adc.read_u16()
    utime.sleep_us(10)  # หน่วงเวลาเล็กน้อยระหว่างการอ่านข้อมูล

# ตอนนี้ 'buffer' มีข้อมูลเสียงจาก microphone
# คุณสามารถใช้ข้อมูลนี้ในการวิเคราะห์เสียง, ทำ FFT, หรือการประมวลผลที่ต้องการ

from ulab import numpy as np
a = np.array([1, 2, 3, 4, 5], dtype=np.float)
np.mean(a)
np.std(a)
» ใน np ประกอบด้วยคำสั่ง
» all, any, bool, sort, sum, acos, acosh, arange, arctan2, argmax, argmin, argsort, around, array, asarray, asin, asinh, atan, atanh, bitwise_and, bitwise_or, bitwise_xor, ceil, clip, complex, compress, concatenate, conjugate, convolve, cos, cosh, cross, degrees, delete, diag, diff, dot, e, empty, equal, exp, expm1, eye, fft, flip, float, floor, frombuffer, full, get_printoptions, imag, inf, int16, int8, interp, isfinite, isinf, left_shift, linalg, linspace, load, loadtxt, log, log10, log2, logspace, max, maximum, mean, median, min, minimum, nan, ndarray, ndinfo, nonzero, not_equal, ones, pi, polyfit, polyval, radians, real, right_shift, roll, save, savetxt, set_printoptions, sin, sinc, sinh, size, sort_complex, sqrt, std, tan, tanh, trace, trapz, uint16, uint8, vectorize, where, zeros

MicroMLGen

» MicroMLGen คือเครื่องมือที่ใช้แปลงโมเดลจากการเรียนรู้เครื่องจาก Scikit-learn ให้อยู่ในรูปแบบของโค้ด C ที่สามารถใช้งานได้บนไมโครคอนโทรลเลอร์เช่น RP2040.

ขั้นตอนทั่วไป:
การเตรียมโมเดล:

สร้างและฝึกฝนโมเดลใน Python ใช้ Scikit-learn หรือฟรีมเวิร์คการเรียนรู้เครื่องอื่น ๆ
ใช้ MicroMLGen เพื่อแปลงโมเดลเป็นโค้ด C
การนำโค้ด C ไปใช้บน RP2040:

นำโค้ด C ที่ได้จาก MicroMLGen ไปใช้บนโปรแกรมที่รันบน RP2040
ทำการเชื่อมต่อและอ่านข้อมูลจากเซ็นเซอร์หรืออินพุตต่าง ๆ
ป้อนข้อมูลเหล่านี้เข้าไปยังโมเดลและทำนายผล
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
from micromlgen import port

# โหลดข้อมูลตัวอย่างและสร้างโมเดล
iris = datasets.load_iris()
X, y = iris.data, iris.target
clf = DecisionTreeClassifier()
clf.fit(X, y)

# แปลงโมเดลเป็นโค้ด C
c_code = port(clf)
print(c_code)
#ตัวอย่างโค้ด C บน RP2040:
#include "pico/stdlib.h"
// ตรงนี้คุณต้อง include โค้ด C ที่ได้จาก MicroMLGen
#include "your_model.h"

int main() {
    stdio_init_all();
    // ตัวอย่างการใช้งานโมเดล
    // ป้อนข้อมูลให้กับโมเดล
    float features[] = {5.1, 3.5, 1.4, 0.2};
    // ทำนายผล
    int prediction = your_model_predict(features);
    // แสดงผลลัพธ์
    printf("Prediction: %d\n", prediction);
}
โค้ด C ที่ได้จาก MicroMLGen ควรถูกนำไปใส่ในโปรเจ็คของคุณที่รันบน RP2040.

การใช้งาน Embedded Linux

» การควบคุม I/O (Input/Output) สามารถใช้ผ่าน Shell ได้ดังนี้
$ sudo gpio -g mode 17 out    # กำหนดให้ GPIO 17 เป็น Output
$ sudo gpio -g write 17 1        # กำหนดให้ GPIO 17 มีสถานะเป็น High
$ sudo gpio -g write 17 0        # กำหนดให้ GPIO 17 มีสถานะเป็น Low
$ for i in {1..12}; do gpio -g write 17 1; sleep 0.2; done  # วนรอบ 12 ครั้งให้เปิดปิดขา GPIO17 และหน่วง 0.2 วินาที

การใช้งาน NRF24L01และ ESP8266

» การเชื่อมต่อสายสัญญาณ
» ดาวน์โหลดไลบรารี่ nrf2401.py เก็บไว้ในไมโครไพธอน /lib หรือไดเร็คทอรี่นอกสุด /
# MASTER
import sys
import ustruct as struct
import utime
from machine import Pin, SPI
from nrf24l01 import NRF24L01
from micropython import const
from urandom import getrandbits

# Slave pause between receiving data and checking for further packets.
_RX_POLL_DELAY = const(15)
# Slave pauses an additional _SLAVE_SEND_DELAY ms after receiving data and before
# transmitting to allow the (remote) master time to get into receive mode. The
# master may be a slow device. Value tested with Pyboard, ESP32 and ESP8266.
_SLAVE_SEND_DELAY = const(10)

if sys.platform == 'pyboard':
    cfg = {'spi': 2, 'miso': 'Y7', 'mosi': 'Y8', 'sck': 'Y6', 'csn': 'Y5', 'ce': 'Y4'}
elif sys.platform == 'esp8266':  # Hardware SPI
    cfg = {'spi': 1, 'miso': 12, 'mosi': 13, 'sck': 14, 'csn': 4, 'ce': 5}
elif sys.platform == 'esp32':  # Software SPI
    cfg = {'spi': -1, 'miso': 32, 'mosi': 33, 'sck': 25, 'csn': 26, 'ce': 27}
else:
    raise ValueError('Unsupported platform {}'.format(sys.platform))

pipes = (b'\xf0\xf0\xf0\xf0\xe1', b'\xf0\xf0\xf0\xf0\xd2')

def master():
    csn = Pin(cfg['csn'], mode=Pin.OUT, value=1)
    ce = Pin(cfg['ce'], mode=Pin.OUT, value=0)
    if cfg['spi'] == -1:
        spi = SPI(-1, sck=Pin(cfg['sck']), mosi=Pin(cfg['mosi']), miso=Pin(cfg['miso']))
        nrf = NRF24L01(spi, csn, ce, payload_size=8)
    else:
        nrf = NRF24L01(SPI(cfg['spi']), csn, ce, payload_size=8)

    nrf.open_tx_pipe(pipes[0])
    nrf.open_rx_pipe(1, pipes[1])
    nrf.start_listening()

    num_needed = 16
    num_successes = 0
    num_failures = 0
    led_state = 0

    print('NRF24L01 master mode, sending %d packets...' % num_needed)

    while num_successes < num_needed and num_failures < num_needed:
        # stop listening and send packet
        nrf.stop_listening()
        millis = utime.ticks_ms()
        led_state = max(1, (led_state << 1) & 0x0f)
        print('sending:', millis, led_state)
        try:
            #nrf.send(struct.pack('ii', millis, led_state))
            z=getrandbits(8)
            nrf.send("hello")
        except OSError:
            pass

        # start listening again
        nrf.start_listening()

        # wait for response, with 250ms timeout
        start_time = utime.ticks_ms()
        timeout = False
        while not nrf.any() and not timeout:
            if utime.ticks_diff(utime.ticks_ms(), start_time) > 250:
                timeout = True

        if timeout:
            print('failed, response timed out')
            num_failures += 1

        else:
            # recv packet
            got_millis, = struct.unpack('i', nrf.recv())

            # print response and round-trip delay
            print('got response:', got_millis, '(delay', utime.ticks_diff(utime.ticks_ms(), got_millis), 'ms)')
            num_successes += 1

        # delay then loop
        utime.sleep_ms(250)

    print('master finished sending; successes=%d, failures=%d' % (num_successes, num_failures))



print('NRF24L01 test module loaded')
print('NRF24L01 pinout for test:')
print('    CE on', cfg['ce'])
print('    CSN on', cfg['csn'])
print('    SCK on', cfg['sck'])
print('    MISO on', cfg['miso'])
print('    MOSI on', cfg['mosi'])
print('run master()')
# SLAVE
"""Test for nrf24l01 module.  Portable between MicroPython targets."""

import sys
import ustruct as struct
import utime
from machine import Pin, SPI
from nrf24l01 import NRF24L01

from ws2812b import *

from micropython import const

# Slave pause between receiving data and checking for further packets.
_RX_POLL_DELAY = const(15)
# Slave pauses an additional _SLAVE_SEND_DELAY ms after receiving data and before
# transmitting to allow the (remote) master time to get into receive mode. The
# master may be a slow device. Value tested with Pyboard, ESP32 and ESP8266.
_SLAVE_SEND_DELAY = const(10)

if sys.platform == 'pyboard':
    cfg = {'spi': 2, 'miso': 'Y7', 'mosi': 'Y8', 'sck': 'Y6', 'csn': 'Y5', 'ce': 'Y4'}
elif sys.platform == 'esp8266':  # Hardware SPI
    cfg = {'spi': 1, 'miso': 12, 'mosi': 13, 'sck': 14, 'csn': 4, 'ce': 5}
elif sys.platform == 'esp32':  # Software SPI
    cfg = {'spi': -1, 'miso': 32, 'mosi': 33, 'sck': 25, 'csn': 26, 'ce': 27}
else:
    raise ValueError('Unsupported platform {}'.format(sys.platform))

pipes = (b'\xf0\xf0\xf0\xf0\xe1', b'\xf0\xf0\xf0\xf0\xd2')

def slave():
    csn = Pin(cfg['csn'], mode=Pin.OUT, value=1)
    ce = Pin(cfg['ce'], mode=Pin.OUT, value=0)
    if cfg['spi'] == -1:
        spi = SPI(-1, sck=Pin(cfg['sck']), mosi=Pin(cfg['mosi']), miso=Pin(cfg['miso']))
        nrf = NRF24L01(spi, csn, ce, payload_size=8)
    else:
        nrf = NRF24L01(SPI(cfg['spi']), csn, ce, payload_size=8)

    nrf.open_tx_pipe(pipes[1])
    nrf.open_rx_pipe(1, pipes[0])
    nrf.start_listening()

    print('NRF24L01 slave mode, waiting for packets... (ctrl-C to stop)')

    while True:
        if nrf.any():
            while nrf.any():
                buf = nrf.recv()
                n, r,g,b = struct.unpack('iiii', buf)
                #millis, led_state = struct.unpack('ii', buf)
                #print('received:', millis, led_state)
                #t = struct.unpack('sssss', buf)
                print(n,r,g,b)
                
                utime.sleep_ms(_RX_POLL_DELAY)

            # Give master time to get into receive mode.
            utime.sleep_ms(_SLAVE_SEND_DELAY)
            nrf.stop_listening()
            try:
                #nrf.send(struct.pack('i', millis))
                nrf.send(struct.pack('i', 0))
            except OSError:
                pass
            #print('sent response')
            nrf.start_listening()

leds=[]

การใช้ Raspberry Pi Pico Clone

» ราคา 79 บาท 
» https://youtu.be/EChO4o4Z9v8
from machine import Pin
led=Pin(25, Pin.OUT)
led.toggle()
from neopixel import NeoPixel
import machine
np=NeoPixel(machine.Pin(23), 1)
np[0] = (255,0,0);np.write()

แหล่งข้อมูลเพิ่มเติม

» ไลบรารี่ต่าง ๆ สำหรับ MicroPython
»  Fourier Transforms With scipy.fft: Python Signal Processing
» โรงงานผลิตหลอด WS2812B  KTRLIGHT 
» JarutEx
» micropython-for-stm32
»  ESP32 ภาษาไทย
» ESP32-CAM

การเขียนโปรแกรมควบคุมการขยายพอร์ตด้วยไอซี 74hc595 เข้า 3 ออก 8 (Multiplexer)

» ชิป 74HC595 (74HC595N) เป็นชิปขนาดเล็กที่ใช้ในงานขยายจำนวนขาอินพุตและขาเอาท์พุตของไมโครคอน๊ทรอลเลอร์หรืออุปกรณ์อื่น ๆ โดยมีโครงสร้างหลักดังนี้:
1. ขาอินพุตข้อมูล (Serial Data Input - DS): นี่คือขาที่ใช้ในการรับข้อมูลเข้าสู่ชิป 74HC595 โดยใช้โหมดขยาย (Serial-In, Parallel-Out) ข้อมูลจะถูกเลื่อนเข้ามาทีละบิตเพื่อเก็บไว้ในชิป.
2. ขาของชนิดข้อมูล (Clock Input - SHCP): นี่คือขาที่ใช้ในการกำหนดระยะเวลาของการเลื่อนข้อมูลเข้ามา ข้อมูลจะถูกอ่านจากขา DS และเลื่อนข้ามไปข้ามอินพุตข้อมูลเพื่อเข้าสู่ชิป 74HC595 ทีละบิต.
3. ขาของล็อก (Storage Register Clock Input - STCP): นี่คือขาที่ใช้ในการเก็บข้อมูลที่ถูกเลื่อนเข้ามาจากขา DS และนำข้อมูลนี้เข้าไปในที่เก็บข้อมูลในชิป 74HC595.
4. ขาอโพล็กเอาท์ (Parallel Data Outputs - Q0 ถึง Q7): มี 8 ขาเอาท์พุตที่ใช้ในการเอาท์พุตข้อมูลจากชิป 74HC595 โดยข้อมูลที่ถูกเก็บในชิปจะถูกนำออกผ่านขาเอาท์พุตนี้ ซึ่งคุณสามารถเชื่อมต่อกับอุปกรณ์อื่น ๆ เพื่อควบคุมและควบคุมอุปกรณ์เหล่านั้น.
5. ขาอินพุตของคลีร์ (Clear Input - MR): นี่คือขาที่ใช้ในการล้างข้อมูลที่ถูกเก็บในชิป 74HC595 โดยที่เมื่อถูกเปิด ข้อมูลในชิปจะถูกล้าง.
6. ขาอินพุตของโหมดขยาย/โหมดเก็บข้อมูล (Output Enable - OE): นี่คือขาที่ใช้ในการเปิดหรือปิดการใช้งานขาเอาท์พุต ถ้าถูกเปิดใช้งาน (ให้ล๊อกขานี้) ข้อมูลที่ถูกเก็บในชิปจะถูกส่งออกผ่านขาเอาท์พุต ถ้าถูกปิดใช้งาน (ไม่ล๊อกขานี้) ข้อมูลจะไม่ถูกส่งออก.
7. ขาอินพุตของตัวควบคุมข้อมูล (Master Reset - MR): นี่คือขาที่ใช้ในการล้างข้อมูลที่ถูกเก็บในชิป 74HC595 โดยการใช้โหมดขยาย ถ้าถูกเปิดใช้งาน, ข้อมูลที่ถูกเก็บในชิปจะถูกล้าง.
8. ขา VCC และ GND: ขา VCC ใช้ในการจ่ายแหล่งจ่ายไฟให้กับชิป 74HC595 และขา GND เป็นขาอ้างอิงเป็นอันดับสูงของจ่ายแหล่งจ่ายไฟ.

รวมถึงคุณสมบัติอื่น ๆ ของชิป 74HC595 เช่น ความสามารถในการจัดการข้อมูลที่ถูกเก็บ การทำงานในโหมดขยายและโหมดเก็บข้อมูล และความเสถียรในการทำงานในระดับไฟต่าง ๆ และความเร็วในการเลื่อนข้อมูล ซึ่งทั้งหมดนี้ทำให้ชิป 74HC595 เป็นชิปที่นิยมในการควบคุมอุปกรณ์หลาย ๆ ตัวจากไมโครคอน๊ทรอลเลอร์หรือไมโครคอน๊ทรอลเลอร์อื่น ๆ โดยใช้ขาเข้าน้อยลง
import machine
import time

# กำหนดขาที่เชื่อมต่อระหว่าง RP2040 และ 74HC595
data_pin = machine.Pin(2, machine.Pin.OUT)  # ขาข้อมูล (DS)
clock_pin = machine.Pin(3, machine.Pin.OUT)  # ขาคล็อก (SHCP)
latch_pin = machine.Pin(4, machine.Pin.OUT)  # ขาล็อก (STCP)

# สร้างฟังก์ชันสำหรับส่งข้อมูลไปยังชิป 74HC595
def shift_out(data_pin, clock_pin, latch_pin, data):
    for i in range(7, -1, -1):
        bit_value = (data >> i) & 1
        data_pin.value(bit_value)
        clock_pin.on()
        clock_pin.off()
    latch_pin.on()
    latch_pin.off()

# รหัสส่งผ่านข้อมูลสำหรับแสดงตัวเลข 0-9 บน 7-Segment Display
seven_segment_codes = [0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110,
                       0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111]

# ฟังก์ชัน settext สำหรับแสดงตัวเลขบน 7-Segment Display
def settext(number):
    if 0 <= number <= 9:
        shift_out(data_pin, clock_pin, latch_pin, seven_segment_codes[number])
    else:
        print("Invalid number. Please enter a number between 0 and 9.")

# ตัวอย่างการใช้งาน
try:
    while True:
        for i in range(10):
            settext(i)
            time.sleep(1)
except KeyboardInterrupt:
    pass

การเขียนโปรแกรมสื่อสารไร้สายด้วย LORA และ RPI 2040

» เชื่อมต่อ Lora และ RP2040 ทุกชุดทั้งตัวรับตัวส่งเหมือนกันหมด ดังนี้
   - LORA.GND -> RP2040.GND
   - LORA.VCC -> RP2040.3.3v
   - LORA.AUX -> ไม่ต้องต่อ
   - LORA.Tx -> RP2040.Pin2(Rx0)
   - LORA.Rx -> RP2040.Pin1(Tx0)
   - LORA.M1 -> RP2040.Gnd
   - LORA.M0 -> RP2040.Gnd
- เว็บ micropeta.com/video24
#Transmitter code starts here
import utime
from machine import UART
from machine import Pin

lora = UART(0,9600)
switch1 = Pin(20, Pin.IN, Pin.PULL_DOWN)
switch2 = Pin(19, Pin.IN, Pin.PULL_DOWN)
switch3 = Pin(18, Pin.IN, Pin.PULL_DOWN)
while True:
    if switch1.value() and switch2.value() and switch3.value():
        lora.write("SECRET111")
    elif switch1.value() and switch2.value():
        lora.write("SECRET110")
    elif switch1.value() and switch3.value():
        lora.write("SECRET101")
    elif switch2.value() and switch3.value():
        lora.write("SECRET011")
    elif switch1.value():
        lora.write("SECRET100")
    elif switch2.value():
        lora.write("SECRET010")
    elif switch3.value():
        lora.write("SECRET001")
    else:
        lora.write("SECRET000")
    utime.sleep(0.2)
#Transmitter code Ends here
#Receiver code starts here
import utime
from machine import UART
from machine import Pin

lora = UART(0,9600)

led_red = machine.Pin(20, machine.Pin.OUT)
led_green = machine.Pin(19, machine.Pin.OUT)
led_blue = machine.Pin(18, machine.Pin.OUT)
led_red.value(0)
led_green.value(0)
led_blue.value(0)

while True:
    dataRead = lora.readline()
    if dataRead is not None and "SECRET000" in dataRead:
        led_red.value(0)
        led_green.value(0)
        led_blue.value(0)
    elif dataRead is not None and "SECRET001" in dataRead:
        led_red.value(0)
        led_green.value(0)
        led_blue.value(1)
    elif dataRead is not None and "SECRET010" in dataRead:
        led_red.value(0)
        led_green.value(1)
        led_blue.value(0)
    elif dataRead is not None and "SECRET011" in dataRead:
        led_red.value(0)
        led_green.value(1)
        led_blue.value(1)
    elif dataRead is not None and "SECRET100" in dataRead:
        led_red.value(1)
        led_green.value(0)
        led_blue.value(0)
    elif dataRead is not None and "SECRET101" in dataRead:
        led_red.value(1)
        led_green.value(0)
        led_blue.value(1)
    elif dataRead is not None and "SECRET110" in dataRead:
        led_red.value(1)
        led_green.value(1)
        led_blue.value(0)
    elif dataRead is not None and "SECRET111" in dataRead:
        led_red.value(1)
        led_green.value(1)
        led_blue.value(1)
    utime.sleep(0.2)
#Receiver code Ends here

การใช้ UART จำนวน 6 ตัวด้วย ESP32 และ Micropython

» 




ESP32 สร้าง Web Server

» 
try:
  import usocket as socket
except:
  import socket

import network

import esp
esp.osdebug(None)

import gc
gc.collect()

ssid = 'MicroPython-AP'
password = '123456789'

ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(essid=ssid,  authmode=network.AUTH_WPA2_PSK, password=password)

while ap.active() == False:
  pass

print('Connection successful')
print(ap.ifconfig())

def web_page():
  html = """
  

Hello, World!

""" return html s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', 80)) s.listen(5) while True: conn, addr = s.accept() print('Got a connection from %s' % str(addr)) request = conn.recv(1024) print('Content = %s' % str(request)) response = web_page() conn.send(response) conn.close()

การใช้ rshell กับ ESP32

» ติดตั้ง rshell: ให้ติดตั้ง rshell บนคอมพิวเตอร์ของคุณ โดยใช้คำสั่ง pip install rshell
» เชื่อมต่อ ESP32: เข้าสู่โหมด REPL บน ESP32  เช่น Thonny, ampy, หรือ esptool ในการอัปโหลด MicroPython firmware ลงใน ESP32
» เชื่อมต่อ ESP32 กับคอมพิวเตอร์: เมื่อ ESP32 อยู่ในโหมด REPL (อ่าน-ส่งคำสั่ง), ให้เชื่อมต่อ ESP32 กับคอมพิวเตอร์ของคุณผ่านพอร์ต UART ด้วยสาย USB-to-Serial
» ให้เขียนไฟล์ boot.py เพิ่มคำสั่งต่อไปนี้
import machine
import os
import esp

esp.osdebug(None)

# Enable REPL on UART0 (USB-to-Serial)
uart = machine.UART(0, baudrate=115200)
os.dupterm(uart)
» กดปุ่ม reset บนบอร์ด ESP32
» รัน rshell: เมื่อ ESP32 กำลังทำงานและเชื่อมต่อกับคอมพิวเตอร์, ให้เปิดหน้าต่างคอมพิวเตอร์ใหม่และใช้คำสั่งต่อไปนี้เพื่อเริ่มใช้งาน rshell:
   rshell --buffer-size=30 -p /dev/ttyUSB0  # ให้แทน /dev/ttyUSB0 ด้วยพอร์ตของ USB-to-Serial ของคุณ
   --buffer-size=30: ระบุขนาดของบัฟเฟอร์สำหรับคำสั่ง (buffer size) เพื่อให้การทำงานดีขึ้น (สามารถปรับขนาดตามความเหมาะสม).
   -p /dev/ttyUSB0: ระบุพอร์ตของ USB-to-Serial ที่ ESP32 เชื่อมต่อกับคอมพิวเตอร์ของคุณ.
» พิมพ์คำสั่ง repl เพื่อเข้าสู่ python prompt บนหน้าจอจะปรากฎหน้าจอ >>> 
» ใช้คำสั่ง rshell: เมื่อเชื่อมต่อสำเร็จ, คุณสามารถใช้คำสั่ง rshell เพื่อควบคุมและจัดการกับ ESP32 ได้ เช่น:
   - ls: แสดงรายการไฟล์บน ESP32.
   - cd directory_name: เปลี่ยนไปยังไดเรกทอรีที่ระบุ.
   - get file_name: ดาวน์โหลดไฟล์จาก ESP32 ไปยังคอมพิวเตอร์ของคุณ.
   - put file_name: อัปโหลดไฟล์จากคอมพิวเตอร์ของคุณไปยัง ESP32.
   - repl: เข้าสู่โหมด REPL บน ESP32.
   - exit: ออกจากรshell.
» ออกจาก rshell: เมื่อคุณเสร็จสิ้นการใช้งาน rshell, ให้ปิด rshell โดยการพิมพ์ exit และกด Enter.

การอ่านเขียนข้อมูล ESP32 และ UART

» ESP32.pin16(Rx) -> Module.Tx
» ESP32.pin17(Tx) -> Module.Rx
» ESP32.3v3 -> Module.vcc
» ESP32.Gnd -> Module.Gnd
import machine
import time

# กำหนดค่า UART
uart = machine.UART(1, baudrate=9600, tx=17, rx=16)  # ตั้งค่า UART กับพิน GPIO 17 (TX) และ 16 (RX)

while True:
    # ส่งข้อมูลผ่าน UART
    #uart.write("Hello, ESP32!\n")
    
    # รับข้อมูลจาก UART
    if uart.any():
        r = uart.read()
        print("Received:", r)

การใช้งานบอร์ด Freenove ESP32-Wrover CAM Board ถ่ายภาพและส่งขึ้น Php Server

» เฟิร์มแวร์ https://www.freenove.com/tutorial
» esp32spiram-20220618-v1.19.1.bin ไม่สามารถใช้ import camera
» esp32spiram-idf4-20191220-v1.12.bin ไม่สามารถใช้ import camera
» micropython_camera_feeeb5ea3_esp32_idf4_4.bin สามารถใช้ import camera
import camera

def camera_init():
    # Disable camera initialization
    camera.deinit()
    # Enable camera initialization
    camera.init(0, d0=4, d1=5, d2=18, d3=19, d4=36, d5=39, d6=34, d7=35,
                format=camera.JPEG, framesize=camera.FRAME_VGA, 
                xclk_freq=camera.XCLK_20MHz,
                href=23, vsync=25, reset=-1, pwdn=-1,
                sioc=27, siod=26, xclk=21, pclk=22, fb_location=camera.PSRAM)

    camera.framesize(camera.FRAME_VGA) # Set the camera resolution
    # The options are the following:
    # FRAME_96X96 FRAME_QQVGA FRAME_QCIF FRAME_HQVGA FRAME_240X240
    # FRAME_QVGA FRAME_CIF FRAME_HVGA FRAME_VGA FRAME_SVGA
    # FRAME_XGA FRAME_HD FRAME_SXGA FRAME_UXGA
    # Note: The higher the resolution, the more memory is used.
    # Note: And too much memory may cause the program to fail.
    
    camera.flip(1)                       # Flip up and down window: 0-1
    camera.mirror(1)                     # Flip window left and right: 0-1
    camera.saturation(0)                 # saturation: -2,2 (default 0). -2 grayscale 
    camera.brightness(0)                 # brightness: -2,2 (default 0). 2 brightness
    camera.contrast(0)                   # contrast: -2,2 (default 0). 2 highcontrast
    camera.quality(10)                   # quality: # 10-63 lower number means higher quality
    # Note: The smaller the number, the sharper the image. The larger the number, the more blurry the image
    
    camera.speffect(camera.EFFECT_NONE)  # special effects:
    # EFFECT_NONE (default) EFFECT_NEG EFFECT_BW EFFECT_RED EFFECT_GREEN EFFECT_BLUE EFFECT_RETRO
    camera.whitebalance(camera.WB_NONE)  # white balance
    # WB_NONE (default) WB_SUNNY WB_CLOUDY WB_OFFICE WB_HOME

camera_init()

การบันทึกภาพลง storage

img = camera.capture() with open("test.jpg", "wb") as file: file.write(img) file.close()

การส่งภาพขึ้น Php Server

import urequests as requests
import ubinascii
camera_init()
img = camera.capture()
base64_img = ubinascii.b2a_base64(img)

url = "http://dsdi.msu.ac.th/iot/esp32/upload.php"  # เปลี่ยน URL เป็น URL ของเซิร์ฟเวอร์ PHP ที่นี่
headers = {
    "Content-Type": "application/json",
}
data = {
    "image_data": base64_img,
}
try:
   response = requests.post(url, headers=headers, json=data)
   if response.status_code == 200:
      print("Image uploaded successfully")
      print(response.text)
   else:
      print("Failed to upload image")
except Exception as e:
   print("Error:", e)

upload.php

<?php
header("Content-Type: application/json");
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $data = json_decode(file_get_contents("php://input"));

    if ($data && isset($data->image_data)) {
        $image_data = $data->image_data;
        $image_name = "captured_image.jpg";  // กำหนดชื่อไฟล์ภาพที่จะบันทึก
        $image_path = "./img/$image_name";  // ระบุเส้นทางที่จะบันทึกภาพ

        // บันทึกภาพลงในเซิร์ฟเวอร์
        if (file_put_contents($image_path, base64_decode($image_data))) {
            echo json_encode(["message" => "Image uploaded successfully"]);
        } else {
            echo json_encode(["message" => "Failed to upload image"]);
        }
    } else {
        echo json_encode(["message" => "Invalid data"]);
    }
} else {
    echo json_encode(["message" => "Invalid request"]);
}
?>

การติดตั้ง package ด้วย upip

» upip เป็นการติดตั้ง package บนไมโครไพธอน
import network
# สร้างออบเจ็กต์ WLAN(สามารถใช้ได้กับ STA_IF หรือ AP_IF)
wlan = network.WLAN(network.STA_IF)
# เปิดการใช้งาน Wi-Fi interface
wlan.active(True)
# เชื่อมต่อกับ Wi-Fi network (SSID และ Password ของคุณ)
wlan.connect('ชื่อ Wi-Fi', 'รหัสผ่าน Wi-Fi')
# รอจนกว่า ESP32 จะเชื่อมต่อกับ Wi-Fi
while not wlan.isconnected():
    pass

# พร้อมแสดง IP address ของ ESP32
print('Connected to Wi-Fi')
print('IP address:', wlan.ifconfig()[0])
import upip
upip.install('urequests')

การอ่าน QR-Code

» ESP32 Cam อ่าน QR-Code 
   -  MC_QR_Reader
   -  QR-Code-via-MQTT-with-only-ESP32
   - ESP32QRCodeReader
»  โมดูล Tiny Code Reader ใช้ RP2040 ในการประมวลผล
» เพื่อความสะดวกควรใช้โมดูล Tiny Code Reader สื่อสารด้วย I2C ราคา $7 เหรียญ

การใช้งานจอ 2.8 นิ้ว SPI Module ILI9341 และ ESP32 ด้วย micropython

» SPI Module ILI9341 เป็นโมดูลที่ใช้ในการเชื่อมต่อกับหน้าจอ TFT (Thin-Film Transistor) แบบสีที่มีชื่อรุ่น ILI9341 ซึ่งมักนำมาใช้ในการสร้างหน้าจอแสดงผลสำหรับโปรเจคอิเล็กทรอนิกส์และโปรเจคอื่น ๆ ที่ต้องการแสดงข้อมูลแบบกราฟิกหรือภาพสี
» SPI Module ILI9341 มีลักษณะสำคัญดังนี้:
» หน้าจอ TFT สี: มีความสามารถในการแสดงสีที่มีความละเอียดสูง มักมีความละเอียดสี 16 บิตหรือ 18 บิต ซึ่งช่วยให้คุณสามารถแสดงภาพและกราฟิกสีได้หลากหลาย
» การเชื่อมต่อผ่าน SPI: โมดูลนี้ใช้การเชื่อมต่อผ่าน SPI (Serial Peripheral Interface) ซึ่งเป็นอินเทอร์เฟซสื่อสารแบบคีย์ข้อมูลซีเรียล ทำให้มันสามารถทำงานกับไมโครคอนโทรลเลอร์หรือไมโครคอมพิวเตอร์อย่าง ESP32 หรือ Raspberry Pi
» ขนาดหน้าจอสูง: ILI9341 มักมีขนาดหน้าจอที่ใหญ่ ส่วนใหญ่มีขนาด 2.8 นิ้ว หรือ 3.2 นิ้ว ซึ่งเหมาะสำหรับการแสดงผลข้อมูลใหญ่
» ความละเอียดสูง: หน้าจอ ILI9341 มักมีความละเอียดสูงสามารถแสดงผลข้อมูลที่คมชัดและละเอียด
» ความสามารถในการควบคุมสี: โมดูล ILI9341 สามารถแสดงสีแบบ RGB (Red-Green-Blue) ซึ่งช่วยให้คุณสามารถกำหนดสีต่าง ๆ ให้กับแต่ละพิกเซลได้
» อินเตอร์เฟซที่ง่ายต่อการใช้งาน: โมดูล ILI9341 มักมีไลบรารีและคู่มือการใช้งานที่ช่วยให้คุณสามารถเริ่มต้นใช้งานได้ง่าย
» ความเร็วในการส่งข้อมูล: SPI มีความเร็วในการส่งข้อมูลสูง ทำให้ ILI9341 สามารถแสดงผลข้อมูลอย่างรวดเร็ว
» การเขียนภาษาไทยบนจอกราฟิกบนไมโครคอนโทรลเลอร์ https://www.praphas.com/forum/index.php?topic=314.0
» การใช้งานจอ 2.8" ด้วย ESP32 https://www.artronshop.co.th/article/95



การออกแบบป้ายความเร็ว 7-SegmentC ด้วยหลอด WS2812

» ให้ออกแบบทีละส่วนทั้งหมด 7 ส่วน จะใช้พื้นที่ขนาดเล็กและใช้ PCB น้อยลง

การใช้งาน ESP32-2432S028 และ LVGL

» ESP32-2432S028 เป็น จอสัมผัส 2.8 นิ้ว ขนาด 240x320 spi ili9341 มี 4MB flash memory 
   - Expanded IO x 2 คือ P3 (Gnd, GPIO35, GPIO22, GPIO21)
   - CN1 (Gnd, NC, GPI27, 3v3)
   - SD Slot (Micro SD)
   - RGB LED (Blue:GPIO16, Red:GPIO4, Green:GPIO17)
   - CDS (GT36516) GPIO34
   - External Power Connector
   - P1 (Vin, TX, RX, Gnd)
   - Audio Out (Audio amp SC8002b)
   - P4 Speak(2=Vo2, 1=Vo1)
   - Touch Pen, 4pin external connector, USB Cable
   - Display Library: LovyanGFX (ILI9341, SPI2_HOST
   - Upload Speed: Windows (921600), MacOS(460800)
» LVGL เป็นไลบรารี่สำหรับออกแบบการโต้ตอบกราฟิกบนไมโครคอนโทรลเลอร์
» ดาวน์โหลด: http://www.jczn1688.com/zlxz  หรือ http://pan.jczn1688.com/directlink/1/ESP32%20module/2.8inch_ESP32-2432S028R.rar
» ปล. ทดลอง burn lvgl_firmware-thaifont/firmware_GENERIC/firmware.bin ด้วย Thronny ทำงานได้ แต่เขียนด้วย ESP Flash Downloader Tool 3.9.3 ใช้ไม่ได้
print(dir(disp))
['__class__', '__init__', '__module__', '__qualname__', '__dict__', 'height', 'stat', 'send_data', 'dc', 'cs', 'width', 'initialize', 'deinit', 'spi', 'monitor', 'miso', 'init', 'flush', 'mosi', 'power', 'start_x', 'start_y', 'power_on', 'word_trans_data', 'send_cmd', 'display_name', 'spimode', 'display_type', 'init_async', 'mhz', 'trans', 'clk', 'backlight_on', 'end_time_ptr', 'spi_send_dma', 'monitor_acc_px', 'bytes_transmitted', 'buf_size', 'spi_send', 'buf1', 'buf2', 'disp_spi_init', '_init', 'asynchronous', 'backlight', 'trans_buffer', 'rst', 'disp_drv', 'cycles_in_ms', 'spihost', 'monitor_count', 'send_trans_word', 'madctl', 'flush_acc_setup_cycles', 'flush_acc_dma_cycles', 'power_down', 'trans_result_ptr', 'hybrid', 'cmd_trans_data', 'factor', 'spi_callbacks', 'start_time_ptr', 'send_data_dma', 'TRANS_BUFFER_LEN', 'event_loop', 'init_cmds', 'monitor_acc_time', 'half_duplex']

การตั้งค่าหน้าจอให้กับจอภาพบน LVGL

# จอภาพของบอร์ดรุ่น ESP32-2432S028R
from ili9XXX import ili9341
disp = ili9341(rot=0x60 , clk=14, cs=15, dc=2, rst=12, power=23, backlight=21, miso=12, mosi=13,width=240, height=320,backlight_on=1)
# จอภาพของบอร์ดรุ่น ESP32- wt32-sc01
# clk (14), cs(15), dc(21), rst(22), backlight(23), power(-1), miso(-1), mosi(13), width(480), height(320)
from ili9XXX import *
disp=st7796(invert=True, hybrid=True, double_buffer=True, rot=REVERSE_LANDSCAPE) #dark theme

การตั้งค่า touch ให้กับจอภาพบน LVGL

# จอภาพของบอร์ดรุ่น ESP32-2432S028R ใช้ไม่ได้เพราะ SPI ไม่แชร์ ถ้าใช้เฉพาะการสัมผัสใช้โค๊ดต่อไปนี้
from xpt2046 import xpt2046
touch = xpt2046(clk=25, mosi=32, miso=39, cs=33, mhz=2)
touch.screen_width=240
touch.screen_height=320
while 1:
    print(touch.get_coords())
# จอภาพของบอร์ดรุ่น ESP32- wt32-sc01
from ft6x36 import ft6x36
touch = ft6x36(sda=18, scl=19, width=320, height=480, inv_x=False, inv_y=True, swap_xy=True)
# ไดรว์เวอร์ -> https://dsdi.msu.ac.th/articles/iot/lvgl/touch-ft6x36/ft6x36.py

การตรวจสอบเวอร์ชั่นของ LVGL

print(lv.version_major())
print(lv.version_minor())
from ili9XXX import ili9341
disp = ili9341(rot=0x60 , clk=14, cs=15, dc=2, rst=12, power=23, backlight=21, miso=12, mosi=13,width=240, height=320,backlight_on=1)

สร้างปุ่มกลางจอภาพ

from display import *
import lvgl as lv
lv.init()
scr = lv.obj()
btn = lv.btn(scr)
btn.align(lv.ALIGN.CENTER, 0, 0)
label = lv.label(btn)
label.set_text('Hello World!')
lv.scr_load(scr)

กำหนดพื้นหลังจอภาพและเขียนข้อความ

from display import *
import lvgl as lv
lv.init()
scr = lv.scr_act()  #lv.screen_active()
scr.set_style_bg_color(lv.color_hex(0x003a57), lv.PART.MAIN)
label = lv.label(lv.scr_act())
label.set_text("Hello world")
label.set_style_text_color(lv.color_hex(0xffffff), lv.PART.MAIN)
label.align(lv.ALIGN.CENTER, 0, 0)

WT32-SC01: สร้างปุ่มเมื่อกดจะเปลี่ยนจาก hello เป็น ok

» เอกสารการใช้งาน WT32-SC01
import lvgl as lv
from ili9XXX import *
disp=st7796(invert=True, hybrid=True, double_buffer=True, rot=REVERSE_LANDSCAPE) #dark theme
from ft6x36 import ft6x36
touch = ft6x36(sda=18, scl=19, width=320, height=480, inv_x=False, inv_y=True, swap_xy=True)

scr = lv.obj()  # สร้างหน้าจอหลัก
btn = lv.btn(scr)  # สร้างปุ่ม
btn.align(lv.ALIGN.CENTER, 0, 0)  # จัดตำแหน่งตรงกลางหน้าจอ
label = lv.label(btn)  # สร้างป้ายกำกับบนปุ่ม
label.set_text("Hello")

def btn_event_cb(e):
    code = e.get_code()
    obj = e.get_target()
    if code == lv.EVENT.CLICKED:
        label.set_text("OK")

btn.add_event_cb(btn_event_cb, lv.EVENT.ALL, None)
lv.scr_load(scr)

WT32-SC01: สร้างปุ่มเมื่อกดจะเปลี่ยนจาก hello เป็น ok สลับกันไป

import lvgl as lv
from ili9XXX import *
disp=st7796(invert=True, hybrid=True, double_buffer=True, rot=REVERSE_LANDSCAPE) #dark theme
from ft6x36 import ft6x36
touch = ft6x36(sda=18, scl=19, width=320, height=480, inv_x=False, inv_y=True, swap_xy=True)

scr = lv.obj()  # สร้างหน้าจอหลัก
btn = lv.btn(scr)  # สร้างปุ่ม
btn.align(lv.ALIGN.CENTER, 0, 0)  # จัดตำแหน่งตรงกลางหน้าจอ
label = lv.label(btn)  # สร้างป้ายกำกับบนปุ่ม
label.set_text("Hello")

def btn_event_cb(e):
    code = e.get_code()
    obj = e.get_target()
    if code == lv.EVENT.CLICKED:
        current_text = label.get_text()
        if current_text == "Hello":
            label.set_text("OK")
        else:
            label.set_text("Hello")

btn.add_event_cb(btn_event_cb, lv.EVENT.ALL, None)
lv.scr_load(scr)

WT32-SC01: สร้างปุ่มเมื่อกดจะเปลี่ยนจาก ON เป็น OFF และเปิดปิดหลอด LED สลับกันไป

import lvgl as lv
from ili9XXX import *
disp=st7796(invert=True, hybrid=True, double_buffer=True, rot=REVERSE_LANDSCAPE) #dark theme
from ft6x36 import ft6x36
touch = ft6x36(sda=18, scl=19, width=320, height=480, inv_x=False, inv_y=True, swap_xy=True)

import machine
gpio4 = machine.Pin(4, machine.Pin.OUT)

scr = lv.obj()  # สร้างหน้าจอหลัก
btn = lv.btn(scr)  #สร้างปุ่ม
btn.align(lv.ALIGN.CENTER, 0, 0)  # จัดตำแหน่งตรงกลางหน้าจอ
label = lv.label(btn)  # สร้างป้ายกำกับบนปุ่ม
label.set_text("ON")

def btn_event_cb(e):
    code = e.get_code()
    obj = e.get_target()
    if code == lv.EVENT.CLICKED:
        current_text = label.get_text()
        if current_text == "ON":
            label.set_text("OFF")
            gpio4.on()  # เปิด GPIO4
        else:
            label.set_text("ON")
            gpio4.off()  # ปิด GPIO4

btn.add_event_cb(btn_event_cb, lv.EVENT.ALL, None)
lv.scr_load(scr)

สร้างปุ่ม 2 ปุ่ม ปรับมุมมนของปุ่มและสร้างสไลด์บาร์

from display import *
import lvgl as lv
lv.init()
style_button =  lv.style_t()
style_button_red = lv.style_t()
style_button_pressed = lv.style_t()
# Create a simple button style
style_button.init()
style_button.set_radius(10)
style_button.set_bg_opa(lv.OPA.COVER)
style_button.set_bg_color(lv.palette_lighten(lv.PALETTE.GREY, 3))
style_button.set_bg_grad_color(lv.palette_main(lv.PALETTE.GREY))
style_button.set_bg_grad_dir(lv.GRAD_DIR.VER)

# Add a border
style_button.set_border_color(lv.color_white())
style_button.set_border_opa(lv.OPA._70)
style_button.set_border_width(2)

# Set the text style
style_button.set_text_color(lv.color_white())

# Create a red style. Change only some colors.
style_button_red.init()
style_button_red.set_bg_color(lv.palette_main(lv.PALETTE.RED))
style_button_red.set_bg_grad_color(lv.palette_lighten(lv.PALETTE.RED, 2))

# Create a style for the pressed state.
style_button_pressed.init()
style_button_pressed.set_bg_color(lv.palette_main(lv.PALETTE.BLUE))
style_button_pressed.set_bg_grad_color(lv.palette_darken(lv.PALETTE.RED, 3))

# Create a button and use the new styles
button = lv.btn(lv.scr_act())                  # Add a button the current screen
# Remove the styles coming from the theme
# Note that size and position are also stored as style properties
# so lv_obj_remove_style_all will remove the set size and position too
button.remove_style_all()                      # Remove the styles coming from the theme
button.set_pos(10, 10)                         # Set its position
button.set_size(120, 50)                       # Set its size
button.add_style(style_button, 0)
button.add_style(style_button_pressed, lv.STATE.PRESSED)

label = lv.label(button)                       # Add a label to the button
label.set_text("Button")                    # Set the labels text
label.center()

# Create a slider in the center of the display
slider = lv.slider(lv.scr_act())
slider.set_width(200)                                              # Set the width
slider.center()                                                    # Align to the center of the parent (screen)

# Create another button and use the red style too
button2 = lv.btn(lv.scr_act())
button2.remove_style_all()                     # Remove the styles coming from the theme
button2.set_pos(10, 80)                        # Set its position
button2.set_size(120, 50)                      # Set its size
button2.add_style(style_button, 0)
button2.add_style(style_button_red, 0)
button2.add_style(style_button_pressed, lv.STATE.PRESSED)
button2.set_style_radius(lv.RADIUS_CIRCLE, 0)  # Add a local style
label = lv.label(button2)                      # Add a label to the button
label.set_text("Button 2")                  # Set the labels text
label.center()

การเพิ่ม Event ให้กับปุ่ม

class CounterBtn():
    def __init__(self):
        self.cnt = 0
        #
        # Create a button with a label and react on click event.
        #

        button = lv.button(lv.screen_active())                               # Add a button the current screen
        button.set_pos(10, 10)                                      # Set its position
        button.set_size(120, 50)                                    # Set its size
        button.align(lv.ALIGN.CENTER,0,0)
        button.add_event(self.button_event_cb, lv.EVENT.ALL, None)  # Assign a callback to the button
        label = lv.label(button)                                    # Add a label to the button
        label.set_text("Button")                                 # Set the labels text
        label.center()

    def button_event_cb(self,e):
        code = e.get_code()
        button = e.get_target_obj()
        if code == lv.EVENT.CLICKED:
            self.cnt += 1

            # Get the first child of the button which is the label and change its text
            label = button.get_child(0)
            label.set_text("Button: " + str(self.cnt))

counterBtn = CounterBtn()

การสร้างปุ่มแสดงข้อความ Click เมื่อกดปุ่มเปลี่ยนข้อความเป็นคำว่า Button

import lvgl as lv
import display_driver

def btn_event_cb(e):
  print("Clicked")

# Create a Button and a Label
btn = lv.btn(lv.scr_act())
btn.center()
btn.set_size(100, 50)
btn.add_event_cb(btn_event_cb, lv.EVENT.CLICKED, None)

label = lv.label(btn)
label.set_text("Button")
label.center()

การแสดงรายการ checkbox เมื่อกดให้แสดงข้อความของแต่ละตัวเลือกออกมา

def event_handler(e):
    code = e.get_code()
    obj = e.get_target()
    if code == lv.EVENT.VALUE_CHANGED:
        txt = obj.get_text()
        if obj.get_state() & lv.STATE.CHECKED:
            state = "Checked"
        else:
            state = "Unchecked"
        print(txt + ":" + state)


lv.scr_act().set_flex_flow(lv.FLEX_FLOW.COLUMN)
lv.scr_act().set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START, lv.FLEX_ALIGN.CENTER)

cb = lv.checkbox(lv.scr_act())
cb.set_text("Apple")
cb.add_event_cb(event_handler, lv.EVENT.ALL, None)

cb = lv.checkbox(lv.scr_act())
cb.set_text("Banana")
cb.add_state(lv.STATE.CHECKED)
cb.add_event_cb(event_handler, lv.EVENT.ALL, None)

cb = lv.checkbox(lv.scr_act())
cb.set_text("Lemon")
cb.add_state(lv.STATE.DISABLED)
cb.add_event_cb(event_handler, lv.EVENT.ALL, None)

cb = lv.checkbox(lv.scr_act())
cb.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
cb.set_text("Melon")
cb.add_event_cb(event_handler, lv.EVENT.ALL, None)

การเขียนข้อความด้วย lvgl label

import sys
from ili9XXX import ili9341
disp = ili9341(rot=0x60 , clk=14, cs=15, dc=2, rst=12, power=23, backlight=21, miso=12, mosi=13,width=240, height=320,backlight_on=1)

import lvgl as lv
lv.init()   

# การเพิ่ม label
a= lv.label(lv.scr_act())
a.set_text('สารสนเทศศาสตร์')
a.align(lv.ALIGN.CENTER, 0, -130)
a.set_style_text_font(lv.Kanit_Regular_20, 0)

การสร้างมิเตอร์เข็ม

import sys
from ili9XXX import ili9341
disp = ili9341(rot=180 , clk=14, cs=15, dc=2, rst=12, power=23, backlight=21, miso=12, mosi=13,width=240, height=320,backlight_on=1)

import lvgl as lv
lv.init()
def set_value(indic, v):
    meter.set_indicator_value(indic, v)

#
# A simple meter
#
meter = lv.meter(lv.scr_act())
meter.set_size(180, 180)
meter.center()

#Add a scale first
meter.set_scale_ticks(41, 2, 10, lv.palette_main(lv.PALETTE.GREY))
meter.set_scale_major_ticks(8, 4, 15, lv.color_black(), 10)
#meter.set_style_text_font(lv.font_montserrat_18, 0)

indic = lv.meter_indicator_t()

# Add a blue arc to the start
indic = meter.add_arc(3, lv.palette_main(lv.PALETTE.BLUE), 0)
meter.set_indicator_start_value(indic, 0)
meter.set_indicator_end_value(indic, 20)

# Make the tick lines blue at the start of the scale
indic = meter.add_scale_lines(lv.palette_main(lv.PALETTE.BLUE), lv.palette_main(lv.PALETTE.BLUE), False, 0)
meter.set_indicator_start_value(indic, 0)
meter.set_indicator_end_value(indic, 20)

# Add a red arc to the end
indic = meter.add_arc(3, lv.palette_main(lv.PALETTE.RED), 0)
meter.set_indicator_start_value(indic, 80)
meter.set_indicator_end_value(indic, 100)

# Make the tick lines red at the end of the scale
indic = meter.add_scale_lines(lv.palette_main(lv.PALETTE.RED), lv.palette_main(lv.PALETTE.RED), False, 0)
meter.set_indicator_start_value(indic, 80)
meter.set_indicator_end_value(indic, 100)

# Add a needle line indicator
indic = meter.add_needle_line(4, lv.palette_main(lv.PALETTE.GREY), -10)

meter_value = lv.label(lv.scr_act())
meter_value.set_text('มหาวิทยาลัยมหาสารคาม')
meter_value.align(lv.ALIGN.CENTER, 0, -105)
meter_value.set_style_text_font(lv.Kanit_Regular_16, 0)

meter_value = lv.label(lv.scr_act())
meter_value.set_text('สารสนเทศศาสตร์')
meter_value.align(lv.ALIGN.CENTER, 0, -130)
meter_value.set_style_text_font(lv.Kanit_Regular_20, 0)

meter_value = lv.label(lv.scr_act())
meter_value.set_text("LVGL")
meter_value.align(lv.ALIGN.CENTER, -80, 120)
meter_value.set_style_text_font(lv.Kanit_Regular_16, 0)

meter_value = lv.label(lv.scr_act())
meter_value.set_text("V "+str(lv.version_major())+"."+str(lv.version_minor())+"."+str(lv.version_patch()))
meter_value.align(lv.ALIGN.CENTER, 0, 120)
meter_value.set_style_text_font(lv.Kanit_Regular_16, 0)

meter_value = lv.label(lv.scr_act())
meter_value.set_text("Micropython")
meter_value.align(lv.ALIGN.CENTER, -52, 140)
meter_value.set_style_text_font(lv.Kanit_Regular_16, 0)

meter_value = lv.label(lv.scr_act())
meter_value.set_text(str(sys.implementation[1]))
meter_value.align(lv.ALIGN.CENTER, 25, 140)
meter_value.set_style_text_font(lv.Kanit_Regular_16, 0)

# Create an animation to set the value
a = lv.anim_t()
a.init()
a.set_var(indic)
a.set_values(0, 100)
a.set_time(2000)
a.set_repeat_delay(100)
a.set_playback_time(500)
a.set_playback_delay(100)
a.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a.set_custom_exec_cb(lambda a,val: set_value(indic,val))
lv.anim_t.start(a)

สร้างปุ่มสีเหลืองขอบฟ้าข้อความ สวัสดีชาวโลก ฟอนต์ Kanit

from ili9XXX import ili9341
import lvgl as lv
disp = ili9341(rot=180 , clk=14, cs=15, dc=2, rst=12, power=23, backlight=21, miso=12, mosi=13,width=240, height=320,backlight_on=1)
lv.init()
scr = lv.obj()
lv.scr_load(scr)

btn = lv.btn(scr)
btn.align(lv.ALIGN.CENTER, 0, 0)
btn.set_style_outline_width(3, lv.STATE.DEFAULT)
btn.set_style_outline_color(lv.color_hex(0xFF0000), lv.STATE.DEFAULT)

# Set focused outline color
btn.set_style_outline_color(lv.color_black(), lv.STATE.FOCUSED)

label = lv.label(btn)
label.set_style_text_font(lv.Kanit_Regular_20, 0)
label.set_text('สวัสดี')

การใช้ Touch Screen บนบอร์ด ESP32 2432S028

» ESP32-2432S028  ทำงานบน ESP32-DOWDQ6 ความถี่สูงสุด 240MHz ออกแบบโดย  Adeept in China ติดต่อ support@adeept.com,
» https://www.adeept.com/video/ 
from xpt2046 import xpt2046
touch = xpt2046(clk=25, mosi=32, miso=39, cs=33, mhz=2)
touch.screen_width=240
touch.screen_height=320
while 1:
    print(touch.get_coords())

การสร้างปุ่มและข้อความบนปุ่ม

import lvgl as lv
lv.init()
scr = lv.obj()
btn = lv.btn(scr)
btn.set_size(100, 50)  # Set the button size
label = lv.label(btn)
label.set_text("Click me!")
lv.scr_load(scr)
disp.display_name  # ILI9341
print(disp.width , disp.height , disp.cs, disp.dc ,disp.power)  # 240 320 15 2 23
print(dir(lv.PALETTE))
['RED', 'PINK', 'BROWN', 'DEEP_ORANGE', 'CYAN', 'DEEP_PURPLE', 'BLUE', 'LIME', 'PURPLE', 'YELLOW', 'NONE', 'INDIGO', 'LIGHT_GREEN', 'GREEN', 'AMBER', 'ORANGE', 'GREY', 'TEAL', 'BLUE_GREY', 'LIGHT_BLUE']
» ใช้ขาสัญญาณตามนี้ https://github.com/rzeldent/esp32-smartdisplay/blob/main/include/esp32_smartdisplay.h

คู่มือและโปรแกรมจำลองการทำงาน lvgl

» https://sim.lvgl.io/
» คู่มือไลบรารี่ LVGL version 9 
»  ตัวอย่าง

บอร์ด ESP32 2432S028 อ่าน QR Code

» การต่อสายสัญญาณ
  - ESP32.pin27 -> GM805.Tx
  - ESP32.vcc -> GM805.vcc
  - ESP32.gnd -> GM805.gnd
import sys
from ili9XXX import ili9341
disp = ili9341(rot=180 , clk=14, cs=15, dc=2, rst=12, power=23, backlight=21, miso=12, mosi=13,width=240, height=320,backlight_on=1)

import lvgl as lv
lv.init()
def set_value(indic, v):
    meter.set_indicator_value(indic, v)

#
# A simple meter
#
meter = lv.meter(lv.scr_act())
meter.set_size(180, 180)
meter.center()

#Add a scale first
meter.set_scale_ticks(41, 2, 10, lv.palette_main(lv.PALETTE.GREY))
meter.set_scale_major_ticks(8, 4, 15, lv.color_black(), 10)
#meter.set_style_text_font(lv.font_montserrat_18, 0)

indic = lv.meter_indicator_t()

# Add a blue arc to the start
indic = meter.add_arc(3, lv.palette_main(lv.PALETTE.BLUE), 0)
meter.set_indicator_start_value(indic, 0)
meter.set_indicator_end_value(indic, 20)

# Make the tick lines blue at the start of the scale
indic = meter.add_scale_lines(lv.palette_main(lv.PALETTE.BLUE), lv.palette_main(lv.PALETTE.BLUE), False, 0)
meter.set_indicator_start_value(indic, 0)
meter.set_indicator_end_value(indic, 20)

# Add a red arc to the end
indic = meter.add_arc(3, lv.palette_main(lv.PALETTE.RED), 0)
meter.set_indicator_start_value(indic, 80)
meter.set_indicator_end_value(indic, 100)

# Make the tick lines red at the end of the scale
indic = meter.add_scale_lines(lv.palette_main(lv.PALETTE.RED), lv.palette_main(lv.PALETTE.RED), False, 0)
meter.set_indicator_start_value(indic, 80)
meter.set_indicator_end_value(indic, 100)

# Add a needle line indicator
indic = meter.add_needle_line(4, lv.palette_main(lv.PALETTE.GREY), -10)

meter_value = lv.label(lv.scr_act())
meter_value.set_text('มหาวิทยาลัยมหาสารคาม')
meter_value.align(lv.ALIGN.CENTER, 0, -105)
meter_value.set_style_text_font(lv.Kanit_Regular_16, 0)

meter_value = lv.label(lv.scr_act())
meter_value.set_text('สารสนเทศศาสตร์')
meter_value.align(lv.ALIGN.CENTER, 0, -130)
meter_value.set_style_text_font(lv.Kanit_Regular_20, 0)

a = lv.anim_t()
a.init()
a.set_var(indic)
a.set_values(0, 100)
a.set_time(2000)
a.set_repeat_delay(100)
a.set_playback_time(500)
a.set_playback_delay(100)
a.set_repeat_count(lv.ANIM_REPEAT_INFINITE)
a.set_custom_exec_cb(lambda a,val: set_value(indic,val))

lv.anim_t.start(a)

from machine import Pin, UART
uart = UART(1,baudrate=9600, rx=27, bits=8, parity=None, stop=1)
import time

def read_uart_data(uart, threshold_time):
    start_time = time.ticks_ms()

    while time.ticks_diff(time.ticks_ms(), start_time) < threshold_time * 1000:
        if uart.any():
            return uart.read()
    return None

def show():
    meter_value = lv.label(lv.scr_act())
    meter_value.set_text('สารสนเทศศาสตร์')
    meter_value.align(lv.ALIGN.CENTER, 0, -130)
    meter_value.set_style_text_font(lv.Kanit_Regular_20, 0)

        
while 1:
    data = read_uart_data(uart, 2.0)
    if data:
        show()
        meter_value.set_text(data.strip())
        meter_value.align(lv.ALIGN.CENTER, 0, 110)
        meter_value.set_style_text_font(lv.Kanit_Regular_20, 0)
    else:
        show()
        meter_value.set_text("     ")
        meter_value.align(lv.ALIGN.CENTER, 0, 110)
        meter_value.set_style_text_font(lv.Kanit_Regular_20, 0)
  
        meter_value.set_text("คิวอาร์ไอโอที")
        meter_value.align(lv.ALIGN.CENTER, 0, 110)
        meter_value.set_style_text_font(lv.Kanit_Regular_20, 0) 

บอร์ด WT32-SC01 และ WT32-SC01 PLUS

» บอร์ด WT32-SC01 เป็นบอร์ดที่มีจอสัมผัสขนาด 3.5 นิ้ว มีสองรุ่น คือ WT32-SC01 และ WT32-SC01 PLUS
» https://www.marutsu.co.jp/contents/shop/marutsu/datasheet/khe_WT32-SC01-PLUS.pdf
» ใช้ downloader tool -> เขียนเฟิร์มแวร์ firmware_s3_thai_font กำหนด [x] DIO -> start
» ใช้ thonny -> เข้าไป micropython -> ดาวน์โหลดไฟล์ไดรเวอร์มาใส่ lib/  -> https://github.com/russhughes/wt32sc01py
» เวอร์ชั่นแรก วันที่ 1 สค พ.ศ. 2565 -> เวอร์ชั่น 2 วันที่ 22 กันยายน พ.ศ. 2565 เพิ่ม firmware burining และ GUI development platform
» Debugging: pin1(5v), pin2(3.3v) , pin3(Tx), pin4(Rx), pin5(Chip Enable), pin6(GPIO0:Boot) , pin7(GPIO14:Gnd)
» Speaker Interface Pin1(SPK+), Pin2(SPK-)
» SD Card: sd_cs(gpio 41), mosi(gpio 40), clk(gpio 39), miso(gpio 38)
» LCD: bl_pwm(gpio 45), reset(gpio 4), rs(gpio0), wr(gpio 47), te(gpio48), db0(gpio 9), db1(gpio 46), db2 (gpio 3), db3(gpio 8), db4(gpio 18), db5(gpio 17), db6(gpio 16), db7(gpio 15), int (gpio 7), sda(gpio 6), scl(gpio 5), rst(gpio 4)
» RS485: pin1( A ), pin2 (B), pin3 (gnd) pin4(+5v), rx(gpio 1), tx(gpio 42), rts (gpio 2)
» Audio lrck(gpio 35), bclk(gpio 36), dout (gpio 37)
» display: st7796ui, 480x320, MCU8080 8bit, RGB565
» Touch: capacitive, FT6336U, I2C
» wt32sc01 plus driver -> https://penfold.owt.com/wt32sc01py/

import wt32sc01py as wt32
import vga2_bold_16x32 as font
tft = wt32.WT32SC01(1)
tft.rotation(0)  # 0, 1, 2, 3
tft.clear()
tft.text(font, "OK",10,10, wt32.color565(255,255,255), wt32.color565(0,0,0))


# เขียนข้อความสีขาว พื้นหลังสีเขียว
tft.text(font, "Hello!",10,10, wt32.color565(255,0,0), wt32.color565(0,255,0))


#ค่าการเชื่อมต่อสายสัญญาณ
print(tft.dc, tft.cs, tft.width, tft.rst, tft.bl)
# Pin(0) Pin(6) 320 Pin(4) Pin(45)

# การ fill จะช้ามาก
tft.fill(wt32.color565(0,255,0))

แม่สูตรคูณ

import wt32sc01py as wt32
import vga2_bold_16x32 as font
tft = wt32.WT32SC01(1)
tft.rotation(0)  # 0, 1, 2, 3
tft.clear()
for i in range(1,13):
    tft.text(font, "3 x %d = %d"%(i,3*i),10,30*i, wt32.color565(255,255,255), wt32.color565(0,0,0))

บอร์ด Freenove-ESP32-Wrover-CAM

การรับ/ส่งข้อมูล ESP32 และ 433mhz LoRa E32-433T20D V8.0 UART

» เชื่อมต่อ ESP32 และ 433mhz LoRa E32-433T20D V8.0 UART
  - ESP32.3v3 -> LoRa.3v3
  - ESP32.Gnd -> LoRa.Gnd
  - ESP32-Uart2.Tx(GPIO17) -> LoRa.Rx
  - ESP32-Uart2.Rx(GPIO16) -> LoRa.Tx
» คัดลอกไฟล์ https://github.com/xreef/EByte_LoRa_E32_micropython_library/tree/main/src 1) lora_e32.py 2) lora_e32_constants.py 3) lora_e32_operation_constant.py เก็บใน ESP32 -> โฟลเดอร์ /lib
» ตัวอย่างโค๊ดอยู่ใน https://github.com/xreef/EByte_LoRa_E32_micropython_library/tree/main/examples

ส่ง Broadcast String

from lora_e32 import LoRaE32, Configuration
from machine import UART

from lora_e32_constants import FixedTransmission
from lora_e32_operation_constant import ResponseStatusCode

# Initialize the LoRaE32 module
uart2 = UART(2)
lora = LoRaE32('433T20D', uart2, aux_pin=15, m0_pin=21, m1_pin=19)
code = lora.begin()
print("Initialization: {}", ResponseStatusCode.get_description(code))

# Set the configuration to default values and print the updated configuration to the console
# Not needed if already configured
configuration_to_set = Configuration('433T20D')
configuration_to_set.ADDL = 0x02  # Address of this sender no receiver
configuration_to_set.OPTION.fixedTransmission = FixedTransmission.FIXED_TRANSMISSION
code, confSetted = lora.set_configuration(configuration_to_set)
print("Set configuration: {}", ResponseStatusCode.get_description(code))

# Send a string message (fixed)
message = 'Hello, world!'
code = lora.send_broadcast_message(23, message)
# The receiver must be configured with ADDH = 0x00, ADDL = 0x01, CHAN = 23
print("Send message: {}", ResponseStatusCode.get_description(code))

รับข้อมูลสตริงใน channel

from lora_e32 import LoRaE32, Configuration, BROADCAST_ADDRESS
from machine import UART
import utime

from lora_e32_constants import FixedTransmission
from lora_e32_operation_constant import ResponseStatusCode

# Initialize the LoRaE32 module
uart2 = UART(2)
lora = LoRaE32('433T20D', uart2, aux_pin=15, m0_pin=21, m1_pin=19)
code = lora.begin()
print("Initialization: {}", ResponseStatusCode.get_description(code))

# Set the configuration to default values and print the updated configuration to the console
# Not needed if already configured
configuration_to_set = Configuration('433T20D')
# With BROADCASS ADDRESS we receive all message
configuration_to_set.ADDL = 0x01
configuration_to_set.ADDH = 0x00
configuration_to_set.CHAN = 23
configuration_to_set.OPTION.fixedTransmission = FixedTransmission.FIXED_TRANSMISSION
code, confSetted = lora.set_configuration(configuration_to_set)
print("Set configuration: {}", ResponseStatusCode.get_description(code))

print("Waiting for messages...")
while True:
    if lora.available() > 0:
        code, value = lora.receive_message()
        print(ResponseStatusCode.get_description(code))

        print(value)
        utime.sleep_ms(100)

การใช้งานเซนเซอร์วัดปุ๋ย NPK Sensor

» เซ็นเซอร์วัดค่า NPK ของดินเหมาะสำหรับตรวจจับปริมาณของไนโตรเจน, ฟอสฟอรัส และโพแทสเซียมในดิน ซึ่งช่วยในการกำหนดความอุดมสมบูรณ์ของดิน และส่งเสริมการประเมินสภาพดินอย่างเป็นระบบ เซ็นเซอร์นี้สามารถฝังอยู่ในดินได้นาน เซ็นเซอร์นี้มีโพรบที่มีคุณภาพสูง ทนต่อสนิม, ทนต่อการกัดกร่อนทางไฟฟ้า, ทนต่อเกลือและด่าง ซึ่งช่วยให้ส่วนโพรบทำงานได้อย่างยาวนาน ดังนั้นจึงเหมาะสำหรับทุกประเภทของดิน นอกจากนี้ยังเหมาะสำหรับการตรวจจับดินด่าง, ดินเปรี้ยว, ดินปลูกพืช, ดินปลูกต้นกล้า และดินมะพร้าวกระสอบได้เป็นอย่างดี
» อุปกรณ์ 1) NPK Sensor 2) RS485 to TTL 3) Micro Controller
» ค่าที่ส่งออกมาคือ ระดับของ NPK (ไนโตรเจน,ฟอสฟอรัส,โพแทสเซียม)

การใช้งานเซนเซอร์วัดออกซิเจนในน้ำ (Dissolved Oxygen Sensor)

» เซ็นเซอร์วัดออกซิเจนละลายในน้ำ (Dissolved Oxygen Sensor) ทำงานตามหลักการของการวัดปริมาณออกซิเจนที่ละลายอยู่ในน้ำ ซึ่งเป็นตัวบ่งชี้สำคัญของคุณภาพน้ำและสิ่งแวดล้อมทางน้ำ ออกซิเจนที่ละลายในน้ำจำเป็นสำหรับการหายใจของสัตว์น้ำและเป็นตัวชี้วัดการมีชีวิตทางน้ำที่สำคัญ หลักการทำงานของเซ็นเซอร์วัดออกซิเจนละลายในน้ำมีดังนี้:
   1. Electrochemical Oxygen Sensors: เซ็นเซอร์ชนิดนี้ใช้กระบวนการไฟฟ้าเคมีในการวัดปริมาณออกซิเจนที่ละลายในน้ำ โดยทั่วไปจะมีอิเล็กโทรดสองอันซึ่งเกิดปฏิกิริยาออกซิเดชั่น-รีดักชั่น เมื่อออกซิเจนติดต่อกับอิเล็กโทรด ปฏิกิริยานี้จะทำให้เกิดกระแสไฟฟ้าที่สามารถวัดได้ และกระแสนี้เป็นตัวบ่งชี้ถึงปริมาณออกซิเจนที่ละลายในน้ำ
   2. Optical Oxygen Sensors: เซ็นเซอร์ชนิดนี้ใช้หลักการของดาราศาสตร์แสง (luminescence) ในการวัดออกซิเจน โดยมีสารที่สามารถเรืองแสงเมื่อกระตุ้นด้วยแสงที่ความยาวคลื่นเฉพาะ และเมื่อสารนี้ติดต่อกับออกซิเจน การเรืองแสงจะถูกยับยั้งหรือลดลง การวัดความเข้มของแสงที่เรืองแสงจะให้ค่าของปริมาณออกซิเจนที่ละลายในน้ำ 
      - การใช้ Diode สีฟ้ามีความยาวคลื่น 450-495 nm และสีแดงที่มีความยาวคลื่น 630-700 nm ส่งออกมากระตุ้นสารเชิงซ้อน (sensing foil) เมื่อมีปริมาณอ็อกซิเจนมากจะทำให้การเปล่งแสงจากการกระตุ้นเชิงซ้อนน้อยลง 
      - Photodiodes หรือ Phototransistors มีความไวต่อช่วงความยาวคลื่นของแสงสีฟ้า
      - Color Sensors สามารถตรวจจับได้หลายสีและจะใช้ฟิลม์สีฟ้ากรองเพื่อวัดค่าสีฟิลเตอร์นั้น
      - Spectrophotometers ใช้กับงานวิจัยที่ต้องการความละเอียดที่มากยิ่งขึ้น
      - อินฟราเรด (IR) มีความยาว 700nm - 1mm มองไม่เห็น เพราะแสงที่มองเห็นคือ (380-700nm) ก) NIR 700-1400nm 2) MIR 1400-3000nm 3) FIR 3000-1um
   3. Clark Electrode Oxygen Sensors: เซ็นเซอร์ตามหลักการของ Clark Electrode ใช้เทคนิคปิดผนึกอิเล็กโทรดไว้ในเมมเบรนที่ออกซิเจนสามารถผ่านเข้ามาได้ อิเล็กโทรดนี้จะวัดปริมาณออกซิเจนที่ละลายในน้ำโดยการวัดกระแสไฟฟ้าที่เกิดจากปฏิกิริยาเคมีของออกซิเจนกับสารเคมีบนอิเล็กโทรด
» เซ็นเซอร์วัดออกซิเจนละลายในน้ำสามารถใช้ในหลายๆ สถานการณ์ เช่น การตรวจสอบคุณภาพน้ำในบ่อเลี้ยงปลา การตรวจวิเคราะห์น้ำในโรงงานบำบัดน้ำเสีย หรือในการศึกษาทางนิเวศวิทยาของแหล่งน้ำธรรมชาติ เป็นต้น

การใช้งาน EasyEDA

» EasyEDA ใช้ออกแบบวงจร ทำงาน 1) บนเว็บ easyeda.com 2) บน Desktop Computer
» การซูม: หมุนสกอร์
» การเลื่อน: คลิ๊กขวาแล้วเลื่อนเมาส์
» แถบเครื่องมือด้านขวา: เป็นการตั้งค่าคุณสมบัติเอกสาร ก) background color ข) visible grid ค) grid size ขนาดกริด ง) snap size จ) alt snap (กด alt และเลื่อนวัตถุ) 
» แถบ Drawing Tools สำหรับวาด 
» แถบ Wiring Tools ใช้ลากสายระหว่างอุปกรณ์ เช่น เครื่องมือลากเส้น, ไฟ 5 โวลต์
» แถบ Sheet1, Sheet2 ด้านล่างเหมือนใน Excel
» เครื่องมือด้านซ้าย Design Manager จัดการอุปกรณ์
» เครื่องมือด้านซ้าย Commonly LIbrary  คืออุปกรณ์ที่ใช้งานบ่อย เช่น LED, Transistor , 7-Segment ฯลฯ
» เครื่องมือด้านซ้าย Library จะเป็น online library จำนวนมากให้ใช้งาน ผู้ใช้อื่น ๆ สร้างเอาไว้ มีอุปกรณ์ของบริษัท LCSC Electronics จำหน่ายอุปกรณ์ให้เลือกใช้ได้
» เครื่องมือด้านซ้าย JLCPCB คือ สั่งผลิตจากบริการ JLCPCB
» การหมุนอุปกรณ์ ให้เลือกอุปกรณ์ จากนั้นกดแป้น space bar
» กด w เมื่อต้องการวาดเส้นระหว่างอุปกรณ์
» เพิ่ม Crystal เลือก library -> Crystals/Oscillator -> Crystals -> เลือกรุ่นที่ต้องการ
» เพิ่ม ICSP

การตรวจสอบว่ามี PSRAM (Pseudo Static Random Access Memory) ใน Micropython

» การตรวจสอบว่ามี PSRAM (Pseudo Static Random Access Memory) ในระบบที่ใช้ MicroPython สามารถทำได้โดยการเขียนสคริปต์ที่จะลองเข้าถึงหน่วยความจำ PSRAM และดูว่ามีข้อผิดพลาดหรือไม่ เนื่องจาก MicroPython ไม่มีฟังก์ชันมาตรฐานที่จะบอกว่ามี PSRAM หรือไม่โดยตรง สคริปต์จะขึ้นอยู่กับบอร์ดและการตั้งค่าระบบที่คุณใช้งานอยู่
try:
    # สำหรับ ESP32, การใช้งาน PSRAM อาจทำได้ผ่านการเรียกใช้งาน memoryview
    # โดยตรงบนบัฟเฟอร์ที่ใหญ่กว่าขนาดของหน่วยความจำภายใน
    ram_buffer = bytearray(4 * 1024 * 1024)  # สมมติว่าเรามี PSRAM ขนาด 4MB
    ram_view = memoryview(ram_buffer)
    ram_view[0] = 123  # เขียนค่าลงไปใน PSRAM
    print(ram_view[0])  # อ่านค่าที่เขียนไป
    print("PSRAM is available!")
except MemoryError:
    print("PSRAM is not available or not enough memory to allocate the buffer.")
» เมื่อคุณรันสคริปต์นี้, ถ้าไม่มีข้อผิดพลาด MemoryError แสดงว่าบอร์ดของคุณมี PSRAM และสามารถใช้งานได้ หากมีข้อผิดพลาด MemoryError แสดงว่าไม่มี PSRAM หรือหน่วยความจำที่คุณพยายามจองไม่สามารถใช้งานได้
» สำคัญที่จะต้องทราบว่า, วิธีนี้ไม่ได้รับประกันว่าจะทำงานได้กับทุกบอร์ดที่รัน MicroPython เนื่องจากการใช้งาน PSRAM อาจต้องมีการตั้งค่าหรือการคอนฟิกเฉพาะใน firmware ที่คุณใช้งาน. คุณอาจต้องอ่านเอกสารสำหรับบอร์ดของคุณเพื่อดูวิธีการที่ถูกต้องในการทดสอบ PSRAM.

อื่น ๆ

» บอร์ด IOXESP32
» IOXESP32 ใช้ ESP32 ECO V3 ออกแบบโดย ArtronShop และผลิตโดย บริษัท Gravitech Thai ราคา 275 บาท
» Wifi 2.4GHz และ Bluetooth v4.2
» Flash 4MB SRAM 520kB
» I2C x 2, SPI x2, UART x 3, I2S x 2, ADC 16 channels) DAC x2, CAN x 1, VCC=3.3v
» ขาใช้งานตรงกับ Node32U, Node32Lite, NodeMCU-32S
» maxpromer เป็นคนออกแบบ IOXESP32 ด้วย EasyEDA ในปี ค.ศ. 2020 -> https://github.com/maxpromer
» บริษัท artronshop.co.th พัฒนาบอร์ดเพื่อจำหน่ายด้าน maker -> https://www.artronshop.co.th/article

ESP32-S2 Mini

» ESP32-S2 Mini เป็นรุ่นย่อยของชิป ESP32-S2 ซึ่งเป็นชิปไมโครคอนโทรลเลอร์ที่พัฒนาโดย Espressif Systems ชิป ESP32-S2 เป็นส่วนหนึ่งของซีรีย์ ESP32 ซึ่งเป็นที่รู้จักในด้านการเชื่อมต่อ Wi-Fi และความสามารถในการเขียนโปรแกรมหลากหลายแพลตฟอร์ม
» โดยทั่วไป, ESP32-S2 Mini มีคุณสมบัติเหล่านี้:
   1. Wi-Fi แบบเดี่ยว: ชิปมีการเชื่อมต่อ Wi-Fi ที่ให้การสื่อสารไร้สายแบบ 2.4 GHz.
   2. หน่วยประมวลผล: มักจะมี CPU แบบ Xtensa® 32-bit LX7 ซึ่งให้ประสิทธิภาพการประมวลผลที่ดี.
   3. หน่วยความจำและที่เก็บข้อมูล: มีหน่วยความจำแบบ SRAM และมีตัวเลือกในการเพิ่ม flash memory ภายนอก.
   4. การเชื่อมต่อ: ชิปนี้มีหลากหลายพอร์ตเชื่อมต่อ เช่น GPIO, SPI, UART, I2C, และอื่นๆ.
   5. ความสามารถในการเขียนโปรแกรม: สามารถเขียนโปรแกรมได้ผ่านทาง Arduino IDE, ภาษา C/C++ ใน ESP-IDF, และแพลตฟอร์มอื่นๆ.
   6. ขนาดเล็กและประหยัดพลังงาน: ดีไซน์เพื่อการใช้งานที่ต้องการขนาดเล็กและการบริโภคพลังงานต่ำ.
»  ESP32-S2 Mini เหมาะสำหรับโปรเจ็กต์ DIY, การพัฒนา IoT หรือการใช้งานที่ต้องการขนาดเล็กและการเชื่อมต่อ Wi-Fi.
» การเขียน micropython ให้ใช้ thonny เวอร์ชั่น thonny-4.1.3-windows-portable หรือใหม่กว่า 
» ต่อสาย usb type-c -> option -> installation -> เลือก ESP32 รุ่น S2 -> เลือก firmware ในรายการ -> เขียน Firmware
» การใช้งานด้วย Thronny เหมือนการใช้งานทั่วไป

การตรวจสอบ Loopback บน ESP32-S2 Mini

» การเชื่อมต่อ TX pin และ RX pin บนบอร์ด ESP32-S2 เดียวกันสำหรับทดสอบการส่งและรับข้อมูลในเวลาเดียวกันนั้นเป็นไปได้ และเป็นวิธีที่ใช้ในการทดสอบโปรโตคอล UART บนบอร์ดเดียว. วิธีนี้เรียกว่า "loopback test" หรือการทดสอบลูปแบ็ก.
» ในการทดสอบลูปแบ็ก, ข้อมูลที่ส่งออกไปจาก TX pin จะถูกส่งกลับเข้ามาที่ RX pin ของบอร์ดเดียวกัน. นี่เป็นวิธีที่ดีในการทดสอบว่า UART กำลังทำงานอย่างถูกต้องหรือไม่ โดยไม่ต้องใช้อุปกรณ์ภายนอก.
» การส่งและรับข้อมูลในเวลาเดียวกันในรูปแบบนี้ไม่ใช่การสื่อสารแบบ full-duplex (ที่สามารถส่งและรับข้อมูลพร้อมกันได้อย่างแท้จริง) แต่เป็นการทดสอบการส่งและรับข้อมูลเป็นครั้งคราว.
» เชื่อมสายสัญญาณจาก pin 17 ไปยัง p 12 จากนั้นรันโค๊ดต่อไปนี้
from machine import UART, Pin
import time

# กำหนดขา GPIO สำหรับ TX และ RX
tx_pin = Pin(17)  # GPIO 17 เป็น TX
rx_pin = Pin(16)  # GPIO 16 เป็น RX

# สร้างอินสแตนซ์ของ UART โดยกำหนดบอดเรตและขา TX และ RX
uart = UART(1, baudrate=9600, tx=tx_pin, rx=rx_pin)

# ส่งและรับข้อมูล
while True:
    uart.write('Hello ESP32-S2')
    time.sleep(1)
    if uart.any():
        data = uart.read()
        print("Received:", data)

การใช้งาน GM805 2D Barcode Scanner

GM805-S และ GM805-L ผลิตโดยบริษัท Gangzhou Grow Technology Co., Ltd (www.hzgrow.com) สองรุ่นนี้ต่างกันที่เลนส์และระยะการอ่าน QR Code 
    - GM805-S ระยะอ่าน 5-30cm
   - GM805-L ระยะาน 7-50cm
» ซีรีส์ GM805 มีรุ่น GM805-S และ GM805-L ความแตกต่างเพียงอย่างเดียวระหว่าง GM805-S และ GM805-L คือเลนส์ ระยะอ่านของเลนส์ GM805-S คือ 5-30 ซม. ในขณะที่ระยะอ่านของเลนส์ GM805-L คือ 7-50 ซม. (ข้อมูลจริงขึ้นอยู่กับขนาดและเนื้อหาของรหัส)
» โมดูลอ่านบาร์โค้ดของซีรีส์ GM805 เป็นสแกนเนอร์ที่มีประสิทธิภาพสูง สามารถอ่านบาร์โค้ด 1 มิติได้ง่าย และอ่านบาร์โค้ด 2 มิติด้วยความเร็วสูง นอกจากนี้ยังมีความเร็วในการสแกนสูงสำหรับรหัสเส้นตรง แม้กระทั่งบาร์โค้ดบนกระดาษหรือหน้าจอ โมดูลอ่านบาร์โค้ดของซีรีส์ GM805 เป็นอัลกอริทึมการถอดรหัสบาร์โค้ดขั้นสูงที่พัฒนาขึ้นบนอัลกอริทึมการรู้จำภาพ ซึ่งสามารถอ่านบาร์โค้ดได้ง่ายและแม่นยำ ทำให้การพัฒนาต่อยอดง่ายขึ้น
» การเชื่อมต่อสายสัญญาณ
   - UART 9600bps, Data format: data (8 bit), stop( 1 bit), No parity bit
   - Controller MCU 3.3v ต่อตรงเข้ากับ Tx, Rx ได้โดยตรง 
  - ต่อกับคอมพิวเตอร์ต้อใช้ RS232 level converter chip
» คู่มือ articles/iot/qr/GM805-V1.2.pdf
from machine import Pin, UART
from time import sleep #sleep_us
uart = UART(1,baudrate=9600, rx=16, bits=8, parity=None, stop=1)
while True:
    if uart.any():
        data = uart.read()
        print(data)

WT32-SC01 Plus - ESP32 dev board With 3.5 Inch LCD Touch Screen

» ใช้ ESP32-S3 
» ESP32-S3 เป็นไมโครคอนโทรลเลอร์ที่พัฒนาโดยบริษัท Espressif Systems ซึ่งเป็นรุ่นที่ปรับปรุงใหม่จากชิป ESP32 ที่ได้รับความนิยมอย่างสูง มันเป็นชิปที่มาพร้อมกับ Wi-Fi และ Bluetooth แบบ dual-mode (BLE และ Bluetooth Classic) และมีคุณสมบัติพิเศษที่เพิ่มเข้ามาเช่นการสนับสนุนการประมวลผล AI ระดับขอบ (edge computing) และการประมวลผลสัญญาณดิจิตอล (DSP) ที่ปรับปรุงใหม่
» ESP32-S3 ออกแบบมาเพื่อการประยุกต์ใช้งานที่ต้องการประสิทธิภาพการประมวลผลที่สูงกว่าเดิมและการสนับสนุนคุณสมบัติการเชื่อมต่อที่เหนือกว่า เช่น การใช้งานในอุปกรณ์สมาร์ทโฮม, อุปกรณ์สวมใส่ได้, อุปกรณ์ IoT ที่มีความสามารถในการประมวลผลข้อมูลอัจฉริยะ และอื่นๆ นอกจากนี้ยังมีการสนับสนุนสำหรับการเข้ารหัสและการถอดรหัสภาพและเสียงที่ปรับปรุงใหม่ ทำให้เหมาะสำหรับการใช้งานที่เกี่ยวข้องกับมัลติมีเดีย
» รายละเอียดทางเทคนิคของ ESP32-S3 มักจะรวมถึง:
   - ซีพียู Xtensa® 32-bit LX7 dual-core ที่สามารถทำงานได้สูงสุดที่ประมาณ 240 MHz
   - เนื้อที่ RAM และ ROM ที่ใหญ่กว่าเมื่อเทียบกับรุ่นก่อน
   - การสนับสนุนการเชื่อมต่อ Wi-Fi 802.11 b/g/n และ Bluetooth 5 (LE)
   - สามารถสนับสนุนการทำงานด้าน AI ด้วย NPU (Neural Processing Unit)
   - หน่วยความจำ PSRAM แบบเสริมสำหรับการประมวลผลข้อมูลขนาดใหญ่
   - หน่วยความจำ Flash ที่สามารถเพิ่มเติมได้ผ่าน SPI
   - การสนับสนุนการทำงานของหลากหลายเซ็นเซอร์และอุปกรณ์นอกบอร์ดผ่าน GPIO
» ESP32-S3 ยังมีความยืดหยุ่นสูงในการพัฒนาด้วยการสนับสนุนจากการพัฒนาซอฟต์แวร์เช่น
   - Arduino IDE, Espressif IoT Development Framework (ESP-IDF), และ MicroPython ทำให้เป็นที่นิยมในหมู่ผู้พัฒนาและนักสร้างสรรค์โปรเจ็กต์ DIY.

การใช้งานสวิตช์ระดับน้ำแบบไม่สัมผัส Xkc-y21

» เป็นเซนเซอร์แบบ NPN โดยจะส่งสัญญาณเป็น Gnd (0v) เมื่อพบว่ามีน้ำในระดับเดียวกับสวิตช์ แรงดัน 5vdc 
» มีจำหน่าย 1) XKC-Y25-v จะมีผลลัพธ์เป็น 0/1 2) รุ่น NPN และ PNP  3) XKC-Y25-RS485
» เวลาตอบสนอง 500ms , อุณหภูมิน้ำ -20 ถึง 105 องศาเซลเซียส ระยะห่างจากผนังและน้ำ 2 cm , ความผิดพลาดของระดับน้ำ ~1.5 mm
» สาย mode ถ้าต่อเข้ากับ 1) Gnd จะมีเอาพุตแบบ NC (Normal Close) คือเท่าแรงดันที่จ่ายให้ MCU ~ 3.3 - 5vdc 2) ถ้าสาย mode ไม่มีต่อ เอาพุตจะเป็น NO (Normal Open) 
» ปล. สามารถใช้ IR Sensor ส่งคลืน Infrared และวัดพลังงานที่สะท้อนกลับมาได้
» ต้องตรวจสอบว่าบอร์ดที่คุณใช้งานรองรับการตั้งค่า pull-up สำหรับ GPIO ที่คุณต้องการใช้งาน และหมายเลขพินที่ใช้ในโค้ดอาจแตกต่างกันไปในแต่ละบอร์ด
from machine import Pin

# ฟังก์ชัน callback ที่จะถูกเรียกเมื่อมี interrupt
def on_interrupt(pin):
    print("Interrupt detected on pin:", pin)

# กำหนด GPIO 3 ให้เป็น input พร้อม pull-up
pin = Pin(3, Pin.IN, Pin.PULL_UP)

# กำหนดให้เรียกฟังก์ชัน callback เมื่อตรวจจับได้ falling edge
pin.irq(trigger=Pin.IRQ_FALLING, handler=on_interrupt)
» Pin.IRQ_FALLING หมายถึงการตรวจจับการเปลี่ยนแปลงของสัญญาณจากสูงไปต่ำ (falling edge).
» handler=on_interrupt กำหนดให้ฟังก์ชัน on_interrupt เป็นฟังก์ชัน callback ที่จะถูกเรียกเมื่อมี interrupt.
» หมายเหตุ: ในการใช้งาน interrupt, ควรหลีกเลี่ยงการทำงานที่ใช้เวลานานหรือการทำงานที่ซับซ้อนในฟังก์ชัน callback เพราะอาจส่งผลต่อการทำงานของระบบโดยรวม. นอกจากนี้, โปรดตรวจสอบว่าบอร์ดที่คุณใช้รองรับการใช้งาน interrupt กับ GPIO ที่คุณเลือก.

การใช้งาน SquareLine Studio ร่วมกับ MicroPython และ ESP32 เพื่อใช้งานกับ LVGL

» การใช้งาน SquareLine Studio ร่วมกับ MicroPython และ ESP32 เพื่อใช้งานกับ LVGL (Light and Versatile Graphics Library) ทำได้โดยการสร้างอินเทอร์เฟซผู้ใช้ (UI) ด้วย SquareLine Studio และจากนั้นนำโค้ดที่สร้างได้มาใช้ในโปรเจ็กต์ MicroPython บน ESP32
» ขั้นตอนการใช้งาน SquareLine Studio กับ MicroPython + ESP32
  - การติดตั้ง SquareLine Studio:
  - ดาวน์โหลดและติดตั้ง SquareLine Studio จากเว็บไซต์อย่างเป็นทางการของ SquareLine Studio.
» การออกแบบ UI ด้วย SquareLine Studio:
  - เปิด SquareLine Studio และเริ่มต้นการสร้างอินเทอร์เฟซผู้ใช้ (UI) ของคุณ. คุณสามารถใช้วิดเจ็ตและองค์ประกอบต่างๆ ที่มีให้เลือกมากมาย.
  - ทำการออกแบบ UI ให้ตรงกับความต้องการของโปรเจกต์ของคุณ.
» การส่งออกโค้ดจาก SquareLine Studio:
  - หลังจากที่ออกแบบ UI เสร็จสมบูรณ์, ให้ทำการส่งออกโค้ดจาก SquareLine Studio. โค้ดนี้จะถูกใช้ในโปรเจกต์ MicroPython ของคุณ.
» การตั้งค่าเพื่อใช้งาน LVGL บน ESP32 ด้วย MicroPython:
  - ตรวจสอบให้แน่ใจว่า MicroPython ที่ติดตั้งบน ESP32 ของคุณรองรับ LVGL. บางเวอร์ชันของ MicroPython มีการรวม LVGL มาให้แล้ว.
  - ถ้าจำเป็น, คุณอาจต้องติดตั้งไลบรารี LVGL เพิ่มเติมบน ESP32.
  - การนำโค้ดที่ส่งออกมาใช้ในโปรเจกต์ MicroPython:
» นำโค้ดที่ได้จาก SquareLine Studio ไปใช้ในโปรเจกต์ MicroPython ของคุณบน ESP32.
» ทำการปรับแต่งโค้ดตามความจำเป็นเพื่อให้สอดคล้องกับโครงสร้างโปรแกรมและฮาร์ดแวร์ของคุณ.
» การทดสอบและการปรับแต่ง:
» อัปโหลดโค้ดลงใน ESP32 และทำการทดสอบ UI ที่คุณได้ออกแบบมา.
» ทำการปรับแต่งและแก้ไขปัญหาที่อาจเกิดขึ้นจนกว่า UI จะทำงานได้อย่างลื่นไหลและตรงตามความต้องการ.


การควบคุม Stepper Motor ด้วย A4988 และ DRV8825

» A4988: สามารถรองรับการควบคุมในรูปแบบ microstepping ได้สูงสุดถึง 1/16 step.
» DRV8825: สามารถรองรับการควบคุมในรูปแบบ microstepping ได้สูงสุดถึง 1/32 step ซึ่งทำให้มีความละเอียดในการควบคุมที่สูงกว่า A4988.
» แรงดันและกระแสไฟฟ้าสูงสุด:
   - A4988: ทำงานได้ดีที่แรงดันประมาณ 8V ถึง 35V และสามารถจัดการกระแสได้สูงสุดประมาณ 1A โดยไม่ต้องใช้ heat sink, และสูงสุด 2A กับ heat sink.
   - DRV8825: สามารถจัดการกับแรงดันไฟฟ้าได้ระหว่าง 8.2V ถึง 45V และกระแสไฟฟ้าสูงสุดถึง 1.5A โดยไม่ต้องใช้ heat sink, และสูงสุด 2.2A กับ heat sink.
» การจัดการความร้อน:
   - A4988 อาจจำเป็นต้องใช้ heat sink หรือระบบระบายความร้อนในการใช้งานที่กระแสไฟฟ้าสูง.
   - DRV8825 มีประสิทธิภาพในการจัดการความร้อนที่ดีกว่า แต่ยังคงแนะนำให้ใช้ heat sink ในการใช้งานที่กระแสไฟฟ้าสูง.
» การลดสัญญาณรบกวนและการสั่นสะเทือน:
   - DRV8825 มีการปรับปรุงในแง่ของการลดสัญญาณรบกวนและการสั่นสะเทือน เมื่อเทียบกับ A4988.
» ความเข้ากันได้:
   - ทั้งสองไดร์เวอร์มีรูปแบบที่คล้ายกันและสามารถถูกใช้แทนกันได้ในหลายๆ โปรเจกต์. อย่างไรก็ตาม, อาจต้องมีการปรับแต่งการตั้งค่าในซอฟต์แวร์หรือฮาร์ดแวร์เล็กน้อยเมื่อเปลี่ยนจากหนึ่งไดร์เวอร์ไปยังอีกหนึ่งไดร์เวอร์.



การใช้งาน YOLOv8

» YOLO, หรือ "You Only Look Once", เป็นระบบการตรวจจับวัตถุแบบเรียลไทม์ที่ใช้งานง่ายและมีประสิทธิภาพสูงในการวิเคราะห์ภาพและวิดีโอ. มันถูกพัฒนาขึ้นเพื่อสามารถระบุและจำแนกวัตถุต่างๆ ในภาพหรือวิดีโอได้อย่างรวดเร็ว. ระบบ YOLO มีคุณสมบัติหลักดังนี้:
  - การตรวจจับแบบเรียลไทม์: YOLO สามารถประมวลผลภาพหรือวิดีโอและตรวจจับวัตถุในเวลาจริงได้.
  - การทำงานอย่างมีประสิทธิภาพ: แทนที่จะแยกการวิเคราะห์ภาพออกเป็นหลายขั้นตอน, YOLO วิเคราะห์ภาพทั้งหมดในเวลาเดียว (หรือ "look once") ซึ่งทำให้มีความเร็วและประสิทธิภาพสูง.
  - ความแม่นยำ: แม้ว่าในเวอร์ชันแรกๆ ความแม่นยำของ YOLO อาจไม่สูงเท่าระบบการตรวจจับวัตถุอื่นๆ, แต่ด้วยการพัฒนาต่อเนื่อง, ระบบ YOLO ได้ปรับปรุงความแม่นยำอย่างมากในเวอร์ชันหลังๆ.
  - การใช้งานที่หลากหลาย: YOLO ถูกใช้ในหลากหลายสถานการณ์ เช่น ระบบเฝ้าระวัง, การวิเคราะห์วิดีโอ, การจดจำป้ายทะเบียนรถยนต์, และอื่นๆ.
» ติดตั้ง pip install ultralytics=8.0.20
from ultralytics import YOLO
model=YOLO('yolov8n.pt')  #yolov8n-seg.pt, 
# results = model.predict("dog_bike_car.jpg")
results=model('dog_bike_car.jpg',verbose=False)

# จำนวนวัตถุที่ค้นพบ 4 ชิ้นภายในภาพ dog_bike_car.jpg
result = results[0]
len(result.boxes)

# result เป็น
for box in result.boxes:
  class_id = result.names[box.cls[0].item()]
  cords = box.xyxy[0].tolist()
  cords = [round(x) for x in cords]
  conf = round(box.conf[0].item(), 2)
  print("Object type:", class_id)
  print("Coordinates:", cords)
  print("Probability:", conf)
  print("---")
ผลลัพธ์วัตถุในไฟล์ภาพ:  dog_bike_car.jpg
Object type: dog
Coordinates: [131, 220, 309, 542]
Probability: 0.91
---
Object type: bicycle
Coordinates: [131, 140, 568, 421]
Probability: 0.89
---
Object type: car
Coordinates: [467, 75, 692, 172]
Probability: 0.53
---
Object type: truck
Coordinates: [467, 75, 693, 172]
Probability: 0.51
---

การเทรนวัตถุใหม่ด้วย Yolov8

» ติดตั้ง labelimg ด้วยคำสั่ง pip install labelimg
» เรียกคำสั่ง labelimg
» เปิดไฟล์วีดีโอ -> เลือก yolo -> Create RectBox -> 
» การ detect เรียกคำสั่ง yolo task-detect mode-predict model-yolov8n.py conf=0.25 source="http://abc.com/dog.jpg" save=True
» การ train เรียกคำสั่ง yolo task=detect mode=train model=yolov8s.pt data=/datasets/data.yaml epochs=100 imgs=800 plots=True
   - จากนั้นจะได้ผลลัพธ์ไฟล์ runs/detect/train/weight/best.pt
» ศึกษาเพิ่มเติม : https://youtu.be/ARHjcG509jo

การใช้งาน cvzone

» cvzone เป็น library ที่ใช้งานร่วมกับ Python ซึ่งทำงานเกี่ยวกับการประมวลผลภาพและวิดีโอ ถูกออกแบบมาเพื่อทำให้งานที่เกี่ยวข้องกับ Computer Vision ง่ายขึ้นและเข้าถึงได้มากขึ้นสำหรับนักพัฒนา และมีคุณสมบัติที่หลากหลายเกี่ยวกับการตรวจจับและการติดตามวัตถุ, การแยกแยะสี, การทำงานกับมือและท่าทาง, และอื่นๆ.
» คุณสมบัติของ cvzone ได้แก่:
   - การตรวจจับวัตถุและการติดตาม (Object Detection and Tracking): cvzone มีความสามารถในการตรวจจับและติดตามวัตถุต่างๆ ในภาพหรือวิดีโอ
   - การตรวจจับมือและท่าทาง (Hand Detection and Gesture Recognition): มีความสามารถในการตรวจจับมือและเข้าใจท่าทางของมือ, ซึ่งมีประโยชน์ในการสร้างปฏิสัมพันธ์ที่อิงกับท่าทาง
   - การประมวลผลภาพ (Image Processing): สามารถใช้ cvzone ในการประมวลผลภาพด้วยการใช้งานต่างๆ เช่น การปรับแต่งสี, การตรวจจับขอบ, และอื่นๆ
   - ความเข้ากันได้กับ OpenCV: cvzone ได้รับการพัฒนาให้ทำงานร่วมกับ OpenCV, ซึ่งเป็นห้องสมุดที่นิยมสำหรับการประมวลผลภาพและวิดีโอ
   - เรียบง่ายและเข้าถึงได้ง่าย: ห้องสมุดถูกออกแบบมาเพื่อทำให้การทำงานกับ Computer Vision เป็นเรื่องที่เข้าใจง่ายและเข้าถึงได้สำหรับนักพัฒนาที่มีระดับประสบการณ์ต่าง ๆ
   - cvzone เหมาะสำหรับโปรเจ็กต์ที่ต้องการความสามารถในการประมวลผลภาพและวิดีโอที่มีความซับซ้อนน้อยกว่า, หรือสำหรับผู้ที่เพิ่งเริ่มต้นเรียนรู้เกี่ยวกับ Computer Vision และต้องการเครื่องมือที่ใช้งานง่าย

การตรวจจับมือด้วย cvzone

import cv2
from cvzone.HandTrackingModule import HandDetector

# กำหนดการใช้งานกล้อง
cap = cv2.VideoCapture(0)

# สร้างตัวตรวจจับมือ
detector = HandDetector(detectionCon=0.8, maxHands=2)

while True:
    # อ่านภาพจากกล้อง
    success, img = cap.read()
    
    # ค้นหามือในภาพ
    hands, img = detector.findHands(img)

    # หากตรวจพบมือ
    if hands:
        # วนซ้ำทุกมือที่ตรวจพบ
        for hand in hands:
            # ข้อมูลที่เกี่ยวข้องกับมือ
            lmList, bboxInfo = hand["lmList"], hand["bbox"]
            # ทำงานกับข้อมูลนี้ตามต้องการ, อาทิเช่น พิมพ์พิกัด
            print(lmList)

    # แสดงผลภาพ
    cv2.imshow("Image", img)

    # หากกดปุ่ม 'q' จะหยุดการทำงาน
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# ปิดการใช้งานกล้องและหน้าต่าง
cap.release()
cv2.destroyAllWindows()

การตรวจจับท่าทางของมือเพื่อควบคุมวิดีโอเล่นหรือหยุด

import cv2
from cvzone.HandTrackingModule import HandDetector
import cvzone

# กำหนดการใช้งานกล้อง
cap = cv2.VideoCapture(0)

# สร้างตัวตรวจจับมือ
detector = HandDetector(detectionCon=0.8)

# โหลดวิดีโอ
videoPlayer = cvzone.VideoPlayer("path/to/video.mp4")

playing = False

while True:
    # อ่านภาพจากกล้อง
    success, img = cap.read()

    # ค้นหามือในภาพ
    hands, img = detector.findHands(img)

    # หากตรวจพบมือ
    if hands:
        hand = hands[0]
        fingers = detector.fingersUp(hand)
        # หากยกนิ้วชี้และโป้ง ให้เปลี่ยนสถานะการเล่นวิดีโอ
        if fingers[1] and fingers[0]:
            playing = not playing

    if playing:
        img = videoPlayer.nextFrame(img)
    else:
        videoPlayer.pause()

    # แสดงผลภาพ
    cv2.imshow("Image", img)

    # หากกดปุ่ม 'q' จะหยุดการทำงาน
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# ปิดการใช้งานกล้องและหน้าต่าง
cap.release()
cv2.destroyAllWindows()

การคำนวณความเร็วของวัตถุที่เคลื่อนไหวและนับจำนวนวัตถุในวิดีโอ

» อ่านวิดีโอ: ใช้ cv2.VideoCapture เพื่ออ่านฟรอมเฟรมของวิดีโอ.
» ตรวจจับวัตถุ: ใช้ตัวตรวจจับวัตถุ (เช่น YOLO, SSD หรืออื่นๆ) เพื่อตรวจจับวัตถุในแต่ละเฟรม.
» ติดตามวัตถุ: ใช้วิธีการติดตามวัตถุ (เช่น KCF, CSRT หรือ MOSSE tracker ใน OpenCV) เพื่อติดตามวัตถุที่ตรวจจับได้.
» คำนวณความเร็ว: คำนวณความเร็วของวัตถุโดยใช้การเปลี่ยนแปลงของตำแหน่งวัตถุในเวลาที่ผ่านไป.
» นับจำนวนวัตถุ: นับจำนวนวัตถุที่ตรวจจับได้.
import cv2
import time
from cvzone.ObjectDetectionModule import Detector

cap = cv2.VideoCapture("path/to/video.mp4")

# ตั้งค่าตัวตรวจจับ
detector = Detector("path/to/yolov3.weights", "path/to/yolov3.cfg", "path/to/coco.names")

# ตัวแปรสำหรับเก็บข้อมูลของวัตถุ
objects_prev_frame = {}
object_speed = {}

frame_id = 0
fps = cap.get(cv2.CAP_PROP_FPS)
frame_time = 1/fps

while True:
    success, img = cap.read()
    if not success:
        break

    img, bboxs = detector.findObjects(img)
    objects_current_frame = {}

    for bbox in bboxs:
        x, y, w, h, id, index = bbox
        center_x, center_y = x + w // 2, y + h // 2

        if index in objects_prev_frame:
            prev_center_x, prev_center_y = objects_prev_frame[index]
            distance = ((center_x - prev_center_x) ** 2 + (center_y - prev_center_y) ** 2) ** 0.5
            speed = (distance / frame_time) / fps
            object_speed[index] = speed
        else:
            object_speed[index] = 0

        objects_current_frame[index] = (center_x, center_y)
        cv2.putText(img, f"ID: {index} Speed: {object_speed[index]:.2f}", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)

    objects_prev_frame = objects_current_frame.copy()
    frame_id += 1

    cv2.imshow("Image", img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

การพิมพ์สลิปด้วยเครื่อง Thermal Printer RS232-TTL

» เครื่องพิมพ์ความร้อนแบบฝังใช้เทคโนโลยีความร้อน ซึ่งไม่จำเป็นต้องใช้หมึกหรือผงหมึก มีประสิทธิภาพที่มั่นคงและขนาดที่เล็กกระทัดรัด ทำให้เหมาะสมกับการใช้งานในหลากหลายสถานการณ์ เช่น ในเครื่องใช้ทางการแพทย์ การตรวจสอบอาหาร เครื่องจุดขาย และตู้ล็อกเกอร์ เครื่องพิมพ์เหล่านี้สามารถพบเห็นได้ในตู้เอทีเอ็ม ตู้ล็อกเกอร์ เครื่องจุดขายและตาชั่งอิเล็กทรอนิกส์ในซูเปอร์มาร์เก็ต เครื่องพิมพ์เหล่านี้ใช้ไฟฟ้าแรงดันกว้าง 5-9V โดยที่แรงดันไฟฟ้าที่สูงขึ้นจะทำให้ภาพพิมพ์ชัดเจนยิ่งขึ้น และรองรับกับม้วนกระดาษความร้อนที่มีความกว้าง 58MM นอกจากนี้ยังสามารถให้บริการพิมพ์ได้ยาวนานถึง 50 กิโลเมตร
» เครื่องพิมพ์เหล่านี้รองรับกับระบบ Arduino, Raspberry Pi และ Windows โดยใช้ชุดคำสั่งที่มีให้ เราสามารถส่งคำสั่งผ่านพอร์ตอนุกรม USB หรือ TTL เพื่อให้เครื่องพิมพ์ทำการพิมพ์ข้อความ ตัวอักษร บาร์โค้ด รหัส QR และภาพขาวดำตามคำสั่งที่ได้รับ นอกจากนี้ เรายังสามารถเชื่อมต่อกับบอร์ดควบคุม Arduino หรือ Raspberry Pi ผ่านพอร์ตอนุกรม และร่วมกับเซ็นเซอร์ต่างๆ เพื่อสร้างเครื่องมือทางปฏิบัติการได้หลากหลาย เช่น ตาชั่งอิเล็กทรอนิกส์ อุปกรณ์วัดค่าความดันโลหิต อุปกรณ์วิเคราะห์สารประกอบในดิน และกล้องบันทึกการขับขี่ นอกจากนี้ เรายังสามารถสร้างเครื่องพิมพ์ตั๋วขนาดเล็กของตัวเองได้ โดยเนื้อหาบนตั๋วสามารถกำหนดเองได้ และหลังจากการพัฒนาเพิ่มเติม เรายังสามารถทำการพิมพ์แบบรีโมทได้อีกด้วย
»  CircuitPython and Python

การใช้งาน SimpleFOC Mini

» SimpleFOC เป็นบอร์ดสำหรับขับมอเตอร์ BLDC 
» ไลบรารี่ https://github.com/simplefoc/
» https://docs.simplefoc.com/mini_connect_hardware
import simplefoc

commander = simplefoc.commander.serial("COM1", 115200)
commander.connect()

motor = commander.full_control('M')
motor.set_target(10)

motor.set_limits(max_voltage=12.0, max_current=1.0, max_velocity=20.0)
motor.set_mode(MotionControlType.torque, TorqueControlType.voltage)
motor.enable()

วงจรหน่วงเวลา

» ชิปทริกเกอร์หน่วงเวลา มัลติฟังก์ชั่น 10 ชิ้น จับเวลา IC Timing 2s-1000h
» แหล่งข้อมูล: shopee

บอร์ด P10 Matrix Panel Shield Arduino DMD2 Libraries

» P10 Matrix Panel:
  - P10 คือ LED Matrix Panel ที่มี "Pixel Pitch" 10 มิลลิเมตร หมายความว่าระยะห่างระหว่างแต่ละ LED ในแผงคือ 10 มิลลิเมตร
  - ถูกใช้ในการสร้างจอแสดงผลขนาดใหญ่ ทั้งในร่มและกลางแจ้ง สำหรับการแสดงข้อความ, ภาพกราฟิก, หรือแม้กระทั่งวิดีโอ
» DMD2 Libraries
  - DMD2 (Dot Matrix Display 2) Libraries เป็นชุดไลบรารีสำหรับ Arduino ที่ใช้ในการควบคุมและแสดงผลบน Dot Matrix Displays และ LED Panels
  - ไลบรารีนี้ช่วยให้นักพัฒนาสามารถเขียนโค้ดสำหรับควบคุม LED Panels ได้ง่ายขึ้น โดยมีฟังก์ชันสำหรับการจัดการข้อความ, ภาพกราฟิก และอื่นๆ
» โปรแกรม HD 2018 สำหรับเขียนตัวอักษรลงบนจอ P10 Led Matrix
» ป้าย P10 มีระยะห่างหลอด LED 10มม มีจำหน่ายสีแดง เหลือง เขียว ขาว และ RGB
» บอร์ดควบคุมจะมี Slot 1, 2, 3, 4 .. 16 สำหรับควบคุมแต่ละชั้น หนึ่งชั้นสามารถต่ออนุกรมได้หลายตัว
» โปรแกรม HD2018
   - ปรับความสว่าง -> Operation -> Brightness Settings -> ปรับ custom 50% -> ok -> กดปุ่ม U Disk เพื่อบันทึกลง Flash Drive
» P10 led ใช้ไอซี 74HC595 แปลงอนุกรมเป็นขนาน เพื่อขยาย I/O -> https://youtu.be/RMLYRQvhhVs

การใช้งาน Lightburn เพื่องานเลเซอร์ตัดชิ้นงาน

» ดาวน์โหลด Lightburn-v1.0.04 (ใส่นามสกุลไฟล์)
» การแปลงภาพเป็นเวกเตอร์  1) File->Import-> ไฟล์ภาพ 2) เลือกภาพ -> Tools -> Trace Image (Alt+t) -> ok -> ลบภาพ ให้เหลือแต่ลายเส้นที่ต้องการ 
» ขนาดชิ้นงาน ให้ปรับภาพวัตถุให้มีขนาดที่ต้องการโดยดูไม้บรรทัดที่ด้านซ้ายและล่าง
» หน้าต่างการพิมพ์ จะมีสัญลักษณ์ หยุด (stop) หยุดชั่วคราว (pause) สตาร์ท (start) 
   - เลือกเครื่องพิมพ์ ให้เลือก COM Port ที่เชื่อมต่อกับเครื่องพิมพ์ -> กำหนดขนาดกว้างxยาว ให้ใส่ตามต้องการเพราะซอฟต์รองรับความกว้างและยาวไม่จำกัด 
» หน้าต่าง move จะมีรูปไอคอน < > ^ ฯลฯ และมี set origin, clear origin  -> ให้เลื่อนหัวเลเซอร์ไปยังตำแหน่งเริ่มต้น (ใช้มือเลื่อนได้เลยที่เครื่อง) -> กดปุ่ม clear origin -> กดปุ่ม set origin
» กดปุ่ม frame เพื่อทดสอบเคลื่อนหัวเลเซอร์
» การตั้งค่าเครื่องพิมพ์ -> Edit -> Device Setting -> ตั้งความเร็ว กำลังเลเซอร์ และจำนวนรอบการตัด

การใช้เครื่องตัดเลเซอร์ K40

activate py38
pip install meerk40t[all]
meerk40t
meerk->ปุ่ม console (>>) -> พิมพ์ ruidacontrol 
lightburn -> device -> Create Mnually -> Ruida ->  Ethernet/UDP -> ไอพี 127.0.0.1 -> ตั้งชื่อ กำหนดความกว้าง 300mm สูง 200mm -> next
ที่โปรแกรม meek40t -> Config tab -> Config -> Interface tab -> ใส่ serial number: 81453C660A975553
การใช้งาน meerk40t ผ่าน command line
activate py38
meerk40t -zc
>> help	แสดงคำสั่งทั้งหมด
>> ruidacontrol   #เปิด ruidacontrol server port 50200 (data server) และ 50207 (jog server)
>> quit   คำสั่งออกจากโปรแกรม
>> usb_connect  เชื่อมต่อ usb+เครื่องตัดเลเซอร์
>> usb_disconnect ยุติการเชื่อมต่อ usb+เครื่องตัดเลเซอร์
>> usb_release ยกเลิกการเชื่อมต่อ
>> usb_reset รีเซต USB device
>> home เลื่อนหัวเลเซอร์ไปตำแหน่ง home
>> move 10mm 10mm	ย้ายไปซ้ายและขวาตำแหน่งที่ 10
ปล. การเปิด/ปิด เลเซอร์จะต้องควบคุมผ่านคำสั่ง G-Code (M3 เปิดเลเซอร์, M5 ปิดเลเซอร์) เท่านั้น  ไม่สามารถสั่งผ่าน console โดยตรงเพื่อความปลอดภัย
ปล. ความเร็ว 1400 mm/min ; number passes: 2; power: 40%

Two Mosfet Driver

» อุปกรณ์ที่ออกแบบมาเพื่อขับเคลื่อนและควบคุมการทำงานของสอง MOSFETs (Metal-Oxide-Semiconductor Field-Effect Transistors) ซึ่งเป็นประเภทของทรานซิสเตอร์ที่ใช้ในหลายๆ อุปกรณ์อิเล็กทรอนิกส์เพื่อการสวิตช์หรือขยายสัญญาณ

เครื่องเชื่อม IGBT

» การเช็คว่า Mosfet เสียหรือไม่ (ใช้กับ IGBT ได้เช่นกัน) 1) ขามอสเฟตทุกเบอร์จะเป็น Gate - Drain - Source 2) ตั้งมิเตอร์เข็มที่ 10K ohm ต่อสาย meter.+ เข้ากับ Mosfet.source และต่อสาย Meter.- เข้ากับ Mosfet.drain 3) ใช้นิ้วมือจับที่ขา gate + drain ที่มิเตอร์เข็มจะเลื่อนขึ้นมา 4) ใช้สาย meter.+ แต่ที่ขา mosfet.gate เข็มจะต้องกลับมาที่ตำแหน่ง 0


» หลอดไฟซ้ายมือต่อเอาพุต หลอดกลางและขวาต่ออนุกรมและวัดไฟ 300โวลต์ที่ผ่าน diode bridge dc เพื่อดูว่าไฟนิ่งมั้ย
» การต่อ soft switch ต่อดังภาพด้านบน บริเวณ input + bridge 


» หม้อแปลง CT (Current Transformer) จะต่ออนุกรมในหม้อแปลงหลักฝั่งอินพุต เพื่อตรวจจับกระแสเกิน
» R shunt จะต่อจากขั้ว Center ของหม้อแปลงหลักฝั่งขาออก และต่อลงกราวด์ เพื่อต้องการแรงดันที่คล่อม R-shunt เข้า opamp และไปเข้า SG3525.pin9 เพื่อปรับแต่งค่า duty cycle
» แรงดันที่หน้าตู้ก่อนผ่าน R shunt จะนำไปขยาย เพื่อควบคุมที่ขา sg3525.pin9 เพื่อควบคุม duty cycle จากภายนอก โดยอาศัยศักย์แรงดันที่ R-shunt ไปขยาย


» Diode Bridge 80A 1000v
» C 0.1/630v by pass สัญญาณรบกวน
» เมื่อไฟ 220vac ผ่าน Diode Bridge และจะผ่าน NTC ซึ่งเป็นตัวต้านทานเปลี่ยนค่าตามอุณหภูมิ เมื่อร้อนขึ้นความต้านทานลดลง สมัยก่อนจะใช้ R กระเบื้อง (R100 10W) แทน NTC เพราะตัวต้านทานจะใช้งานในช่วงเปิดเพียง  1 วินาที 
»  ต่อ Relay คล่อม NTC เพื่อให้ NTC ทำหน้าที่ลดการกระชากไฟเข้า C อย่างรุนแรง เนื่องจากหากไม่มีประจุใน C แล้วเปิดเครื่องจะมีความต้านทานแฝงที่ต่ำ กระแสจึงเข้าไปชาร์จตัวมันอย่างแรง เรียกกระแสกระชาก ซึ่งใช้ NTC มาช่วยลดกระแสกระชาก
» หลังจากชุดไฟต่ำทำงาน Relay 24v จะทำงานทำให้หน้าสัมผัสทำให้ไฟ 220v วิ่งผ่านหน้าสัมผัสของ Relay แทนการไหลผ่าน NTC
» C 100k ต่อค่อมไฟ 300v เมื่อปิดสวิตช์จะทำการ discharge ไฟที่ตกค้างใน C ออก


» IGBT เวลาช็อตจะแตกรุนแรง เพราะ
   1) IGBT และ MOSFET ไม่มีรอยต่อไม่มีจังชั่นทำให้มีความไวสูง  2) Transistor มีรอยต่อจังชั่นทำให้มีความไวในการสลับช้ากว่า
   2) ก) IGBT และ MOSFET ไบอัสด้วยแรงดันหรือสนามไฟฟ้า  ดังนั้น ห้ามลอยขา Gate แล้วเสียบไฟอย่างเด็ดขาด ข) Transistor ไปอัสด้วยกระแส
» AC Bias คือ หม้อแปลงฝั่งขาออกพันกลับทิศกัน ทำให้มีไฟออกเป็นทั้งบวกและลบ
» ไอซี SG3525 ขา CT และ RT เป็นตัวกำหนดความถี่ ห้ามวัดเพราะจะทำให้ความถี่เปลี่ยนแปลงรูปสัญญาณเพี้ยนทำให้ IGBT ช็อตได้
» ไอซี SG3525.pin8 เป็นขาป้องกัน แต่จริง ๆ เป็น softstart วัดไฟได้ประมาณ 3.8v แต่ถ้ามีแรงดัน 0v มันจะหยุดสร้าง pwm นอกจากนั้นขา 8 มีโครงสร้างสั่งให้ shutdownได้ ถ้าช็อตขา 8 ลง Gnd มันจะหยุดสร้าง PWM 
» ไอซี OpAmp ที่นิยมใช้มีสองเบอร์ ไม่สามารถใช้แทนกันได้
   1) LM358 เป็น opamp คู่อยู่ในไอซีตัวเดียว  ตัวแรกขา 1,2,3 และอีกตัวขา 6,7,5 ใช้ปรับกระแส
       - LM358.pin1 = output, LM358.pin2 = inverting (ขั้วลบ) , LM358.pin3 = non inverting (ขั้วบวก)
       - ก) จ่ายไฟ 5v เข้าขา 3 ข) แรงดัน 12.4v ผ่านอนุกรม R6.2k -> เข้าขา 2 จะมีแรงดัน 3.6v และ R5.1k เข้าขา 3 3) ถ้า ขา 2 > ขา 3 = ขา 1 = Low; ถ้าขา 2 < ขา 3 = ขา 1 = HIgh ->>>> ส่งไปขา Sg3525.pin8 เพื่อให้หยุดสร้าง pwm
   2) CA3140 เป็น opamp ตัวเดียว
» SCR ถ้ามีสัญญาณไป Trig แล้วมันจะเปิดอยู่อย่างนั้นตลอด


» Converter คือ การเปลี่ยนแรงดันและความถี่  
» Inverter คือ เปลี่ยนแรงดัน เช่น แปลงจากไฟ 12vdc เป็น 220vac
» Bulk Converter คือ แรงดันอินพุตต้องมากกว่าแรงดันเอาพุตเสมอ ประกอบด้วย 1) Transistor ใช้ NPN เป็นหลัก ปัจจุบันใช้ Mosfet หรือ IGBT 2) Diode 3) Inductor (L) ตัวเหนี่ยวนำ 4) Capacitor (C) ตัวเก็บประจุ
» ประเทศไทยใช้ไฟ 220vac เมื่อแปลงเป็นกระแสตรงจะได้ไฟ 300vdc -> Transistor ได้รับสัญญาณ PWM เข้ามาที่ขาเกต ถ้า PWM.+ จะทำให้ไฟผ่านตัวเหนี่ยวนำและผ่านโหลดออกไป อีกส่วนเก็บไว้ในตัวเก็บประจุ เมื่อ PWM.- ทรานซีสเตอร์จะ cut off ทำให้ตัวเหนี่ยวนำที่มีสนามแม่เหล็กเกิดการยุบตัว ทำให้ไฟลบผ่านไดโอดและไหลผ่านโหลดออกไป จากนั้นจะมีวงจรแบ่งแรงดันด้วยตัวต้านทานสองตัว และแบ่งแรงดันออกมาเป็น Feedback และส่งให้วงจรเปรียบเทียบแรงดัน จากนั้นนำแรงดันที่เป็นผลต่างเอาไปควบคุม Pwm เพื่อให้แรงดันคงที่

DM13A (16-bit Constant Current LED Driver)

»  DM13A ไดรเวอร์ LED แบบ 16 บิตสำหรับกระแสคงที่ รุ่น DM13A จากบริษัท Silicon Touch Technology Inc. ไดรเวอร์นี้ออกแบบมาสำหรับการใช้งานแสดงผล LED และมีคุณสมบัติเด่นดังนี้:

มีช่องออกแสง LED ทั้งหมด 16 ช่อง สามารถปรับกระแสแต่ละช่องได้ด้วยตัวต้านทานภายนอกเดียวตั้งแต่ 3mA ถึง 60mA.
- รองรับแรงดันไฟฟ้าสูงสุดที่ 17V.
- ความถี่สูงสุดที่ 25MHz.
- ใช้ไฟเลี้ยงตั้งแต่ 3.3V ถึง 5V.
- มีการควบคุมกระแสเริ่มต้นที่เข้าสู่ระบบ.
- ใช้งานได้กับระบบแสดงผล LED ทั้งในร่มและกลางแจ้ง.
ไดรเวอร์นี้มีการออกแบบให้สามารถเข้ากันได้กับแพ็คเกจและขาเชื่อมต่อของไดรเวอร์ LED แบบเดิมๆ เช่น ST2221C, DM134/5/6 ซึ่งทำให้ง่ายต่อการใช้งานและเปลี่ยนแปลงในการออกแบบระบบไฟ LED ที่มีอยู่
from machine import Pin
import utime

# กำหนด GPIO pins สำหรับ DM13A
data_pin = Pin(2, Pin.OUT)  # DAI
clock_pin = Pin(3, Pin.OUT)  # DCK
latch_pin = Pin(4, Pin.OUT)  # LAT
enable_pin = Pin(5, Pin.OUT)  # EN

# ฟังก์ชันสำหรับส่งข้อมูลไปยัง DM13A
def send_data(data):
    for i in range(16):  # DM13A มี 16-bit ข้อมูล
        bit = (data >> i) & 1  # คำนวณ bit ที่ i จากท้าย
        data_pin.value(bit)  # ตั้งค่าข้อมูล
        clock_pin.value(1)  # ตั้งค่านาฬิกา
        utime.sleep_us(10)  # รอสักครู่
        clock_pin.value(0)  # รีเซ็ตนาฬิกา

# ลงล็อกข้อมูลในรีจิสเตอร์ของ DM13A
def latch_data():
    latch_pin.value(1)
    utime.sleep_us(10)
    latch_pin.value(0)

# ฟังก์ชันสำหรับเปิดใช้งาน DM13A
def enable_output():
    enable_pin.value(0)  # เปิดใช้งาน output

# ฟังก์ชันสำหรับปิดใช้งาน DM13A
def disable_output():
    enable_pin.value(1)  # ปิดใช้งาน output

# เริ่มต้นโดยการตั้งค่า
disable_output()  # ปิดใช้งานขาออกเพื่อตั้งค่าเริ่มต้น
send_data(0x0000)  # ส่งข้อมูลเริ่มต้น
latch_data()
enable_output()

# ตั้งค่าเพื่อเปิด LED ที่ Do, D1, D3
# คำนวณ bit pattern ที่จำเป็น: 0000000000001011 คือ 0x000B
send_data(0x000B)
latch_data()

การควบคุม DM13A ต่ออนุกรมรวม 32 หลอด LED

from machine import Pin
import time

# กำหนดขาที่ใช้สำหรับการส่งข้อมูล (DATA), นาฬิกา (CLOCK), LATCH, และ ENABLE
data_pin = Pin(18, Pin.OUT)
clock_pin = Pin(19, Pin.OUT)
latch_pin = Pin(21, Pin.OUT)   # กำหนดขาสำหรับ LATCH
enable_pin = Pin(22, Pin.OUT)  # กำหนดขาสำหรับ ENABLE

# ข้อมูลหลอด LED ที่ต้องการแสดง 0 คือ OFF, 1 คือ ON
led_state = "11010111100110010101100011011011"

def send_bit(value):
    data_pin.value(value)
    clock_pin.value(1)
    time.sleep_us(1)  # หน่วงเวลาสั้นๆ เพื่อให้นาฬิกาได้ถูกอ่าน
    clock_pin.value(0)

def update_leds(state):
    enable_pin.value(0)  # เปิดการทำงานของชิพ
    for bit in state:
        send_bit(int(bit))
    latch_pin.value(0)
    latch_pin.value(1)   # Latch ข้อมูลที่ส่งเข้าไป
    latch_pin.value(0)
    enable_pin.value(1)  # ปิดการทำงานของชิพเพื่อลดการรบกวน

# อัปเดตสถานะของหลอด LED
update_leds(led_state)

แหล่งข้อมูล

» ไลบรารี่ micropython -> https://awesome-micropython.com/
IoT (Internet Of Things)