บทเรียนไทเทรตกรด-เบส (รุ่นลีน / MicroPad) | Acid-Base Titration Lesson (Lean / MicroPad)
วิชา: Integrated Chemistry Laboratory I (2302311)
คู่มือนี้สำหรับ วันทดลอง ดูทฤษฎีและขั้นตอนเต็มได้ที่
LAB_DIRECTION.mdและดูภาพรวมเทคนิคได้ที่README.md
ไดรเวอร์ดิบ (raw ADC/OneWire/PWM) อยู่ใน เฟิร์มแวร์ MicroPad
การสอบเทียบ (calibration) เป็นงานของนิสิต: ทำใน Week_2 แล้ว Week_3 อ่านมาใช้
บทเรียน /workspace มีแค่ 3 ไฟล์ และเรียก helper: import scilabpro as slp
แท็บเล็ต (แอป MicroPad) จับคู่ผ่าน BLE และเป็น "จอแสดงผล"
นิสิตสอบเทียบ "ของบอร์ดตัวเอง" ใน Week_2 ผลจะถูกบันทึกลงไฟล์ถาวรที่ Week_3 อ่านไปใช้:
รันใน Week_2 (run first) ได้ไฟล์ (produces) Week_2/01_pH_Sensor/02_calibration_3point.py/workspace/data/ph_calibration.txtWeek_2/02_Pump_Control/01_flow_rate_calibration.py/workspace/data/flow_calibration.txtถ้าไฟล์ใดหาย Week_3 จะ หยุดทันทีและไม่เดินปั๊ม พร้อมแจ้ง
ph_calibration_missing/flow_calibration_missingให้กลับไปสอบเทียบ Week_2 ที่หายก่อน
| ไฟล์ | บทบาท |
|---|---|
01_titration_auto.py |
จุดเริ่ม — โหลดผลสอบเทียบ → ลำดับการไทเทรต (runner เรียก /workspace/01_titration_auto.py) |
titration.py |
เคมี + นำผลสอบเทียบมาใช้ (pH = slope·mV+b) — หาจุดสมมูล + คำนวณความเข้มข้น |
experiment.py |
ค่าคงที่การทดลอง + เส้นทางไฟล์สอบเทียบ (ปรับ dose, ความเข้มข้น, เวลาหน่วง) |
รัน Week_2 สองสคริปต์นี้บนบอร์ดตัวเอง (run on THIS board) เพื่อสร้างไฟล์สอบเทียบ:
1) Week_2/01_pH_Sensor/02_calibration_3point.py
→ สอบเทียบ pH 3 จุด (pH 4/7/10) → /workspace/data/ph_calibration.txt
2) Week_2/02_Pump_Control/01_flow_rate_calibration.py
→ สอบเทียบอัตราการไหลปั๊ม → /workspace/data/flow_calibration.txt
ค่าสอบเทียบเป็น "ของบอร์ดตัวเอง" (per-board) ถ้ายังไม่มีไฟล์ Week_3 จะหยุดและบอกให้ทำขั้นตอนนี้ก่อน
แอป MicroPad → GitHub repo browser
→ TitraLab/MicroPython/Week_3/
→ import: 01_titration_auto.py , titration.py , experiment.py (ไฟล์ .py แบน)
→ ปลายทาง: /workspace/ บนบอร์ด
1. เปิดแอป MicroPad บนแท็บเล็ต → สแกน BLE → จับคู่บอร์ด
2. เปิด /workspace/01_titration_auto.py → กด Run
3. (ถ้า WAIT_FOR_LOCAL_START=True) กด BUTTON_1 บนบอร์ดเพื่อเริ่ม หรือรอ timeout
เริ่มต้น: โหลดผลสอบเทียบของบอร์ด → แอปแจ้ง event calibration_loaded
(slope/intercept ของ pH + flow_rate + เวลาเปิดปั๊มต่อ step ที่คำนวณได้)
ระบบทำซ้ำอัตโนมัติ: หยด 0.2 mL → รอ pH เสถียร → อ่าน pH/temp → สตรีมขึ้นแอป
• เวลาเปิดปั๊มต่อ step = 0.2 mL / flow_rate(ที่สอบเทียบ) → ปริมาตรที่จ่ายตรงจริง
• pH คำนวณจาก ADC ดิบด้วยสมการของนิสิต: pH = slope·mV + intercept
• volume_ml (mL) , pH , temp_c (°C) เลื่อนขึ้นแบบสด
• buzzer ดังเตือนเมื่อใกล้จุดสมมูล (ที่ ALERT_VOLUME_ML)
• กด Stop ในแอปเพื่อหยุดได้ตลอดเวลา (ปั๊มปิดทันที)
เมื่อเสร็จ แอปจะแสดง event: titration_complete
• equivalence_volume_ml (ปริมาตรที่จุดสมมูล)
• equivalence_ph_estimate (pH ที่จุดสมมูล — ค่าประมาณ)
• unknown_concentration_m (ความเข้มข้นที่คำนวณได้ mol/L)
ตัวอย่างผลที่ตรวจสอบแล้ว (HCl 0.1 M 5 mL + NaOH 0.1 M): V_eq ≈ 5.0 mL · pH ≈ 7.0 · C(HCl) ≈ 0.1 mol/L
| สิ่ง | ขา/Endpoint | หน้าที่ในบทเรียน |
|---|---|---|
| BUTTON_1 | GPIO34 (input-only) | กดเพื่อเริ่ม local (เมื่อ WAIT_FOR_LOCAL_START=True) |
| GREEN LED | GPIO4 (output) | ติด = กำลังทำงาน |
| BUZZER | GPIO26 | เสียงเตือนใกล้จุดสมมูล + เสียงเสร็จสิ้น |
| pH probe | GPIO32 (ADC1) | อ่าน ADC ดิบ (slp.read_analog('PH')) → แปลงเป็น pH ด้วยสอบเทียบของนิสิต |
| DS18B20 temp | GPIO16 (OneWire) | อ่านอุณหภูมิ |
| Pump | CONTROL_1 |
ปั๊มจ่ายไทแทรนต์ (เวลาเปิด = ปริมาตร/flow_rate · guarded ด้วย max_on_ms) |
การควบคุมหลักอยู่ที่แอป (Stop/Run) ไม่ใช่ปุ่มบนบอร์ด BUTTON_1 มีไว้เพื่อ "เริ่ม local" เท่านั้น เลขขาส่วนใหญ่เป็นของ routing profile ในเฟิร์มแวร์ บทเรียนใช้ ชื่อ endpoint (
CONTROL_1,PH) ส่วนเลขขาในexperiment.py(32 เป็น fallback ของ pH, 16) มีเพราะ helperslp.ds18b20(num)/slp.pin(num)รับเลขขาตรง ๆ
นี่คือตารางเต็มที่
LAB_DIRECTION.mdอ้างถึง (This is the full tableLAB_DIRECTION.mdpoints to).Routing profile เริ่มต้น:
titralab_v1_default(ณ วันที่ 2026-06-25) แหล่งข้อมูลจริง (SOURCE OF TRUTH):firmware/routing_profiles/titralab_v1_default.tomlตารางนี้เป็น สำเนาสะท้อน (mirror) ของ profile นั้น — ถ้า profile เปลี่ยน ต้อง ซิงก์ตารางนี้ใหม่ (บอร์ดเป็นการออกแบบตายตัวของผู้ใช้ จึงแทบไม่เปลี่ยน / The board is the user's fixed design, so it rarely changes.)
TitraLab Ver. 1.0 เป็นบอร์ด ESP32 แบบ patch-panel/จัมเปอร์ มี header สองชุด: GPIO header กับ DEVICES header นิสิตใช้สายจัมเปอร์ต่อระหว่างสอง header ให้ตรงกับ profile (TitraLab Ver. 1.0 is a jumper-configurable ESP32 board; students patch the GPIO header to the DEVICES header to match the profile.)
ต่อด้วยสายจัมเปอร์ระหว่าง GPIO header ↔ DEVICES header ให้ตรงตามนี้ (Patch these with jumper wires):
| Endpoint | GPIO | Device / อุปกรณ์ | Notes / หมายเหตุ |
|---|---|---|---|
RED |
2 | RED LED | output |
GREEN |
4 | GREEN LED | output · ใช้ในบทเรียน = แสดงสถานะ "กำลังทำงาน" |
BUTTON_1 |
34 | ปุ่มกด 1 / Button 1 | input-only · ใช้ในบทเรียน = เริ่ม local + safe-mode/pairing |
BUTTON_2 |
35 | ปุ่มกด 2 / Button 2 | input-only |
BUTTON_3 |
39 | ปุ่มกด 3 / Button 3 | input-only · safe-mode/cancel |
DS18B20 |
16 | เซ็นเซอร์อุณหภูมิ / temperature | 1-Wire · ต้องมี pull-up 4.7 kΩ · ใช้ในบทเรียน = อ่าน temp |
RELAY |
17 | รีเลย์ / relay | actuator · guard max-on 5000 ms |
CONTROL_1 |
21 | ปั๊ม / pump | actuator · guard max-on 5000 ms · ใช้ในบทเรียน = ปั๊มจ่ายไทแทรนต์ |
CONTROL_2 |
(verify) | — | UNMAPPED — ห้ามใช้ในบทเรียน (do NOT use) |
BUZZER |
26 | บัซเซอร์ / buzzer | actuator · guard max-on 1500 ms · ใช้ในบทเรียน = เสียงเตือน/เสร็จสิ้น |
PH_PROBE |
32 | หัววัด pH / pH probe | ADC1, wireless-safe (ไม่ใช่ GPIO25/ADC2) · ใช้ในบทเรียน = อ่าน pH |
POT_1 |
32 | โพเทนชิออมิเตอร์ 1 / pot 1 | ใช้สาย ADC1 ร่วมกับ PH_PROBE — ใช้ไม่ได้เมื่อ pH อยู่ที่ GPIO32 |
POT_2 |
33 | โพเทนชิออมิเตอร์ 2 / pot 2 | ADC1 |
บทเรียนรุ่นลีนนี้ (
01_titration_auto.py/titration.py/experiment.py) ใช้เพียง 6 endpoint:PH_PROBE,DS18B20,CONTROL_1,GREEN,BUTTON_1,BUZZER(This lean lesson uses only these 6 endpoints.)เพดานความปลอดภัยของ actuator (hardware-guard max-on): เฟิร์มแวร์ตัดเอาต์พุตเมื่อถึงเวลานี้ ไม่ว่าสคริปต์จะสั่งอะไร —
RELAY= 5000 ms,CONTROL_1= 5000 ms,BUZZER= 1500 ms (Firmware cuts the output at this time regardless of the script.)
ห้ามต่อสายจัมเปอร์ใหม่ / ห้ามกำหนดขาใหม่ — เป็นวงจรตายตัวบน PCB และเฟิร์มแวร์เป็นเจ้าของ (Never re-patch or reassign — these are hardwired on the PCB and firmware-owned.)
| Bus | ขา (Pins) | หมายเหตุ / Notes |
|---|---|---|
| ILI9341 TFT (firmware-owned) | SCK=14, MOSI=13, DC=27, CS=15 | MISO = unused/verify (GPIO0) · เฟิร์มแวร์วาดหน้า splash เอง |
| MicroSD (SPI) | SCK=18, MOSI=23, MISO=19, CS=5 | mount ที่ /sd |
- ขา input-only (เป็น output ไม่ได้ / cannot drive outputs): GPIO 34, 35, 36, 39 — เหมาะกับปุ่มกด (buttons) เท่านั้น
- ห้ามใช้เป็น student IO (forbidden / do-not-use): GPIO 0, 5, 12, 15 (strapping / TFT / SD)
- ADC2 (GPIO 25/26/27) ชนกับ Wi-Fi —
PH_PROBEจึงอยู่บน ADC1 (GPIO32) เสมอ (ห้ามย้าย pH ไป GPIO25) - Safe-mode bootstrap: กดค้าง
BUTTON_1+BUTTON_3(GPIO34 + GPIO39) ~1.5 วินาที ตอนบูต - Pairing (จับคู่): กดค้าง
BUTTON_1~3 วินาที - Cancel (ยกเลิก): กดค้าง
BUTTON_3~3 วินาที
ก่อนรันบทเรียน ให้ patch บอร์ดให้ตรงกับ titralab_v1_default (Before running the lesson, patch the board to match titralab_v1_default):
- ดูตารางที่ 1 แล้วต่อสายจัมเปอร์จากขา GPIO ฝั่ง GPIO header ไปยังอุปกรณ์ฝั่ง DEVICES header ทีละ endpoint (เช่น GPIO32 →
PH_PROBE, GPIO16 →DS18B20, GPIO21 →CONTROL_1, GPIO4 →GREEN, GPIO34 →BUTTON_1, GPIO26 →BUZZER). - สำหรับ
DS18B20(GPIO16) ต้องมีตัวต้านทาน pull-up 4.7 kΩ บนสายข้อมูล 1-Wire (ปกติบอร์ดเตรียมไว้แล้ว — ตรวจให้แน่ใจ). - อย่าแตะ ตารางที่ 2 (TFT / MicroSD) — เป็นวงจรตายตัว.
- บทเรียน อ้างถึงอุปกรณ์ด้วยชื่อ endpoint (เช่น
slp.set_actuator('CONTROL_1', ...)) แล้ว เฟิร์มแวร์แปลงชื่อ → หมายเลขขา GPIO ให้เอง ดังนั้นถ้าวันใดมีการเปลี่ยนการต่อสาย เพียงอัปเดต routing profile ในเฟิร์มแวร์ก็พอ ไม่ต้องแก้โค้ดบทเรียน (The lesson refers to devices by endpoint NAME; the firmware maps name → GPIO. Re-wiring means updating the profile, not the lesson code.) - ส่วนเลขขาที่โผล่ใน
experiment.py/01_titration_auto.py(32, 16, 4, 34, 26) มีไว้เพราะ helper บางตัว (slp.ds18b20(num),slp.pin(num),slp.buzzer(num)) รับ เลขขาตรง ๆ — เลขเหล่านี้ตรงกับtitralab_v1_defaultส่วน pH อ่านด้วยชื่อ endpointslp.read_analog('PH')(มีslp.pin(32, input=True)เป็น fallback) (These pin numbers in the lesson matchtitralab_v1_default.)
| ค่าคงที่ | ค่าเริ่มต้น | ปรับเมื่อไหร่ |
|---|---|---|
DOSE_VOLUME_ML |
0.2 mL | ต้องการกราฟละเอียดขึ้น → ลดค่า |
SAMPLE_VOLUME_ML |
5.0 mL | ตามปริมาตร analyte จริง |
MAX_VOLUME_ML |
10.0 mL | ถ้าจุดสมมูลเกินช่วง → เพิ่มค่า |
TITRANT_CONCENTRATION_M |
0.1 mol/L | ตามความเข้มข้น titrant จริง |
STOICHIOMETRIC_RATIO |
1.0 | กรด/เบสไม่ใช่ 1:1 (เช่น H2SO4 → 0.5) |
SETTLE_MS |
10000 ms | ทดสอบเร็วใช้ 2000; จริงใช้ 10000 |
ALERT_VOLUME_ML |
4.80 mL | ปรับตามจุดสมมูลที่คาด |
ค่าสอบเทียบไม่ได้ปรับที่นี่ (calibration is NOT tuned here):
PH_CAL_PATH,FLOW_CAL_PATH,RAW_TO_MVในexperiment.pyชี้/แปลงผลสอบเทียบที่นิสิตทำใน Week_2 ปกติไม่ต้องแก้ ถ้าต้องการ เปลี่ยน slope/intercept หรือ flow rate ให้ สอบเทียบใหม่ใน Week_2 แล้ว Week_3 จะอ่านค่าใหม่อัตโนมัติ
วิธีตั้ง alert_volume: ALERT_VOLUME_ML = (จุดสมมูลที่คาด) − (1–2 × DOSE_VOLUME_ML)
ตัวอย่าง HCl 0.1 M 5 mL + NaOH 0.1 M → จุดสมมูล ≈ 5.0 → 5.0 − 0.2 = 4.80 mL
| เสียง | ความหมาย |
|---|---|
| บี๊บสั้นเสียงสูง (2000 Hz) | ใกล้จุดสมมูล (ถึง ALERT_VOLUME_ML) — เตรียมสังเกต |
| สองโน้ต (1000 → 1500 Hz) | ไทเทรชันเสร็จสิ้น |
| ปัญหา | สาเหตุ | วิธีแก้ |
|---|---|---|
ph_calibration_missing (หยุดทันที) |
ยังไม่ได้สอบเทียบ pH ใน Week_2 / ไฟล์หาย | รัน Week_2/01_pH_Sensor/02_calibration_3point.py → รัน Week_3 ใหม่ |
flow_calibration_missing (หยุดทันที) |
ยังไม่ได้สอบเทียบอัตราการไหลใน Week_2 / ไฟล์หาย | รัน Week_2/02_Pump_Control/01_flow_rate_calibration.py → รัน Week_3 ใหม่ |
| pH เพี้ยน / ปริมาตรเพี้ยน | ใช้ไฟล์สอบเทียบของบอร์ดอื่น / สอบเทียบ Week_2 คุณภาพต่ำ | สอบเทียบใหม่ "บนบอร์ดตัวนี้" (R² ≥ 0.99, RSD ต่ำ) |
| ไม่เห็นค่าในแอป | ยังไม่จับคู่ BLE / ไม่ได้ Run | จับคู่ใหม่ → เปิด 01_titration_auto.py → Run |
ค้างที่ waiting_for_start |
รอกดปุ่มเริ่ม | กด BUTTON_1 บนบอร์ด หรือรอ timeout |
ขึ้น temp_sensor_warning |
สาย DS18B20 หลุด/ไม่มี pull-up 4.7K | ตรวจสายขา 16; ระบบใช้ 25 °C ต่อได้ |
| ไม่พบจุดสมมูล | ข้อมูล < 3 จุด / ปริมาตรไม่ถึง | เพิ่ม MAX_VOLUME_ML / ตรวจความเข้มข้น |
| ปั๊มไม่ทำงาน | ต่อปั๊มผิด endpoint | ต่อปั๊มที่ CONTROL_1 ให้ตรง PUMP_ENDPOINT |
| ปั๊มเปิดค้าง | — | ฮาร์ดแวร์ไทเมอร์ตัดเองที่ DOSE_MAX_ON_MS; กด Stop ในแอปได้ |
- ล้างหัววัด pH ด้วยน้ำกลั่นทุกครั้งที่เปลี่ยนสารละลาย แล้วซับให้แห้ง (ห้ามถู)
- หัววัดต้องจม ถึง glass bulb ตลอดการวัด (เติมน้ำกลั่นได้โดยไม่เปลี่ยนจำนวนโมล)
- การสอบเทียบเป็นงานของนิสิต — ทำใน Week_2 (pH 3 จุด + อัตราการไหลปั๊ม) บน "บอร์ดตัวเอง" ก่อนเสมอ Week_3 อ่านไฟล์ผลสอบเทียบมาใช้จริง (ไม่ใช้ค่าคงที่ตายตัว และไม่ใช่ "กล่องดำ" ของเฟิร์มแวร์)
- บทเรียนนี้ไม่มีโหมดสอบเทียบ — ถ้าไฟล์สอบเทียบหาย Week_3 จะหยุดและบอกให้ไปสอบเทียบ Week_2 ก่อน
- กด Stop ในแอป เพื่อหยุดได้ทุกเมื่อ ปั๊มจะปิดทันทีและถูก guard ด้วยฮาร์ดแวร์ไทเมอร์
- อ่านผลที่ event
titration_completeในแอป (ไม่ต้องดาวน์โหลดไฟล์)
Lean User Manual for Week 3 — app is the display, raw drivers live in firmware, calibration is the student's own Week_2 work that Week_3 reads and applies. คู่มือใช้งานรุ่นลีน — แอปเป็นจอแสดงผล, ไดรเวอร์ดิบอยู่ในเฟิร์มแวร์, การสอบเทียบเป็นงานของนิสิตจาก Week_2 ที่ Week_3 อ่านมาใช้.