SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
Loading...
Searching...
No Matches
J2 — การอ่านเซนเซอร์ทั้ง 6 ตัว
variant ที่ใช้ได้
mtb-mpy เท่านั้น (ไม่มีการลิงก์ libbento_mpy.a เมื่อ BENTO_HAS_MPY=0)

โมดูล sensors คอมไพล์มาจาก modsensors.c กับไฟล์คู่ modsensors_*.c อีก 5 ไฟล์ซึ่งไม่มีไฟล์ใดอยู่ในแพ็กเกจ mtb-only ส่วนจุดเข้าใช้งานฝั่ง C ที่อยู่ใต้การเรียกทุกตัวในบทนี้ มีอยู่ในทั้งสองแพ็กเกจ และแสดงรายการไว้ที่ จุดเข้าฝั่ง C (ทั้ง 2 แพ็กเกจ) ส่วนเส้นทางไปหาจุดเหล่านั้นบน mtb-only คือ J1 — บัสของเซนเซอร์และ lock ของมัน

เป้าหมายของหัวข้อนี้

เมื่อจบบทนี้จะอ่านเซนเซอร์ทุกตัวบนบอร์ดจาก REPL ได้ คาดเดาได้ว่าการเรียกแต่ละครั้งคืนค่าอะไรและในหน่วยใด และที่มีประโยชน์กว่านั้นคือบอกได้ว่าแต่ละตัวทำอะไรเมื่อฮาร์ดแวร์ไม่มีอยู่ อีกทั้งจะรู้ว่าการเรียกครั้งเดียวแบบใดให้ค่าหกแกนที่สอดคล้องกัน และเหตุใดอีก 5 วิธีที่ได้ตัวเลข 6 ตัวชุดเดียวกันจึงไม่เทียบเท่ากัน

Note
หลักฐานบางเบา ระบุไว้ตั้งแต่ต้น แพ็กเกจนี้ไม่มีไฟล์ Python อยู่เลย: find template -name "*.py" คืนเฉพาะเครื่องมือสำหรับ build ตัวอย่างการใช้งานที่อยู่ในแพ็กเกจจริงสำหรับส่วนอุปกรณ์ต่อพ่วงทั้งหมดมีเพียงชิ้นเดียวคือ README.md:130-132 ซึ่งแสดง sensors.scan() และ sensors.read_all() ตัวอย่างอื่นทุกชิ้นในบทนี้ เขียนขึ้นเอง และกำกับไว้เช่นนั้น ไม่มีสิ่งใดในบทนี้ที่ให้รันไฟล์ที่ไม่มีอยู่

ลำดับการทำงานจริงของเฟิร์มแวร์

ทั้ง 6 ตัวไม่สมมาตรกัน โมดูล sensors ตั้งชื่อเซนเซอร์ไว้ 6 ตัว — bmi270, dps368, sht40, bmm350, capsense, pot (modsensors.c:1218-1223) — แต่มีเพียง BMI270 ที่ไม่มีเงื่อนไข อีก 5 ตัวอยู่หลัง flag BSP_HAS_* ในตารางลงทะเบียน (modsensors.c:1341-1362) และบน build ที่ flag เป็น 0 โมดูลย่อยนั้นไม่ได้แค่นิ่งเฉยแต่ ตัว attribute นั้นไม่มีอยู่ sensors.sht40 โยน AttributeError ไม่ใช่ OSError ให้ถาม hasattr(sensors, 'sht40') เมื่อต้องการรู้ว่า "บอร์ดนี้ติดตั้งตัวนั้นมาหรือไม่"

init() ส่งเสียงดังเมื่อล้มเหลวบางส่วน และตรงไปตรงมาเมื่อล้มเหลวทั้งหมด sensors.init() (modsensors.c:523) โยน OSError เฉพาะเมื่อบัสเองยกขึ้นไม่ได้ไดรเวอร์ย่อยแต่ละตัวที่ล้มเหลวจะพิมพ์หนึ่งบรรทัดและล้าง flag ของตน การเรียกจึงคืน False ในขณะที่เซนเซอร์ที่ปกติยังทำงานต่อไป:

if (!bmi270_init()) {
mp_printf(&mp_plat_print, "sensors: BMI270 init failed\n");
ok = false;
}

รูปแบบเดียวกันนี้ซ้ำสำหรับ DPS368, SHT40, BMM350, CapSense และ pot โดยแต่ละตัวอยู่ภายใน #if BSP_HAS_* ของตัวเอง (modsensors.c:538-571)

read_all() จับ lock เดียวตลอดการกวาดทั้งรอบ มันเริ่มด้วย sensor_i2c_lock(200) — เป็นสองเท่าของ timeout ต่อการเรียกหนึ่งครั้ง เพราะมันทำงานของเซนเซอร์ 6 ตัว — และสร้าง dict ขึ้นภายใน lock นั้น (modsensors.c:820) เซนเซอร์ที่อ่านไม่สำเร็จจะ ไม่มี key อยู่เลย ไม่ใช่มี key ที่เก็บค่า None โค้ดที่เขียน d['sht40']['temperature'] โดยไม่ตรวจสมาชิกภาพก่อนจะโยน KeyError บนบอร์ดที่ SHT40 หยุดตอบ และนั่นคือสัญญาณที่ตั้งใจให้เป็น

ทั้ง 6 ตัว ทีละการเรียก

หน่วย ค่าที่คืน และรูปแบบความล้มเหลว ตารางฉบับเต็มพร้อมเลขบรรทัดอยู่ที่ MicroPython — โมดูลย่อยของเซนเซอร์

เซนเซอร์ การเรียก หน่วย เมื่อล้มเหลว
BMI270 acceleration(), gyroscope(), temperature(), motion(), chip_id() m/s², deg/s, °C OSError พร้อมระบุชื่อเซนเซอร์
DPS368 pressure(), temperature(), altitude(), pressure_temperature() hPa, °C, m OSError
SHT40 temperature(), humidity(), temperature_humidity() °C, RH OSError
BMM350 magnetic(), heading(), chip_id(), cal_reset(), cal_status() µT, องศา 0..360 โดย 0 = ทิศเหนือ OSError
CapSense read(), buttons(), slider() bool, bool, 0..100 OSError ที่ระบุคอร์ผู้ตอบ
Pot read(), percent(), voltage() 0..65535, 0..100, 0..3.3 V OSError

หมายเหตุ 3 ข้อที่ตารางบรรจุไม่ได้:

  • bmi270.temperature() คืออุณหภูมิของได ไม่ใช่อุณหภูมิห้อง มันอ่านเซนเซอร์ภายในของ IMU และมีค่าสูงกว่าอุณหภูมิแวดล้อมหลายองศา หากต้องการอุณหภูมิห้องให้ใช้ sht40.temperature() หรือ dps368.temperature()
  • bmm350.heading() ต้องดู cal_status() ก่อนจึงจะเชื่อถือได้ ค่าทิศทางคำนวณจากเวกเตอร์ X/Y ดิบเทียบกับค่าชดเชยการสอบเทียบที่กำลังทำงานอยู่ (sensor_bmm350.c:791 และ :829) cal_reset() (modsensors_bmm350.c:54) เริ่มการประมาณค่านั้นใหม่ และ cal_status() (:62) คือวิธีตรวจว่าค่าลู่เข้าแล้วหรือยัง
  • pot.voltage() ปรับสเกลเทียบกับค่าคงที่ ไม่ใช่เทียบกับค่าที่วัดได้ POTENTIOMETER_VDDA_V มีค่า 3.3f (sensor_potentiometer.c:19) และซอร์สบันทึกไว้ว่าผังวงจรระบุ VDD_1V8 ทั้ง 2 ค่ายังไม่ได้ปรับให้ตรงกัน และคำสั่งที่ยืนอยู่คือห้ามยืนยันค่าใดค่าหนึ่งจนกว่าจะวัด P15[1] จริง ให้ถือว่า voltage() เป็นค่านับที่ปรับสเกลแล้วซึ่งมีคำถามค้างอยู่ตามที่บันทึกไว้ และให้ใช้ read() หรือ percent() แทนสำหรับสิ่งที่สำคัญ

การเรียกเดียวที่ไม่ใช่ wrapper เพื่อความสะดวก

sensors.bmi270.motion() (modsensors.c:230) คืนค่าครบทั้งหกแกนใต้ lock ของบัส เดียว:

sensor_i2c_lock(100);
bool ok = bmi270_read_accel(&ax, &ay, &az) &&
bmi270_read_gyro(&gx, &gy, &gz);
sensor_i2c_unlock();

การเรียก acceleration() แล้วตามด้วย gyroscope() ให้ตัวเลข 6 ตัวชุดเดียวกัน แต่ ไม่ใช่ สิ่งเดียวกัน: นั่นคือคู่ lock/unlock สองคู่ และบัสเป็นของผู้อื่นในช่วงระหว่างกลาง บนบอร์ดที่อยู่นิ่งจะไม่มีวันเห็นความต่าง แต่บนบอร์ดที่กำลังถูกเขย่า — ซึ่งคือทุกบอร์ดที่ใช้ฝึกโมเดลการเคลื่อนไหว — เวกเตอร์ทั้งสองบรรยายคนละช่วงเวลา และผลที่หลอมรวมออกมาก็ผิดในแบบที่ unit test จับไม่ได้ ให้ใช้ motion() ทุกครั้งที่ตัวเลข 6 ตัวนั้นถูกใช้ร่วมกัน

ทีละขั้น

ขั้นที่ 1 — ยกเซนเซอร์ขึ้นแล้วอ่านคอนโซล

>>> import sensors
>>> sensors.init()
สิ่งที่ควรสังเกต
บนบอร์ดที่สมบูรณ์ดีทั้งหมดจะได้หนึ่งบรรทัด — sensors: all sensors initialized OK (modsensors.c:573-575) — แล้วตามด้วย True บรรทัดอื่นทุกบรรทัดที่การเรียกนี้พิมพ์ได้คือบรรทัดความล้มเหลว หนึ่งบรรทัดต่อหนึ่งไดรเวอร์ย่อย ในรูปแบบ sensors: BMI270 init failed (modsensors.c:533-536 และรูปแบบเดียวกันสำหรับเซนเซอร์ที่มี flag กำกับทุกตัวจนถึง :571) การคืนค่า False พร้อมบรรทัดแบบนั้นหนึ่งบรรทัดหมายความว่าเซนเซอร์ 5 ตัวปกติและอีกหนึ่งตัวไม่ปกติ ส่วน OSError หมายความว่าบัสเองยกขึ้นไม่ได้ และไม่มีการอ่านค่าใดเลย

ขั้นที่ 2 — การเรียก 2 ตัวที่มีตัวอย่างอยู่จริงในของที่ส่งมอบ

>>> sensors.scan()
>>> sensors.read_all()
สิ่งที่ควรสังเกต
scan() คืนแอดเดรสที่ตอบกลับ (J1 — บัสของเซนเซอร์และ lock ของมัน) ส่วน read_all() คืน dict ที่มี key เป็นเซนเซอร์ที่ตอบในการกวาดรอบนั้นพอดี — bmi270 แล้วตามด้วย dps368, sht40, bmm350, capsense, pot เท่าที่บอร์ดและ flag อนุญาต เซนเซอร์ที่อ่านไม่สำเร็จจะ ไม่ปรากฏ นี่คือคู่ที่แสดงไว้ใน README.md:130-132 และเป็นตัวอย่างด้านอุปกรณ์ต่อพ่วงเพียงตัวอย่างเดียวที่ส่งมาในแพ็กเกจ

ขั้นที่ 3 — การอ่านหกแกนที่สอดคล้องกัน

ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
import sensors
sensors.init()
ax, ay, az, gx, gy, gz = sensors.bmi270.motion()
print("accel m/s^2:", ax, ay, az)
print("gyro deg/s :", gx, gy, gz)
สิ่งที่ควรสังเกต
ค่า float 6 ตัว ขณะอยู่นิ่ง แกนหนึ่งของ accelerometer อ่านได้ประมาณ 9.8 และอีกสองแกนประมาณ 0 — แกนใดขึ้นกับท่าที่วางบอร์ดไว้ — และค่า gyro ทั้ง 3 ตัวอยู่ใกล้ 0 พร้อมสัญญาณรบกวนเล็กน้อย เมื่อเอียงบอร์ด ค่า 9.8 จะย้ายไปมาระหว่างแกน การเรียกนี้ไม่มี call site ที่ใดเลยในซอร์สที่ส่งมอบ จึงเป็นเหตุผลที่ตัวอย่างข้างบนเขียนขึ้นเองแทนการคัดมา

ขั้นที่ 4 — IMU ยังมีชีวิตอยู่จริงหรือไม่

ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
import sensors
sensors.init()
print(hex(sensors.bmi270.chip_id()))
สิ่งที่ควรสังเกต
ได้ 0x24 บน BMI270 ที่ปกติ (modsensors.c:253 ไดรเวอร์อยู่ที่ sensor_bmi270.c:65) ค่าอื่นใด หรือ OSError แยก "เซนเซอร์ไม่พูด" ออกจาก "เซนเซอร์พูด แต่รายงานค่าไร้สาระ" — ความต่างที่การเรียกอ่านค่าแยกให้ไม่ได้ เพราะทั้งบัสที่ค้างและเซนเซอร์ที่ค้างอยู่ที่ศูนย์ต่างก็ให้ค่าศูนย์ทั้งคู่ เป็นการเรียกที่ไม่มีผู้เรียกตัวอย่างจึงเขียนขึ้นเอง

ขั้นที่ 5 — เมื่อแมกนีโตมิเตอร์คือตัวที่ผิด

ตัวอย่าง (เขียนขึ้นเอง — ไม่มี call site ในของที่ส่งมอบจริง)
import sensors
sensors.init()
d = sensors.bmm350_diag()
print(d['step'], d['init'], d['id'])
print(d['wr_st'], d['wr_ev'], d['rd_st'], d['rd_ev'])
สิ่งที่ควรสังเกต
dict ที่มี 12 key: step, init, att, s_init, s_en, pres, forced, wr_st, wr_ev, rd_st, rd_ev, id (modsensors.c:1269-1315 ฝั่งไดรเวอร์คือ bmm350_diagnose(), sensor_bmm350.c:863) จำนวนฟิลด์จำกัดไว้ที่สิบสองโดยเจตนาเพื่อไม่ให้ REPL ตัดข้อความ ส่วนคู่ของมันคือ sensors.bmm350_debug() (modsensors.c:1315) ซึ่งพิมพ์การดัมป์รีจิสเตอร์ออกมาแทนการคืนค่า ทั้งสองเป็นข้อมูลวินิจฉัยที่ไม่มีผู้เรียก ตัวอย่างในบทนี้จึงเขียนขึ้นเอง

เหตุผลที่เซนเซอร์ตัวนี้มีข้อมูลวินิจฉัยของตัวเองในขณะที่ตัวอื่นไม่มี: BMM350 อยู่บน I3C ไม่ใช่บน SCB0 จึงล้มเหลวได้ทั้งตัวตามลำพัง ในขณะที่บัสและเซนเซอร์ตัวอื่นทุกตัวยังปกติ (J1 — บัสของเซนเซอร์และ lock ของมัน)

กับดัก

Warning
key ที่หายไปคือส่วนหนึ่งของ API ไม่ใช่ข้อบกพร่อง read_all() ละเซนเซอร์ที่ล้มเหลวออก และไม่เคยเก็บค่า None ให้ตรวจด้วย in ก่อนทำดัชนี
โมดูลย่อยที่ไม่มีอยู่จะโยน AttributeError บน build ที่ BSP_HAS_SHT40=0 ชื่อ sensors.sht40 ไม่มีอยู่ ให้ตรวจความสามารถด้วย hasattr ไม่ใช่ด้วย try: ... except OSError
ห้ามประกอบ motion() ขึ้นใหม่จากการเรียก 2 ครั้ง คู่ lock/unlock สองคู่ไม่เท่ากับคู่เดียว ดู การเรียกเดียวที่ไม่ใช่ wrapper เพื่อความสะดวก
bmi270.temperature() ไม่ใช่อุณหภูมิของห้อง การรายงานค่านั้นเป็นอุณหภูมิแวดล้อมเป็นข้อผิดพลาดทางเอกสารที่เคยเกิดขึ้นมาแล้ว
pot.voltage() มีคำถามเรื่องแรงดันอ้างอิงที่ยังไม่ได้ข้อยุติ โค้ดปรับสเกลที่ 3.3 V ส่วนผังวงจรระบุ VDD_1V8 ห้าม "แก้" ฝั่งใดฝั่งหนึ่งโดยไม่วัด P15[1] ก่อน (sensor_potentiometer.c:6-19)
sensors.snapshot() ไม่มีอยู่ในแพ็กเกจนี้ — มันลงทะเบียนเฉพาะภายใต้ #ifdef USE_KIT_PSE84_EVAL_EPC2 (modsensors.c:1330-1332) และไม่มี BSP ของ Eva ส่งมาที่นี่ sensors.light() ก็ไม่มีเช่นกัน ซึ่งปรากฏในชุดตัวอย่างรุ่นเก่า และไม่ปรากฏในตารางโมดูลใดในซอร์สชุดนี้

กล่อง variant

mtb-mpy mtb-only
โมดูล sensors มี ไม่มี — modsensors.c ไม่อยู่ในแพ็กเกจ
โมดูลย่อย 6 ตัว มี flag กำกับ ไม่มี
ไดรเวอร์ที่อยู่ข้างใต้ ไฟล์ sensor_*.c ชุดเดียวกัน ไฟล์ sensor_*.c ชุดเดียวกัน เรียกโดยโค้ดที่เขียนเอง
การอ่านหลายค่าที่สอดคล้องกัน sensors.bmi270.motion() sensor_i2c_lock() เดียวครอบการเรียกไดรเวอร์ทั้ง 2 ครั้ง — ขั้นที่ 3 — จับ lock เดียวคร่อมการอ่านหลายรีจิสเตอร์
ข้อมูลวินิจฉัย bmm350_diag(), bmm350_debug() bmm350_diagnose(), bmm350_debug_read()