> For the complete documentation index, see [llms.txt](https://think-embedded.gitbook.io/micropython/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://think-embedded.gitbook.io/micropython/micropython-for-esp32/esp32-networking.md).

# ESP32 Networking

## 1. **ESP32 Networking** <a href="#id-943e" id="id-943e"></a>

**ESP32** สามารถเชื่อมต่อกับอุปกรณ์อื่นแบบไร้สายได้ โดยใช้ **Wi-Fi** ตามมาตรฐาน **IEEE 802.11b/g/n (2.4GHz)** และ **Bluetooth** และมีโหมดการทำงานสำหรับ **Wi-Fi** ดังนี้

* **STA (Station)**
* **AP (Access Point)**&#x20;
* **Soft-AP** (**both STA and AP**)&#x20;

แต่ถ้าจะเชื่อมต่อกับ **Ethernet** จะต้องมีวงจร **Ethernet PHY Transceiver** (**RMII Interface**) นำมาต่อเพิ่ม เช่น **LAN8720** หรือ **TLK110 (10/100 Mbps)** เป็นต้น ตัวอย่างบอร์ด **ESP32** ที่มี **Ethernet/LAN Port** ได้แก่

* [**Espressif Systems ESP32-Ethernet-Kit**](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit.html) (**ESP32-WROVER-B + IP101GRI Transceiver**)&#x20;
* [**LilyGo / TTGO T-Internet-POE Board**](https://github.com/Xinyuan-LilyGO/LilyGO-T-ETH-POE) **(ESP32-WROOM + LAN8720A Transceiver)**&#x20;
* [**Wireless Tag** **WT32-ETH01**](http://www.wireless-tag.com/portfolio/wt32-eth01/) **(ESP32-based WT32-S1 Wi-SoC + LAN8720A transceiver)**

สำหรับ **ESP32** การเชื่อมต่อด้วย **Wi-Fi** คงเป็นวิธีที่ง่ายและสะดวกที่สุด โดยทั่วไปแล้ว เราจะโปรแกรมให้อุปกรณ์ **ESP32** ทำงานในโหมด **STA** และเชื่อมต่อกับอุปกรณ์ **Wi-Fi Access Point** หรือ **Router** ในระบบเครือข่าย **WLAN** และทำให้สามารถเชื่อมต่อไปยังอินเทอร์เน็ตได้

ถ้าจะให้ **ESP32** ทำงานในโหมด **AP** หรือ **Soft-AP** ก็อนุญาตให้อุปกรณ์อื่นเชื่อมต่อผ่าน **Wi-Fi** เข้ามายัง **ESP32** ได้ เราอาจจะเห็นตัวอย่างการใช้งาน **ESP32** ที่ทำงานในโหมดดังกล่าวและทำหน้าที่เป็น **Light-weight Web Server** ในตัวด้วย ทำให้คอมพิวเตอร์อื่นสามารถเชื่อมต่อผ่าน **Wi-Fi** มายัง **ESP32** และผู้ใช้สามารถเปิด **Web Browser** เพื่อเข้าสู่หน้าเว็บของ **ESP32** เช่น ใช้ในการตั้งค่าอุปกรณ์ผ่านหน้าเว็บ หรือดูสถานะบางอย่างของระบบ เป็นต้น

อีกตัวเลือกหนึ่งที่น่าสนใจสำหรับ **ESP32** คือ การสื่อสารแบบไร้สายในรูปแบบที่เรียกว่า [**ESP-NOW**](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html) โดยไม่จำเป็นต้องใช้ **Wi-Fi Infrastructure** แต่ใช้รูปแบบการทำงานแบบ **Peer-to-Peer** และในปัจจุบัน เราก็สามารถใช้ [**ESP-NOW** (**Micropython Port**](https://github.com/glenn20/micropython/blob/espnow-g20/docs/library/espnow.rst)) ร่วมกับไมโครไพธอนได้เช่นกัน

## 2. ESP32 Wi-Fi Scan

เริ่มต้นตัวอย่างแรกด้วยการสแกนหาอุปกรณ์หรือเครือข่ายไร้สาย **Wi-Fi** โดยจะต้องทำให้ **ESP32** เปิดใช้งานในโหมด **STA** ก่อน และสามารถใช้คำสั่งจากไลบรารี `network` ดังนี้ โดยทำการสแกนซ้ำไปเรื่อย ๆ และถ้าต้องการหยุดหรือจบการทำงาน ให้กด **Ctrl+C** ในหน้าต่าง **Interactive Shell** ของ **REPL**

```python
import network
import utime as time

# open WiFi interface in STA mode 
wifi = network.WLAN( network.STA_IF )

# activate the WiFi interface (if_up)
wifi.active( True )

print ('Press Ctrl+C to stop WiFi scanning...')
try:
    while True:
        # scan WiFi
        scan_results = wifi.scan()
        # print scan results (a list of tuples)
        print(60*'=')
        for ap in scan_results:
            print( ap )
        print(60*'-')
except KeyboardInterrupt:
    print('Terminated...')
# deactivate the WiFi interface
wifi.active(False)
```

เมื่อสแกนเครือข่าย **Wi-Fi** ข้อมูลสำหรับแต่ละอุปกรณ์ที่พบ จะประกอบด้วยชื่ออ้างอิงในการเชื่อมต่อ (**SSID**), หมายเลขของอุปกรณ์ (**MAC address**), ช่องสัญญาณความถี่ (**Channel**), ค่าตัวชี้วัดความแรงของสัญญาณที่รับได้ (**RSSI**), โหมดการเข้ารหัสสำหรับความปลอดภัย (**Authentication Mode**) และมีการซ่อนชื่อ **SSID** หรือไม่ (**Hidden**) ตามลำดับ

ลองมาดูตัวอย่างการเขียนโค้ดไมโครไพธอนที่ใช้สำหรับสแกนระบบเครือข่าย **Wi-Fi** ที่อยู่โดยรอบ แล้วแปลงผลที่ได้ให้เป็นข้อมูลแบบ **JSON** เช่น แสดงชื่อ **SSID** รหัสเครื่อง **(MAC Address)** โหมดการตั้งค่าความปลอดภัย (**Authentication Mode**) และช่องสัญญาณ เป็นต้น

```python
import network
import ujson as json

param_names = ['essid','mac','channel','rssi','authmode','hidden']

authmodes = ['OPEN','WEP','WPA-PSK',
             'WPA2-PSK','WPA/WPA2-PSK','MAX']

def mac_bytes_to_hex_str( mac_bytes ):
    return ':'.join(['%02X' % x for x in mac_bytes])

def convert_to_json( found_list ):
    results = {'count': len(found_list), 'found_list': []}
    for ap in found_list:
        pairs = dict(zip(param_names,ap))
        for name in pairs:
            value = pairs.get(name)
            if type(value)==bytes:
                if name=='mac' and len(value)==6:
                    value = mac_bytes_to_hex_str( value )
                else:
                    value = value.decode('ascii')
            else:
                try:
                    value = int(value)
                    if name=='authmode' and 0 <= value <= 5:
                        value = authmodes[value]
                except (ValueError, TypeError):
                    pass
            pairs[name] = value
        results['found_list'].append( pairs )
    return results# open WiFi interface in STA mode

# activate the WiFi interface
wifi = network.WLAN( network.STA_IF )
wifi.active(True)
# scan WiFi and get the results as a list of tuples
scan_results = wifi.scan()
# deactivate the WiFi interface
wifi.active(False)
# convert a list of tuples to json data
results = convert_to_json( scan_results )

if results.get('count') > 0:
    for item in results.get('found_list'):
        print( json.dumps(item) ) # show JSON string
else:
    print( 'No WiFi found')
```

## **3. Wi-Fi Connection in STA Mode** <a href="#a9e3" id="a9e3"></a>

การเชื่อมต่อกับเครือข่าย **Wi-Fi** ในโหมด **STA** จะต้องระบุชื่อ **SSID** และรหัสผ่าน (**Password**) สำหรับเชื่อมต่ออุปกรณ์ไร้สาย เช่น **Wireless Access Point / Router** ตามตัวอย่างโค้ดต่อไปนี้

ในตัวอย่างนี้ มีการสร้างฟังก์ชันชื่อ `connect_wifi()` เพื่อใช้ในการเชื่อมต่อ **Wi-Fi (STA Mode)** เมื่อเรียกใช้ จะต้องระบุอาร์กิวเมนต์เป็นโครงสร้างข้อมูลแบบ **dictionary** อ้างอิงโดยตัวแปร `WIFI_CFG` ที่มีชื่อสมาชิก `‘ssid’` และ `‘pwd’` ตามลำดับ ดังนั้นให้กำหนดค่าให้ถูกต้องเมื่อนำไปใช้งาน

เมื่อเรียกใช้ฟังก์ชันนี้ และสามารถเชื่อมต่อได้สำเร็จ จะได้ค่ากลับคืนเป็นอ็อบเจกต์ที่ไม่ใช่ `None` และเราก็สามารถใช้คำสั่ง `wifi.ifconfig()` เพื่อดูข้อมูลเกี่ยวกับการเชื่อมต่อเครือข่าย เช่น หมายเลขของไอพีที่ได้รับ (**IP address**) หมายเลขของไอพีของอุปกรณ์ **Gateway** เป็นต้น

```python
import network
import utime as time

# Specify the SSID and password of your WiFi network
WIFI_CFG = { 'ssid': "XXXXXX", 'pwd': "XXXXXXXXX" }

def connect_wifi( wifi_cfg, max_retries=10 ):
    # use WiFi in station mode (not AP)
    wifi_sta = network.WLAN( network.STA_IF )
    # activate the WiFi interface (up)
    wifi_sta.active(True)
    # connect to the specified WiFi AP
    wifi_sta.connect( wifi_cfg['ssid'], wifi_cfg['pwd'] )
    retries = 0
    while not wifi_sta.isconnected():
        retries = retries + 1
        if retries >= max_retries:
            return None
        time.sleep_ms(500)
    return wifi_sta

# try to connect the network
wifi = connect_wifi( WIFI_CFG )

if wifi is None:
    print( 'WiFi connection failed' )
else:
    ipaddr, netmask, gateway, dns = wifi.ifconfig()
    print("============================")
    print("IP address  :", ipaddr)
    print("Net mask    :", netmask)
    print("Gateway     :", gateway)
    print("DNS server  :", dns)
    print("----------------------------")
```

## **4. Wi-Fi in AP Mode + Simple HTTP Server** <a href="#cc19" id="cc19"></a>

ตัวอย่างถัดไป สาธิตการเปิดใช้งาน **Wi-Fi** ในโหมด **AP** แบบ **Open** (ไม่มีการป้องกันด้วยรหัสผ่าน) โดยการสร้างและเรียกใช้ฟังก์ชัน `start_wifi_ap()` จากนั้นเมื่อ **ESP32** ทำงานเป็น **Wi-Fi AP** แล้ว (จะมีหมายเลขไอพีตรงกับ `192.168.4.1`  และเลือกใช้ช่องสัญญาณหมายเลข 11) ยังได้มีการสร้างและเรียกใช้ฟังก์ชัน `start_web_server()` เพื่อให้สามารถทำงานเป็น **Web Server** (**HTTP protocol**) อย่างง่ายได้ด้วย แสดงข้อความเป็นหน้าเว็บได้

ในตัวอย่างนี้ ถ้าต้องการจบการทำงานของ **Web Server** เราจะใช้วิธีตรวจสอบการกดปุ่ม ดังนั้นจะต้องมีการต่อวงจรกดปุ่มภายนอกแบบ **Active-Low** และเลือกใช้ขา **GPIO23** เป็นอินพุตสำหรับปุ่มกด

```python
import network
import utime as time 
import usocket as socket
from machine import Pin

AP_CFG = { 'essid': 'MyAP', # SSID for your AP
           'authmode': network.AUTH_OPEN, 'channel': 11, }

def start_wifi_ap( ap_cfg ):
    # open WiFi in AP mode
    ap = network.WLAN( network.AP_IF )
    # activate AP interface (up)
    ap.active(True)
    # configure the AP interface
    try:
        ap.config( **ap_cfg )
    except Exception as ex:
        print ( 'Cannot set AP configuration...' )
    if ap.active():
       return ap
    else:
       return False

def start_web_server( btn=None ):
    global sock
    # create a socket (STREAM TCP socket) for network connection
    sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
    # set socket option: reuse address
    sock.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
    # use socket in non-blocking mode
    sock.setblocking( False )
    # get the server address (localhost)
    addr = socket.getaddrinfo('0.0.0.0',80)[0][-1]
    # bind the socket to port 80 (HTTP)
    sock.bind( addr )
    # listen for an incoming connection
    sock.listen(1)
    while True:
        try:
            # waiting for connection
            conn,addr = sock.accept() 
        except OSError:
            if btn and btn.value()==0: # check button
               sock.close() # close socket
               break        # break the loop
            else:
               continue
        # read incoming request data
        request = conn.recv(1024) 
        print( 'Content = %s' % str(request) )
        # send HTML as response
        html = '<html><head></head><body>'
        html += '<h1>Welcome to ESP32...</h1><br>'
        html += '<b>Client from {}<b><br>'.format(str(addr))
        html += '</body></html>'
        conn.send( html ) # send HTML response
        conn.close() # close HTTP connection
        time.sleep_ms(10)

sock = None
ap = start_wifi_ap( AP_CFG )
ipaddr = ap.ifconfig()[0]
print ( 'IP address:', ipaddr ) # 192.168.4.1

BTN_GPIO = const(23) # use GPIO23 for push button
btn = Pin( BTN_GPIO, Pin.IN, Pin.PULL_UP )

try:
   start_web_server( btn ) # start web server
except KeyboardInterrupt:
    pass
finally:
    if sock:
        sock.close()
    ap.active(False) # turn off WiFi AP
```

ถ้าจะเปลี่ยนจาก **Open** เป็นการตั้งรหัสป้องกันด้วยวิธี **WPA2-PSK** ก็กำหนดค่าสำหรับ **Configuration** ตามตัวอย่างดังนี้

`AP_CFG = { 'essid': 'MyAP', 'password': 'YOUR_STRONG_PASSWORD',` \
&#x20;          `'authmode': network.AUTH_WPA2_PSK, 'channel':11, }`

## 5. Getting Date & Time From NTP Server&#x20;

ตัวอย่างถัดไปสาธิตการเปิดใช้งาน **ESP32** ในโหมด **STA** และเชื่อมต่อกับอุปกรณ์ **Wi-Fi AP** ที่สามารถเชื่อมต่อไปยังอินเทอร์เน็ตได้ และใช้คำสั่ง `ntptime.settime()` เพื่อเชื่อมต่อกับคอมพิวเตอร์ในอินเทอร์เน็ตที่ทำหน้าที่เป็น **NTP Server**  และอัปเดทเวลาปัจจุบันสำหรับการทำงานของวงจรของ **RTC (Real-Time Clock)** ของ **ESP32**

```python
import network
import utime as time
import ntptime
import machine

# Specify the SSID and password of your WiFi network
WIFI_CFG = { 'ssid': "XXXX", 'pwd': "XXXXXXX" }

def connect_wifi( wifi_cfg ):
    wifi_sta = network.WLAN( network.STA_IF )
    wifi_sta.active(True)
    wifi_sta.connect( wifi_cfg['ssid'], wifi_cfg['pwd']  )
    while not wifi_sta.isconnected():
        time.sleep(1.0)
    print(wifi_sta.ifconfig())

def sync_ntp():
    while True:
        try:
            # synchronize RTC with NTP server
            ntptime.settime()
            break
        except OSError:
            print('NTP server: connection timeout...')

connect_wifi( WIFI_CFG )
sync_ntp()

rtc = machine.RTC()
tm = rtc.datetime() # get current RTC datetime (now)
tz_offset = +7 # timezone offset (GMT+7)
hh, mm, ss = (tm[4]+tz_offset) % 24, tm[5], tm[6]
print( 'Time: {}:{}:{}'.format( hh, mm, ss ) )
y, m, d = tm[0], tm[1], tm[2]
print( 'Date: {}-{:02d}-{:02d}'.format( y, m, d ) )
```

## 6. HTTP GET: Getting Datetime Using WorldTime API <a href="#id-1317" id="id-1317"></a>

ตัวอย่างถัดไปสาธิตการเปิดใช้งาน **ESP32** ในโหมด **STA** และเชื่อมต่อกับอุปกรณ์ **Wi-Fi AP** ที่สามารถเชื่อมต่อไปยังอินเทอร์เน็ตได้ และร้องขอข้อมูลแบบ **HTTP Request / GET Method** ไปยัง [**World Time API**](http://worldtimeapi.org/) ซึ่งเป็นผู้ให้บริการข้อมูลเกี่ยวกับวันและเวลาในปัจจุบันตามโซนเวลา (**Timezone**) ที่ระบุไว้ ข้อมูลที่ได้รับกลับมานั้น จะอยู่ในรูปแบบของ **JSON String**

```python
import network
import utime as time
import ujson as json
import urequests as requests

URL = "http://worldtimeapi.org/api/timezone/Asia/Bangkok"

# Specify the SSID and password of your WiFi network
WIFI_CFG = { 'ssid': "XXXX", 'pwd': "XXXXXXX" }

def connect_wifi( wifi_cfg, max_retries=10 ):
    # use WiFi in station mode (not AP)
    wifi_sta = network.WLAN( network.STA_IF )
    # activate the WiFi interface (up)
    wifi_sta.active(True)
    # connect to the specified WiFi AP
    wifi_sta.connect( wifi_cfg['ssid'], wifi_cfg['pwd']  )
    retries = 0
    while not wifi_sta.isconnected():
        retries = retries + 1
        if retries >= max_retries:
            return None
        time.sleep_ms(500)
    return wifi_sta

def get_worldtime_data( url ):
    resp = requests.get( url )
    if resp.status_code==200:  # request ok
        try:
            data = json.loads(resp.text)
        except Exception as ex:
            print ('JSON data error...' )
            return
        print( 'Timezone:', data['timezone'] )
        print( 'Epoch time (Jan 1, 1970):', int(data['unixtime']) ) 
        print( 'UTC   datetime:', data['utc_datetime'] )
        print( 'Local datetime:', data['datetime'] )
        print( 'UTC offset   (hours):', data['utc_offset'] )
        print( 'UTC offset (seconds):', int(data['raw_offset']) )
    else:
        print ('HTTP request error...')

# try to connect the network
wifi = connect_wifi( WIFI_CFG )
if wifi is not None:
    get_worldtime_data( URL )
else:
    print('No WiFi connection')
```

ตัวอย่างเอาต์พุตที่ได้

`Timezone: Asia/Bangkok` \
`Epoch time (Jan 1, 1970): 1609939805` \
`UTC   datetime: 2021-01-06T13:30:05.138524+00:00` \
`Local datetime: 2021-01-06T20:30:05.138524+07:00` \
`UTC offset   (hours): +07:00` \
`UTC offset (seconds): 25200`&#x20;

## 7. HTTPS GET: Thailand's COVID-19 Status Update <a href="#id-16e2" id="id-16e2"></a>

ตัวอย่างถัดไป สาธิตการดึงข้อมูลเกี่ยวกับผู้ป่วย **COVID-19** สำหรับประเทศไทย ด้วยวิธี **HTTPS GET** จาก **API** ของเว็บ [covid19.th-stat.com](http://covid19.th-stat.com/) ข้อมูลที่ได้จะอยู่ในรูปของ **JSON String**

```python
import network
import utime as time
import urequests as requests

URL = "https://covid19.th-stat.com/api/open/today"

# Specify the SSID and password of your WiFi network
WIFI_CFG = { 'ssid': "XXXX", 'pwd': "XXXXXXX" }
WIFI_CFG = { 'ssid': "esl_ap", 'pwd': "cnch2687" }

COVID19_NAMES = [
    ('Confirmed',    u'ยอดผู้ป่วยสะสม'),
    ('Recovered',    u'ผู้ป่วยรักษาหายแล้ว'),
    ('Hospitalized', u'ผู้ป่วยรักษาตัวในโรงพยาบาล'),
    ('Deaths',       u'ผู้เสียชีวิตสะสม'),
    ('NewConfirmed', u'ผู้ป่วยตรวจพบเพิ่มวันนี้'),
    ('NewRecovered', u'ผู้ป่วยรักษาหาย'),
    ('NewDeaths',    u'ผู้เสียชีวิตวันนี้'),
    ('UpdateDate',   u'อัปเดตล่าสุด') ]

def get_covid19_data( url ):
    data = None
    try:
        resp = requests.get( url )
        data = resp.json()
        for name in COVID19_NAMES[:-1]:
            if name[0] in data:
                value = str(data[name[0]])
                print('{}:\t\t\t\t{}'.format(name[1],value) )
        name = COVID19_NAMES[-1]
        update = data[ name[0] ]
        print( '{}: {}'.format(name[1], update)  )
    except Exception as ex:
        print('error:', ex)
    return data

def connect_wifi( wifi_cfg ):
    wifi_sta = network.WLAN( network.STA_IF )
    wifi_sta.active(True)
    wifi_sta.connect( wifi_cfg['ssid'], wifi_cfg['pwd']  )
    while not wifi_sta.isconnected():
        time.sleep(1.0)
    print(wifi_sta.ifconfig())

connect_wifi( WIFI_CFG )
import gc
gc.collect()
get_covid19_data( URL )
```

ตัวอย่างข้อความเอาต์พุตที่ได้

`ยอดผู้ป่วยสะสม: 9331` \
`ผู้ป่วยรักษาหายแล้ว: 4418` \
`ผู้ป่วยรักษาตัวในโรงพยาบาล: 4847` \
`ผู้เสียชีวิตสะสม: 66` \
`ผู้ป่วยตรวจพบเพิ่มวันนี้: 365` \
`ผู้ป่วยรักษาหาย: 21` \
`ผู้เสียชีวิตวันนี้: 1` \
`อัปเดตล่าสุด: 06/01/2021 12:25`

## 8. Getting Air Quality & Weather Data from AirVisual

ตัวอย่างถัดไปแสดงการขอข้อมูลเกี่ยวกับสภาพอากาศและดัชนีชี้วัดคุณภาพอากาศจาก [**Airvisual Platform**](https://www.iqair.com/) ซึ่งใช้วิธีสื่อสารแบบ **HTTPS** ตามรูปแบบ [**AirVisual REST API**](https://api-docs.iqair.com/) ที่ทางบริษัทกำหนดไว้ และจะได้ข้อมูลกลับมาเป็น **JSON Data**&#x20;

การเรียกใช้ **REST API** ในกรณีนี้ จะต้องมี **API Key** โดยจะต้องสมัครสมาชิกเพื่อใช้งาน โดยเลือกแบบฟรี (**Community**) และระบุสถานที่หรือ Location (ในตัวอย่างนี้คือ เมืองนนทบุรี ประเทศไทย)

```python
import sys
import network
import ujson as json
import utime as time
import urequests as requests

JSON_CONFIG_FILE = 'config.json'
AIRVISUAL_URL    = 'https://api.airvisual.com/v2/city'

config = {}
try:
    with open( JSON_CONFIG_FILE ) as json_file:
        config = json.load(json_file)
except OSError as ex:
    print('Cannot open JSON file')
    sys.exit(-1)

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

sta_if.connect( config['ssid'], config['pwd'] )
while not sta_if.isconnected():
    time.sleep(1.0)
# show IP address assigned by DHCP server
#print( 'Connected:', sta_if.ifconfig()[0] )

# Fetch JSON data by using the AirVisual API
url_template = AIRVISUAL_URL + '?city={}&state={}&country={}&key={}'
url = url_template.format( 
    'Mueang%20Nonthaburi',
    'Nonthaburi',
    'Thailand',
    config['airvisual_api_key'] )

resp = None
try:
    resp = requests.get( url )
except OSError:
    print('Cannot fetch data')

if resp and resp.status_code == 200: # OK
    json_data = resp.json()
    if json_data['status'] == 'success':
        data = json_data['data']
        location = (data['state'],data['city'],data['country'])
        polution = data['current']['pollution']
        us_aqi = polution['aqius']
        ts = polution['ts'] # '2020-05-19T02:00:00.000Z'
        weather = data['current']['weather']
        tp = weather['tp'] # temperature (deg.C)
        hu = weather['hu'] # humidity (%)
        pr = weather['pr'] # pressure (mBar or hPa)
        print('Location: {}, {}, {}'.format(*location) )
        print( 'US AQI      : {}'.format(us_aqi) )
        print(('Temperature : {} deg.C\n' +
               'Humidity    : {} %RH\n' +
               'Pressure    : {} mBar').format(tp,hu,pr))
else:
    print (resp.reason)
```

ในการทำงานของโค้ดนี้ จะต้องมีไฟล์ `config.json` ซึ่งได้บันทึกเก็บไว้ใน **MicroPython Flash Storage** และมีข้อความแบบ **JSON** ตามลักษณะดังนี้

`{ "ssid": "XXXXXXX", "pwd": "XXXXXXXXX",` \
&#x20; `"airvisual_api_key": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }`

ตัวอย่างข้อความเอาต์พุต มีดังนี้

`Location: Nonthaburi, Mueang Nonthaburi, Thailand` \
`US AQI      : 126` \
`Temperature : 29 deg.C` \
`Humidity    : 55 %RH` \
`Pressure    : 1011 mBar`

## **9. Getting Weather Data from OpenWeatherMap** <a href="#id-0a64" id="id-0a64"></a>

[**OpenWeatherMap**](https://openweathermap.org/) (<https://openweathermap.org/>) มีบริการ **API** ให้ดึงข้อมูลด้วยโพรโทคอล **HTTP/HTTPS (**&#xE14;ูตัวอย่างการใช้ **API** และตัวอย่างข้อมูลในรูปแบบ **JSON** ได้จาก <https://openweathermap.org/current>)

ผู้ใช้จะต้องสมัครสมาชิกก่อนจึงจะสามารถใช้งานได้ฟรี (แต่มีข้อจำกัดและเงื่อนไขในการใช้งาน) เพื่อให้ได้ **APPID** ซึ่งเป็นข้อความแบบเลขฐานสิบหกจำนวน 32 หลัก

ในตัวอย่างนี้ เราจะดึงข้อมูลสภาพภูมิอากาศ และเลือกนำมาแสดงเฉพาะ อุณหภูมิ (**Temperature**) ความชื้นสัมพัทธ์ (**Relative Humidity**) และความกดอากาศ (**Pressure**) และเลือกเมืองเป็น จังหวัดนนทบุรี (**Nonthaburi, TH**) แล้วนำมาแสดงผลเป็นข้อความเอาต์พุตผ่านทาง **REPL**

```python
import sys
import network
import ujson as json
import utime as time
import urequests as requests

# URL for openweathermap
API_URL = 'https://api.openweathermap.org/data/2.5/weather'

JSON_CONFIG_FILE = 'config.json'

config = {}
try:
    with open( JSON_CONFIG_FILE ) as json_file:
        config = json.load(json_file)
except OSError as ex:
    print('Cannot open JSON file')
    sys.exit(-1)

sta_if = network.WLAN( network.STA_IF )
sta_if.active( True )
sta_if.connect( config['ssid'], config['pwd'] )
while not sta_if.isconnected():
    time.sleep(1.0)

#########################################################

def get_weather_data( APPID, city ):
    results = {}
    url = API_URL+'?units=metric&APPID='+APPID+'&q='+city
    try:
        data = requests.get(url).json()
    except OSError:
        print ('Cannot get data from OpenWeatherMap..')
        return results
    code = data.get('cod')
    #print( json.dumps(data) )
    if code == 200: # response OK
        city = data['name']
        results['p'] = data['main']['pressure'] # hPa
        results['t'] = data['main']['temp']     # deg.C
        results['h'] = data['main']['humidity'] # %
    elif code == 401:
        print('Error Code:', code)
        print( data['message'] )
    return results

city_name = 'Mueang Nonthaburi, TH'
weather_data = {
    't': (' - Temperature :','deg.C'),
    'h': (' - Humidity    :','%RH'),
    'p': (' - Pressure    :','hPa'), }

appid = config['openweather_appid']
results = get_weather_data( appid, city_name )
print( city_name ) # show city name
for key in weather_data:
    name, unit = weather_data[key]
    value = results[key]
    print('{} {} {}'.format(name,value,unit) )
```

ในการทำงานของโค้ดนี้ จะต้องมีไฟล์ `config.json` ซึ่งได้บันทึกเก็บไว้ใน **MicroPython Flash Storage** และมีข้อความแบบ **JSON** ตามลักษณะดังนี้

`{ "ssid": "XXXXXXX", "pwd": "XXXXXXXXX",` \
&#x20; `"openweather_appid": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }`

ตัวอย่างข้อความเอาต์พุต มีดังนี้

`Mueang Nonthaburi, TH`\
&#x20;`- Temperature : 29.53 deg.C`\
&#x20;`- Pressure    : 1011 hPa`\
&#x20;`- Humidity    : 54 %RH`

## 10. Getting Air Quality Data from Air4Thai

ตัวอย่างถัดไป สาธิตการดึงข้อมูลเกี่ยวกับคุณภาพอากาศในเขตกรุงเทพจากเว็บ [**Air4Tha**](http://air4thai.pcd.go.th)i ใช้ **HTTP GET** แล้วนำข้อมูลบางส่วนซึ่งเป็น **JSON Data** มาแสดงผลเป็นข้อความเอาต์พุตทาง **REPL**

ในตัวอย่างนี้ได้เลือกสถานีตรวจวัดที่มี **Station ID** ตรงกับ `bkp95t` ซึ่งเป็นเขตบางซื่อของกรุงเทพ ฯ

```python
import sys
import network
import ujson as json
import utime as time
import urequests as requests

STATION_ID = 'bkp95t'
API_URL = 'http://air4thai.pcd.go.th/services/getNewAQI_JSON.php'

JSON_CONFIG_FILE = 'config.json'

config = {}
try:
    with open( JSON_CONFIG_FILE ) as json_file:
        config = json.load(json_file)
except OSError as ex:
    print('Cannot open JSON file')
    sys.exit(-1)

sta_if = network.WLAN( network.STA_IF )
sta_if.active( True )
sta_if.connect( config['ssid'], config['pwd'] )
while not sta_if.isconnected():
    time.sleep(1.0)

#########################################################

def get_air4thai_data( url, station_id ):
    try:
        api_url = url + '?stationID=' + station_id
        json_data = requests.get(api_url).json()
    except OSError:
        print ('Cannot get data from air4thai...')
        return results
    #print( json.dumps(data) )
    station = json_data['areaEN']
    print( 'Station: {}'.format(station) )
    data = json_data['LastUpdate']
    print( 'Last update:', data['date'], data['time'] )
    print( 'PM25:', data['PM25']['value'], data['PM25']['unit'] )
    print( 'AQI:', data['AQI']['aqi'] )

get_air4thai_data( API_URL, STATION_ID ) 
```

ตัวอย่างข้อความเอาต์พุต มีดังนี้

`Station: Bang Sue, Bangkok` \
`Last update: 2021-01-06 00:00` \
`PM25: 41 µg/m³` \
`AQI: 63`

## 11. ส่งข้อความแจ้งเตือนผ่าน LINE Notify API

อีกตัวอย่างหนึ่งพบเห็นได้คือ การให้อุปกรณ์ประเภทไมโครคอนโทรลเลอร์สำหรับงาน **IoT** สามารถส่งข้อความแจ้งเตือน หรือ **Chat Message** ไปยังผู้ใช้ **LINE App** โดยใช้บริการที่เรียกว่า [**LINE Notify API**](https://notify-bot.line.me/doc/en/) ซึ่งมีการเริ่มใช้งานมาตั้งแต่ปีค.ศ. **2016**

ถ้าจะใช้งานได้นั้น ผู้ใช้จะต้องไปตั้งค่าการใช้งานผ่านหน้าเว็บที่ <https://notify-bot.line.me/en/> เพื่อให้ได้ **Access Token** มาใช้งาน

1. ทำขั้นตอน **Login** เข้าใช้งานด้วย **email address** และ **password** แล้วยืนยันผ่าน **LINE App** บน **Smartphone**
2. ไปที่เมนู [**MyPage**](https://notify-bot.line.me/my/)
3. กดปุ่ม **Generate Token** ในส่วนที่เรียกว่า "**Generate access token (For developers)"**
4. จากนั้นจะมี **Pop-up window** ให้กรอกข้อมูล โดยจะต้องระบุ **Token Name** (ชื่อที่จะปรากฎเมื่อส่งข้อมูลไปยัง **LINE**) และเลือกว่า จะส่งข้อความ **Chat** ไปยังกลุ่ม **LINE** ใด ซึ่งผู้ใช้ได้สร้างหรืออยู่ในกลุ่มดังกล่าว หรือจะส่งหาตัวเองก็ได้โดยให้เลือก **"1-on-1 chat with LINE notify"**  แล้วจึงกดปุ่ม **Generate Token**
5. เมื่อมีการจับคู่และสร้าง **Token** แล้ว ในหน้า **MyPage** ในส่วนที่เรียกว่า "**Connected services**" จะมีรายการการเชื่อมต่อ **From** (ชื่อ **Token Name** ตามที่ได้ตั้งไว้ ) **To** (ชื่อกลุ่มไลน์ตามที่ได้เลือก หรือ ชื่อบัญชีของผู้ใช้เอง)&#x20;

การส่งข้อความไปยัง **LINE Notify API** จะใช้วิธี **HTTPS POST Method** โดยระบุ **URL** เป็น <https://notify-api.line.me/api/notify> และในส่วน **HTTPS Header** จะมีต้องการระบุ `'Authorization'` ร่วมกับ `'Bearer '` ตามด้วย **Access Token**&#x20;

ตัวอย่างโค้ดสำหรับไมโครไพธอนที่ทดลองใช้งานกับ **ESP32** มีดังนี้ และเป็นการทดลองส่งข้อความ เช่น ระบุค่าอุณหภูมิและความชื้นในบ้าน (เป็นตัวอย่างสมมุติ)

```python
import utime as time
import ujson as json
import usocket as socket
import ussl as ussl
import network
import urllib.parse

JSON_CONFIG_FILE = 'config.json'

config = {}
try:
    with open( JSON_CONFIG_FILE ) as json_file:
        config = json.load(json_file)
except OSError as ex:
    print('Cannot open JSON file')
    sys.exit(-1)

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

sta_if.connect( config['ssid'], config['pwd'] )
while not sta_if.isconnected():
    print('waiting for Wi-Fi connection')
    time.sleep(1.0)

#########################################################
def https_post( url, headers={}, data='' ):
    proto, _, host, path = url.split("/", 3)
    assert( proto == 'https:' )
    port = 443
    
    if headers is None:
        headers = {}
    required_headers = {
        'Host'          : host,
        'User-Agent'    : 'MicroPython',
        'Cache-Control' : 'no-cache',
        'User-Agent'    : 'MicroPython',
        'Content-Type'  : 'application/x-www-form-urlencoded' }
    for key in required_headers.keys():
        if key not in headers:
            headers[key] = required_headers[key]

    # get address info of the host machine
    ai = socket.getaddrinfo( host, port, 0, socket.SOCK_STREAM )[0]
    # create a secure socket 
    s = socket.socket( ai[0], ai[1], ai[2] )
    s.connect(ai[-1])
    s.settimeout(2.0)
    s = ussl.wrap_socket(s, server_hostname=host)
    # write header lines
    s.write(b'POST /%s HTTP/1.1\r\n' % path)
    for k in headers:
        s.write(k)
        s.write(b': ')
        s.write(headers[k])
        s.write(b'\r\n' )
    s.write( b'Content-Length: %d\r\n' % (len(data)) )
    s.write( b'\r\n' )
    # write data
    s.write(data)
    
    # receive the response from the server
    status_code = 0
    eof_count = 0
    resp_text = ''
    while True:
        try:
            line = s.readline().decode().strip()
            if line == '':
                eof_count += 1
                if eof_count >= 2:
                    break
            if line.startswith( 'HTTP' ):
                status_code = line.split(' ')[1]
            elif line.startswith('{') and eof_count==1:
                resp_text += line
        except OSError:
            break
    s.close()
    return (status_code, resp_text)

#########################################################

token = config['line_notify_token']
msg = {'message': u'ในบ้าน อุณหภูมิ 27.5 °C ความชื้น 55.7%'}
headers = {'Authorization' : 'Bearer %s' % token}
data = '{}={}'.format( 'message', urllib.parse.quote(msg['message']) )
url = 'https://notify-api.line.me/api/notify'
status_code, resp_text = https_post( url, headers, data )
if status_code == '200':
    print( resp_text )
```

ข้อมูลที่ใช้สำหรับการตั้งค่า **Wi-Fi** และ **LINE Token Access** จะถูกเก็บอยู่ในไฟล์ `config.json` ใน **Flash File Storage** ของตัวอุปกรณ์ไมโครไพธอน ตัวอย่างเช่น

`{ "ssid": "my_wifi_ssid",` \
&#x20; `"pwd": "my_wifi_password",` \
&#x20; `"line_notify_token": "sSVjJ3qoX17pdw5UETGfi3EhLxaCzTxxxxxxxxxxxx" }`&#x20;

เมื่อส่งข้อความไปยัง **LINE Notify Service** จะได้ข้อความตอบกลับมาเป็น **JSON String** ถ้าทำงานได้ถูกต้องจะได้ข้อความ

`{"status":200,"message":"ok"}`&#x20;

![รูปภาพ: ตัวอย่างข้อความที่ได้รับบนสมาร์ทโฟน](/files/-MQeBTG5lxfiw8ACZTWr)

แต่ถ้าเกิดความผิดพลาด จะได้ข้อความ เช่น ในกรณีที่ไม่ได้ระบุ **Authorization** ในส่วน **Header** ของ **HTTPS POST**

`{"status":401,"message":"Missing authorization header"}`&#x20;

หรือถ้าระบุ **Access Token** ไม่ถูกต้อง ก็จะได้ข้อความ

&#x20;`{"status":401,"message":"Invalid access token"}`

**ข้อสังเกต:** เนื่องจากว่า มีการใช้ข้อความเป็นภาษาไทยด้วย ซึ่งบันทึกเป็น **UTF-8** และเมื่อจะส่งออกไปโดยใช้ **HTTPS POST** จึงต้องมีการแปลงข้อมูลก่อน โดยใช้คำสั่ง `urllib.parse.quote()`&#x20;

ดังนั้นจึงจะต้องติดตั้งไลบรารี `urllib.parse` ของ [mciropython-lib](https://github.com/micropython/micropython-lib) โดยสามารถทำได้ง่ายใน **MicroPython REPL** โดยใช้คำสั่ง `upip` ดังนี้ (และการทำงานของ **MicroPython** ใน **ESP32**  จะต้องเคยเชื่อมต่อกับ **Wi-Fi** ได้สำเร็จแล้ว)

&#x20;`>>> import upip` \
&#x20;`>>> upip.install('urllib.parse')`

และจะมีการดาวน์โหลดไฟล์ (**.tar.gz**) ที่เกี่ยวข้องจากเว็บ **micropython.org** แล้วเก็บลงใน `/lib`

**ปัญหาที่พบ:** แต่การใช้งานไลบรารี `urllib.parse` จะพบปัญหา วิธีแก้คือ ให้ลบไฟล์ `re.py` และ `ffilib.py` ออกไป จากนั้นกด **Ctrl+D** เพื่อเริ่มต้นการทำงานของ **MicroPython** ใหม่อีกครั้ง

![รูปภาพ: การติดตั้งไลบรารีผ่านทาง REPL ของ Thonny IDE](/files/-MQe5G8WVFd9wvgvparO)

เราสามารถลองใช้ไลบรารี [`urequests`](https://github.com/micropython/micropython-lib/blob/master/urequests/urequests.py) ซึ่งเป็นโมดูลแบบ **built-in** สำหรับไมโครไพธอน และใช้คำสั่ง `urequests.post()` สำหรับส่งข้อมูลแบบ **POST** ได้ แต่จากการที่ได้ลองใช้กับ **LINE Notify API** พบว่า มีความผิดพลาดเกิดขึ้นในการรับข้อมูลตอบกลับ (**Response**) แต่ก็ได้รับข้อความใน **LINE App**

```python
import network
import sys
import ujson as json
import urllib.parse
import urequests as requests

JSON_CONFIG_FILE = 'config.json'

config = {}
try:
    with open( JSON_CONFIG_FILE ) as json_file:
        config = json.load(json_file)
except OSError as ex:
    print('Cannot open JSON file')
    sys.exit(-1)

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

sta_if.connect( config['ssid'], config['pwd'] )
while not sta_if.isconnected():
    print('waiting for Wi-Fi connection')
    time.sleep(1.0)

#########################################################

token = config['line_notify_token']
url = 'https://notify-api.line.me/api/notify'
msg = {'message': u'ในบ้าน อุณหภูมิ 29.8 °C ความชื้น 69.0%' }
headers = {'Authorization' : 'Bearer %s' % token,
           'Content-Type'  : 'application/x-www-form-urlencoded' }
data = '{}={}'.format( 'message', urllib.parse.quote(msg['message']) )
try:
    resp = requests.post( url, headers=headers, data=data )
    print(resp.status_code, resp.text)
except Exception as ex:
    print(ex)
```

## 12. ตรวจสอบการเชื่อมต่อในเครือข่ายด้วย Ping

ตัวอย่างถัดไปสาธิตการตรวจสอบการเชื่อมต่อกับเครื่องคอมพิวเตอร์หรืออุปกรณ์ในเครือข่าย โดยใช้คำสั่ง **ping** ซึ่งเป็นการส่งแพคเกจตามรูปแบบ **ICMP (Internet Control Message Protocol)**

![รูปภาพ: โครงสร้าง IPv4 ICMP Packet (Source: Wikipedia)](/files/-MQelxeu5W5IOTeyuRRn)

โค้ดตัวอย่างต่อไปนี้ ดัดแปลงมาจากโค้ด [`uping.py`](https://gist.github.com/shawwwn/91cc8979e33e82af6d99ec34c38195fb) (**Micro-Ping for MicroPython**) ที่ได้มีการแชร์ไว้ใน [gist.github.com](https://gist.github.com/shawwwn/91cc8979e33e82af6d99ec34c38195fb)

```python
import network
import utime as time
import ujson as json

########################################################

def checksum(data): # CRC16 
    if len(data) % 2 == 1:
        data += b'\x00'
    chksum = 0
    for pos in range(0, len(data), 2):
        hi_byte = data[pos]
        lo_byte = data[pos + 1]
        chksum += (hi_byte << 8) + lo_byte
    while chksum >= 0x10000:
        chksum = (chksum & 0xffff) + (chksum >> 16)
    chksum = ~chksum & 0xffff
    return chksum

def ping(host, count=4, timeout=5000,
         interval=10, quiet=False, size=64):
    
    import utime 
    import uselect
    import uctypes 
    import usocket
    import ustruct
    import urandom
    
    # prepare packet
    assert( (size >= 16),   "packet size too small")
    assert( (size <= 1000), "packet size too big")
    pkt = b'Q'*(size)
    pkt_desc = {
        "type"     : uctypes.UINT8  | 0,
        "code"     : uctypes.UINT8  | 1,
        "checksum" : uctypes.UINT16 | 2,
        "id"       : uctypes.UINT16 | 4,
        "seq"      : uctypes.INT16  | 6,
        "timestamp": uctypes.UINT64 | 8,
    } # packet header descriptor
    h = uctypes.struct( uctypes.addressof(pkt),
                        pkt_desc, uctypes.BIG_ENDIAN )
    h.type = 8 # ICMP_ECHO_REQUEST
    h.code = 0
    h.checksum = 0
    h.id = urandom.getrandbits(16)
    h.seq = 1
    # init socket
    sock = usocket.socket(usocket.AF_INET, usocket.SOCK_RAW, 1)
    sock.setblocking(0)
    sock.settimeout(timeout/1000.0)
    addr = usocket.getaddrinfo(host, 1)[0][-1][0] # ip address
    sock.connect((addr, 1))
    if not quiet:
        print("PING %s (%s): %u data bytes" % (host, addr, len(pkt)))
    seqs = list(range(1, count+1))
    c = 1
    t = 0
    n_trans = 0
    n_recv = 0
    finish = False
    while t < timeout:
        if t==interval and c<=count:
            # send packet
            h.checksum = 0
            h.seq = c
            h.timestamp = utime.ticks_us()
            h.checksum  = checksum(pkt)
            if sock.send(pkt) == size:
                n_trans += 1
                t = 0 # reset timeout
            else:
                seqs.remove(c)
            c += 1

        # recv packet
        while True:
            socks,_,_ = uselect.select([sock],[],[],0)
            if socks:
                # receive incoming packet as response
                resp = socks[0].recv(1024)
                # create a memoryview object
                resp_mv = memoryview(resp)
                h2 = uctypes.struct(uctypes.addressof(resp_mv[20:]),
                                    pkt_desc, uctypes.BIG_ENDIAN)
                # validate checksum
                calc_chksum = checksum(bytes(resp_mv[24:]))
                chksum = ustruct.unpack("!H", resp_mv[22:24])[0]
                chksum_ok = chksum == calc_chksum
                assert chksum_ok, 'Checksum failed...'
                
                seq = h2.seq
                if h2.type==0 and h2.id==h.id and (seq in seqs): # 0: ICMP_ECHO_REPLY
                    t_elasped = (utime.ticks_us()-h2.timestamp) / 1000
                    ttl = ustruct.unpack('!B', resp_mv[8:9])[0] # time-to-live
                    n_recv += 1
                    if not quiet:
                        args = (len(resp), addr, seq, ttl, t_elasped)
                        print("{} bytes from {}: icmp_seq={}, ttl={}, time={:.1f} ms".format(*args))
                    seqs.remove(seq)
                    if len(seqs) == 0:
                        finish = True
                        break
            else:
                break
        if finish:
            break
        utime.sleep_ms(1)
        t += 1
    # close
    sock.close()
    ret = (n_trans, n_recv)
    if not quiet:
        args = (n_trans, n_recv)
        print("{} packets transmitted, {} packets received".format(*args))
    return (n_trans, n_recv)

########################################################
JSON_CONFIG_FILE = 'config.json'

config = {}
try:
    with open( JSON_CONFIG_FILE ) as json_file:
        config = json.load(json_file)
except OSError as ex:
    print('Cannot open JSON file')
    sys.exit(-1)

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

sta_if.connect( config['ssid'], config['pwd'] )
while not sta_if.isconnected():
    print('waiting for Wi-Fi connection')
    time.sleep(1.0)

ping('8.8.8.8',count=5, timeout=5000, interval=200,
     quiet=False, size=44)
########################################################
```

ตัวอย่างข้อความเอาต์พุต

`PING 8.8.8.8 (8.8.8.8): 44 data bytes` \
`64 bytes from 8.8.8.8: icmp_seq=1, ttl=112, time=139.7 ms` \
`64 bytes from 8.8.8.8: icmp_seq=2, ttl=112, time=64.4 ms` \
`64 bytes from 8.8.8.8: icmp_seq=3, ttl=112, time=150.3 ms` \
`64 bytes from 8.8.8.8: icmp_seq=4, ttl=112, time=61.7 ms` \
`64 bytes from 8.8.8.8: icmp_seq=5, ttl=112, time=158.2 ms` \
`5 packets transmitted, 5 packets received`&#x20;

## กล่าวสรุป

เราได้เห็นตัวอย่างการเขียนโค้ดภาษาไมโครไพธอนสำหรับ **ESP32** เพื่อเชื่อมต่อกับระบบเครือข่ายไร้สายผ่าน **Wi-Fi** และสามารถใช้โพรโตคอลได้ อย่างเช่น **HTTP/HTTPS**&#x20;

{% hint style="info" %}
**เผยแพร่ภายใต้ลิขสิทธิ์**\
**Attribution-ShareAlike 4.0 International (**[**CC BY-SA 4.0**](https://creativecommons.org/licenses/by-sa/4.0/)**)**
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://think-embedded.gitbook.io/micropython/micropython-for-esp32/esp32-networking.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
