# Yeast Cloud Kit — เริ่มจากเครื่องที่ต่อสำเร็จแล้ว

เว็บ: https://jovey.co/yeast-lab/

ชุดนี้เตรียมซอฟต์แวร์ครบสามส่วน: Arduino ส่ง HTTPS, Supabase รับและเก็บข้อมูล,
หน้าเว็บดูการเปลี่ยนแปลงและเก็บ baseline ยังต้องตั้งค่า Supabase ของคุณและอัปโหลดลงบอร์ดจริง
ไม่ต้องเปลี่ยนสายเดิม: DS18B20 G4, MQ3 G34, MQ135 G35 ผ่านตัวแบ่ง 10K/10K แยกแต่ละตัว

## 1. สร้างฐานข้อมูล (ทำครั้งเดียว)
1. เปิด https://supabase.com/dashboard สร้างโปรเจกต์แผน Free ในบัญชีของคุณ
2. เปิด SQL Editor > New query วางเนื้อหา schema.sql ทั้งไฟล์ กด Run (สำหรับโปรเจกต์ใหม่)
3. ไป Authentication > Users > Add user สร้างผู้ใช้ email/password ของคุณสำหรับเข้าเว็บ เปิดยืนยันอีเมลให้ผู้ใช้หากเมนูถาม ผู้ใช้นี้แยกจากบัญชีเข้า Supabase Dashboard
4. คัดลอก User ID จากหน้าผู้ใช้นั้น
5. เปิด register_device.sql แทน OWNER_UUID ด้วย User ID แล้วรัน SQL นี้หนึ่งครั้ง
6. เก็บ device_id และ device_token จากผลลัพธ์ไว้เป็นส่วนตัว token นี้เขียนข้อมูลได้เฉพาะบอร์ดที่ลงทะเบียน และอ่านข้อมูลไม่ได้
7. จาก Connect / Project settings คัดลอก Project URL และ Publishable key (หรือ legacy anon key) ไม่ใช้ secret/service_role key
8. เปิดเว็บด้านบน กรอก URL, public key, email/password ที่สร้างในข้อ 3 จะเห็นอุปกรณ์ชื่อ Yeast bench 01

ห้ามเปิดตารางให้อ่าน/เขียนสาธารณะ schema.sql ตั้ง RLS ให้ผู้ใช้เห็นเฉพาะอุปกรณ์ตนเองแล้ว
หาก token หลุด ให้สร้าง token ใหม่และอัปเดต token_hash ใน yeast_private.tokens จาก SQL Editor แล้วเปลี่ยนบน ESP32

## 2. เตรียม firmware
1. สำรอง sketch เดิมที่ทำงานแล้วไว้ก่อน
2. เปิดโฟลเดอร์ Yeast_Cloud แล้วดับเบิลคลิก Yeast_Cloud.ino ใน Arduino IDE
3. ตรวจว่ามีแท็บ secrets.h และ CloudSender.h ด้วย ต้องอยู่โฟลเดอร์เดียวกันทั้งหมด
4. ใช้บอร์ด ESP32 Dev Module และไลบรารี OneWire / DallasTemperature ตามเดิม
5. ใน secrets.h ใส่ WIFI_SSID / WIFI_PASSWORD ของเครือข่าย 2.4 GHz ที่ออกเน็ตได้
6. ใส่ SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, DEVICE_ID, DEVICE_TOKEN ตามข้อ 1
7. เตรียม TLS root certificate ตามข้อ 3 ด้านล่าง
8. เปลี่ยน CLOUD_ENABLED จาก false เป็น true แล้ว Upload เปิด Serial Monitor 115200

ไม่มีการใส่รหัสจริงในไฟล์ตัวอย่าง และไม่ต้องส่งรหัส Wi-Fi หรือ token ในแชต
อย่าอัปโหลด secrets.h ที่ใส่ข้อมูลจริงแล้วไป GitHub/ที่สาธารณะ

## 3. ใบรับรอง HTTPS
บอร์ดตรวจใบรับรอง server จริง ไม่มี setInsecure() และไม่ยอมรับหน้า Login Wi-Fi เป็นการส่งสำเร็จ
ใช้เครื่องคอมที่ออกอินเทอร์เน็ตได้และมี Python 3 กับ openssl:

```sh
cd /path/to/Yeast_Cloud_Kit
python3 prepare_tls.py https://YOUR_PROJECT.supabase.co
```

โปรแกรมตรวจ chain และ hostname กับ CA ที่คอมไว้ใจ แล้วเลือก root CA ที่ตรวจผ่านใส่ใน secrets.h ให้อัตโนมัติ
หาก Python แจ้งไม่มี CA store ให้ติดตั้งใบรับรองของ Python จากตัวติดตั้งที่ใช้อยู่ก่อน
หากหา openssl ไม่พบ ให้ติดตั้ง openssl จากผู้จัดจำหน่ายที่เชื่อถือได้
หากเครือข่ายองค์กรใช้ TLS inspection ให้ใช้เครือข่ายปกติในการเตรียมและทดสอบ; ไม่ปิดการตรวจ TLS
ใบรับรองอาจต้องอัปเดตหากผู้ให้บริการเปลี่ยน root CA ในอนาคต

## 4. ตรวจว่าข้อมูลเดินทางครบ
- Serial ยังแสดง ms,tempC,mq3_raw,mq135_raw ทุกประมาณ 1 วินาที
- # OPEN http://... คือเว็บในวง LAN เดิม ยังเปิดได้เหมือนเดิม
- # CLOUD_ACK points=... คือเซิร์ฟเวอร์ตอบรับจริงหลังส่งทุกประมาณ 10 วินาที
- บนเว็บคลาวด์ จุดและเวลาต้องเดินต่อ และ Supabase ตาราง yeast_samples ต้องมีแถวเพิ่ม
- ปิดหน้าเว็บ 1 นาทีแล้วเปิดใหม่: ประวัติที่ส่งสำเร็จยังอยู่
- ตัด Wi-Fi ชั่วครู่แล้วต่อใหม่: จุดที่รอส่งจะตามมา โดย unique(device_id,boot_id,seq) กันจุดซ้ำ

CLOUD_RETRY http=-1: ตรวจเน็ต, NTP, certificate, URL และไฟเลี้ยง
HTTP 401/403: ตรวจ key/token/สิทธิ์; HTTP 400/404: ตรวจ schema, URL และค่าที่ตั้ง
หากเห็น CLOUD_DISABLED: ยังไม่ได้เปิด CLOUD_ENABLED
หากมี Wi-Fi แต่ไม่มี ACK และไม่มี retry อาจกำลังรอเวลาจาก NTP (UDP 123 ถูกบล็อก)
Wi-Fi หน้า Login ยังไม่รองรับการล็อกอินอัตโนมัติ การมี IP ไม่ได้แปลว่าออกอินเทอร์เน็ตแล้ว

## 5. เก็บ baseline
1. เตรียมเซนเซอร์และสภาพอ้างอิงให้คงที่ก่อน การกด baseline ไม่ได้แทนการอุ่นเซนเซอร์หรือสอบเทียบก๊าซ
2. รอให้เว็บมีข้อมูลสดและเวลาวัดซิงก์แล้ว
3. ตั้งชื่อรอบ เช่น “อากาศก่อนเริ่ม รอบ 01” ติ๊กยืนยันแล้วกด เริ่มเก็บ 5 นาที
4. เก็บทุก 10 วินาที จะได้ประมาณ 30 จุด ค่าดิบทั้งหมดถูกเก็บ ไม่ลบข้อมูลเดิม
5. ระบบใช้ค่ามัธยฐานแต่ละช่อง แสดงส่วนต่าง °C และ raw counts; % สำหรับ MQ แสดงเฉพาะ baseline >=20 counts
6. ต้องจบเวลา มีอย่างน้อย 20 จุดไม่ชนขอบ ADC และช่วง first→last อย่างน้อย 240 วินาที จึงเริ่มแสดงส่วนต่าง
7. จำนวนจุดครบไม่ได้ยืนยันความนิ่ง ตรวจ min/max และกราฟเอง ถ้าสภาพเปลี่ยนให้สร้าง baseline ใหม่
8. เลือก baseline เก่ากลับมาดูได้ กด CSV ของ baseline นี้เพื่อเก็บข้อมูลช่วงนั้นรวมจุดผิดปกติไว้ตรวจ

จุดไม่มีเวลาจากบอร์ดใช้เวลารับเพื่อแสดงผล แต่ไม่นำเข้า baseline ข้อมูลส่งล่าช้าซึ่งมีเวลาวัดอยู่ในช่วงอ้างอิงอาจทำให้สถิติ baseline อัปเดตหลังครบเวลา ดาวน์โหลด CSV เก็บ snapshot เมื่อต้องใช้วิเคราะห์

## ขอบเขตสำคัญ
- Local: อ่าน ~1 วินาที, RAM ล่าสุด 3600 จุด เว็บเดิมและปุ่ม event เดิมยังอยู่
- Cloud: ส่ง ~10 วินาที, เว็บตรวจใหม่ทุก 2 วินาที; เป็น near realtime ไม่รับประกัน latency
- ส่งซ้ำเมื่อเน็ตหลุดแบบเพิ่มระยะรอสูงสุด 60 วินาที อยู่คนละ task กับการอ่านเซนเซอร์
- คิว RAM 180 จุด + pending สูงสุด 20 จุด ประมาณ 30 นาทีขึ้นไปที่รอบ 10 วินาที คิวเต็มจะทิ้งจุดใหม่และนับ dropped_total
- คิวที่ยังไม่ส่งหายเมื่อไฟดับ/รีบูต ไม่ใช่ durable offline logger ยังไม่มี SD/flash spool
- ข้อมูลที่ server รับแล้วคงอยู่จนลบ/โควตาเต็ม/นโยบายบริการ ต้องสำรอง CSV เองและติดตามโควตา Free
- กราฟและปุ่ม CSV จุดที่โหลดแล้วแสดง/ส่งออกสูงสุด 600 จุด ไม่ใช่ทั้งฐานข้อมูล ใช้ Supabase export สำหรับประวัติทั้งหมด
- event จากเว็บ LAN เดิมยังเก็บเฉพาะ RAM ไม่ได้อัปโหลดในรุ่นนี้
- DS18B20 เป็น °C แต่ยังไม่ได้เทียบเครื่องอ้างอิง; MQ3/MQ135 เป็น raw ADC ไม่ใช่ ppm/CO₂/เปอร์เซ็นต์เอทานอล
- สอง MQ + Wi-Fi ใช้ไฟมากขึ้น USB/V5 ของบอร์ดจริงยังไม่ได้ทดสอบกำลังจ่าย หากรีเซ็ตให้ตรวจไฟเลี้ยง
- หน้าเว็บอยู่บน Jovey / GitHub Pages; Supabase ใช้รับและเก็บข้อมูลโดยตรง ข้อมูลจริงต้องล็อกอินและถูกจำกัดสิทธิ์ด้วย RLS

## แหล่งข้อมูล
https://supabase.com/docs/guides/database/functions
https://supabase.com/docs/guides/database/postgres/row-level-security
https://docs.espressif.com/projects/arduino-esp32/en/latest/api/adc.html
https://www.winsen-sensor.com/manual/mq135.html
