มาทำลองเล่น Sensor API บนแอนดรอยด์กัน

อย่างที่รู้กันว่าบนแอนดรอยด์นั้นมี Sensor มากมายเพื่อช่วยเพิ่มลูกเล่นและความสามารถต่างๆให้กับ Android OS อีกทั้งยังมี Sensor API เพื่อให้นักพัฒนาสามารถสร้างแอปที่เรียกใช้งาน Sensor เหล่านั้นได้อีกด้วย

ดังนั้นในบทความนี้เจ้าของบล็อกก็จะพามาดูกันว่าการเรียกใช้งาน Sensor API บนแอนดรอยด์นั้นต้องทำอย่างไร และมี Sensor สำหรับวัดค่าอะไรให้เรียกใช้งานบ้าง

Sensor ที่มีให้ใช้บนแอนดรอยด์สามารถวัดค่าอะไรได้บ้าง

บอกเลยว่าประเภทของ Sensor ที่มีให้ใช้งานบนแอนดรอยด์นั้นมีหลายประเภทมาก โดยสามารถวัดค่าต่างๆได้ดังนี้

Accelerometer

Sensor.TYPE_ACCELEROMETER (API Level 3)
Sensor.TYPE_ACCELEROMETER_UNCALIBRATED (API Level 26)

วัดความเร่งที่เกิดขึ้นกับอุปกรณ์แอนดรอยด์ในแนวแกน X, Y และ Z

โดยมีหน่วยเป็น Metre/Second²

ค่าจาก Accelerometer จะเป็นผลรวมของความเร่งที่เกิดจากแรงโน้มถ่วง (Gravity) ของโลกและความเร่งที่เกิดจากการเคลื่อนที่ของอุปกรณ์แอนดรอยด์ (Linear Accelerometer)

Gravity

Sensor.TYPE_GRAVITY (API Level 9)

วัดความเร่งที่เกิดจากแรงโน้มถ่วงของโลกโดยอ้างอิงจากทิศทางของอุปกรณ์แอนดรอยด์ในแนวแกน X, Y และ Z

โดยมีหน่วยเป็น Metre/Second²

ค่าจาก Gravity จะเป็นความเร่งที่เกิดจากแรงโน้มถ่วงของโลกเท่านั้น ไม่รวมไปถึงความเร่งที่เกิดจากการเคลื่อนที่ของอุปกรณ์แอนดรอยด์ (Linear Accelerometer)

Linear Accelerometer

Sensor.TYPE_LINEAR_ACCELEROMETER (API Level 9)

วัดความเร่งที่เกิดการจากเคลื่อนที่ของอุปกรณ์แอนดรอยด์ในแนวแกน X, Y และ Z

โดยมีหน่วยเป็น Metre/Second²

ค่าจาก Linear Accelerometer จะเกิดจากความเร่งในการเคลื่อนที่ของอุปกรณ์แอนดรอยด์เท่านั้น ไม่รวมไปถึงความเร่งที่เกิดจากแรงโน่มถ่วงของโลก (Gravity)

Gyroscope

Sensor.TYPE_GYROSCOPE (API Level 3)
Sensor.TYPE_GYROSCOPE_UNCALIBRATED (API Level 18)

วัดความเร็วในการหมุนของอุปกรณ์แอนดรอยด์ในแนวแกน X, Y และ Z

โดยมีหน่วยเป็น Radian/Second

Rotation Vector

Sensor.TYPE_ROTATION_VECTOR (API Level 9)

วัดทิศทางในแนวแกน X, Y และ Z โดยมีการอ้างอิงตำแหน่งจากสนามแม่เหล็กโลกและองศาในการหมุนของอุปกรณ์แอนดรอยด์

โดยมีหน่วยเป็น Degree ในแนวแกน X, Y, Z และความแม่นยำในหน่วย Radian

Geomagnetic Rotation Vector

Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR (API Level 19)

คล้ายกับ Rotation Vector แต่จะใช้ Magnetometer แทน Gyroscope ซึ่งใช้พลังงานน้อยกว่า

Game Rotation Vector

Sensor.TYPE_GAME_ROTATION_VECTOR (API Level 18)

คล้ายกับ Rotation Vector แต่ต่างกันตรงที่การวัดทิศทางในแนวแกน X, Y และ Z จะไม่ได้มีการอ้างอิงจากสนามแม่เหล็กโลก แต่จะใช้แรงโน้มถ่วงในการอ้างอิงแทน

Magnetic Field

Sensor.TYPE_MAGNETIC_FIELD (API Level 3)
Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED (API Level 18)

วัดค่าเหนี่ยวนำทางแม่เหล็กไฟฟ้าที่กระทำต่ออุปกรณ์แอนดรอยด์ในแนวแกน X, Y และ Z

โดยมีหน่วยเป็น micro-Tesla (uT)

Pose 6 Degrees of Freedom

Sensor.TYPE_POSE_6DOF (API Level 24)

การวัดค่าในการเคลื่อนไหวของอุปกรณ์แอนดรอยด์ในรูปแบบของ 6 Degree of Freedom (Forward/Back, Up/Down, Left/Right, Yaw, Pitch และ Roll) โดยจะรวมไปถึงผลต่างขององศาและระยะทางในการเคลื่อนที่ที่เปลี่ยนไปโดยเทียบกับตำแหน่งก่อนหน้า

Significant Motion

Sensor.TYPE_SIGNIFICANT_MOTION (API Level 18)

ตรวจจับการเคลื่อนไหวใดๆก็ตามที่มีนัยยะสำคัญ โดยใช้ Sensor ที่เกี่ยวข้องกับการเคลื่อนไหวของอุปกรณ์แอนดรอยด์ที่มีอยู่ภายในเครื่อง

Motion Detect

Sensor.TYPE_MOTION_DETECT (API Level 24)

ตรวจจับการเคลื่อนไหวของอุปกรณ์แอนดรอยด์ เมื่อเคลื่อนไหวอย่างต่อเนื่องเป็นระยะเวลานาน 5-10 วินาที

Stationary Detect

Sensor.TYPE_STATIONARY_DETECT  (API Level 24)

ตรวจจับการเคลื่อนไหวของอุปกรณ์แอนดรอยด์ เมื่อถูกวางไว้กับที่เป็นระยะเวลานาน 5-10 วินาที

Step Counter

Sensor.TYPE_STEP_COUNTER (API Level 19)

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

Step Detector

Sensor.TYPE_STEP_DETECTOR (API Level 19)

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

Low Latency Off-body Detect

Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT (API Level 26)

ตรวจจับการสวม/ถอดอุปกรณ์แอนดรอยด์ เช่น ผู้ใช้สวม/ถอดอุปกรณ์แอนดรอยด์ที่เป็นแบบ Wearable Device

Heart Rate

Sensor.TYPE_HEART_RATE (API Level 20)

วัดอัตราการเต้นของหัวใจ โดยมีหน่วยเป็น Beat/Minute

Heart Beat

Sensor.TYPE_HEART_BEAT (API Level 24)

ตรวจจับสัญญาณการเต้นของหัวใจที่มีค่าสูงสุดในช่วงเวลานั้นๆ โดยคำนวณด้วย QRS Complex ของสัญญาณ ECG

Hinge Angle

Sensor.TYPE_HINGE_ANGLE (API Level 30)

วัดองศาในการกาง/พับหน้าจอของอุปกรณ์แอนดรอยด์ที่เป็น Foldable Device

โดยมีหน่วยเป็น Degree

Ambient Temperature

Sensor.TYPE_AMBIENT_TEMPERATURE (API Level 14)

วัดอุณหภูมิโดยรอบอุปกรณ์แอนดรอยด์ มีหน่วยเป็น  Degree Celcius

Relative Humidity

Sensor.TYPE_RELATIVE_HUMIDITY (API Level 14)

วัดความชื้นสัมพัทธ์ของอากาศโดยรอบอุปกรณ์แอนดรอยด์ มีหน่วยเป็น %

Light

Sensor.TYPE_LIGHT (API Level 3)

วัดระดับความเข้มของแสงที่กระทำต่อ Sensor โดยมีหน่วยเป็น Lux

Proximity

Sensor.TYPE_PROXIMITY (API Level 3)

วัดระยะห่างของวัตถุที่กระทำต่อ Sensor โดยค่าที่ได้จะขึ้นอยู่กับ Proximity Sensor ของเครื่องนั้นๆ

บางเครื่องที่สามารถวัดระยะห่างได้ก็จะส่งค่ามาเป็น Centimetre ในขณะที่บางเครื่องวัดได้แค่ 2 สถานะเท่านั้นคือมีวัตถุใดๆบังอยู่กับไม่มีวัตถุบัง

Pressure

Sensor.TYPE_PRESSURE (API Level 3)

วัดความดันบรรยากาศ โดยมีหน่วยเป็น Millibar (hPa)

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

ทำไม Sensor บางตัวมี Uncalibrated ด้วย มันคืออะไร?

จากประเภทของ Sensor ทั้งหมด ผู้ที่หลงเข้ามาอ่านก็อาจจะสังเกตเห็นว่า Sensor อย่าง Accelerometer, Gyroscope และ Magnetic Field นั้นมีแบบ Uncalibrated ด้วย

โดยปกติแล้ว Sensor จำพวก Hardware Sensor ที่อยู่ในอุปกรณ์นั้นจะไม่ได้ค่าที่ถูกต้องเป๊ะๆเสมอไป จึงต้องมีการทดสอบและวัดค่าจากนั้นก็ทำการปรับเพื่อชดเชยค่าให้แม่นยำที่สุด ซึ่งนั้นคือ Calibrated

ดังนั้น Uncalibrated จึงหมายถึงการเรียกใช้งาน Sensor เหล่านั้นโดยไม่สนใจการชดเชยค่าแต่อย่างใด ซึ่งจะเหมาะกับการใช้งานในบางกรณีเท่านั้น สำหรับนักพัฒนาทั่วไปไม่ต้องสนใจค่าจาก Sensor แบบ Uncalibrated ก็ได้

การเรียกใช้งาน Sensor API บนแอนดรอยด์

ในการเรียกใช้งานนั้นจะเรียกผ่านคลาสที่ชื่อว่า SensorManager ซึ่งเป็นหนึ่งใน System Service ของระบบแอนดรอยด์นั่นเอง

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

และไม่ว่าจะเป็นอุปกรณ์แอนดรอยด์แบบไหนก็ตาม ก็จะมี SensorManager ให้เรียกใช้งานอยู่เสมอ อยู่ที่ว่าจะมี Sensor ที่ต้องการเรียกใช้งานอยู่หรือป่าว

ในกรณีที่อยากรู้ว่าอุปกรณ์แอนดรอยด์เครื่องนั้นมี Sensor อะไรให้ใช้งานบ้าง ก็สามารถใช้คำสั่งแบบนี้ได้เลย

val sensorManager: SensorManager = /* ... */
val sensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)

โดยที่ Sensor.TYPE_ALL นั้นหมายถึง Sensor ทุกประเภทนั่นเอง แต่ถ้าอยากรู้ว่าอุปกรณ์แอนดรอยด์เครื่องนั้นมี Sensor ประเภทไหนกี่ตัว ก็สามารถกำหนดเป็น Sensor ที่ต้องการได้เช่นกัน

บนแอนดรอยด์ไม่จำเป็นต้องมี Sensor ละ 1 ตัวเสมอไป อาจจะมีมากกว่า 1 ก็ได้ ขึ้นอยู่กับจุดประสงค์ในการออกแบบของอุปกรณ์แอนดรอยด์เครื่องนั้นๆ

และถ้าอยากเรียกใช้งาน Sensor ก็ให้ใช้คำสั่ง

val sensorManager: SensorManager = /* ... */
val lightSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)

จะเห็นว่าคำสั่ง getDefaultSensor(...) นั้นให้ค่าออกมาเป็น Sensor? นั่นหมายความว่าถ้าไม่มี Sensor ดังกล่าว ก็จะได้ค่าออกมาเป็น null นั่นเอง

val sensorManager: SensorManager = /* ... */
sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)?.let { lightSensor: Sensor ->
    // Light sensor is available
} ?: run {
    // No light sensor
}

โดยคลาส Sensor ที่ได้นั้นจะมีข้อมูลของ Sensor แต่ละตัวอยู่ข้างในด้วย

การอ่านค่าจาก Sensor

ในการอ่านค่าจาก Sensor ใดๆก็ตามจะต้องกำหนดผ่าน SensorManager ทุกครั้ง โดย Sensor จะทำการอ่านค่าอยู่ตลอดเวลาและส่งข้อมูลมาให้ในลักษณะของ Event Listener ที่ชื่อว่า SensorEventListener

val sensorEventListener = object : SensorEventListener {
    override fun onSensorChanged(event: SensorEvent) {
        // Sensor's value changed
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // Sensor's accuracy changed
    }
}

โดยนักพัฒนาจะต้องใช้คำสั่ง registerListener(...) ของ SensorManager เพื่อเริ่มการอ่านค่าจาก Sensor ที่ต้องการ และจะต้องใช้คำสั่ง unregisterListener(...)  เพื่อเลิกใช้งาน Sensor นั้นๆด้วย

private val sensorManager: SensorManager = /* ... */

private fun startLightSensor() {
    sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)?.let { sensor: Sensor ->
        sensorManager.registerListener(
            sensorEventListener,
            sensor,
            SensorManager.SENSOR_DELAY_NORMAL
        )
    }
}

private fun stopLightSensor() {
    sensorManager.unregisterListener(sensorEventListener)
}

private val sensorEventListener: SensorEventListener = /* ... */

ใน onSensorChanged(...) ก็จะทำการอัปเดตค่าให้ตลอดเวลาที่ Sensor ทำงาน โดยจะส่งค่ามาเป็นคลาส SensorEvent

และสำหรับค่าที่อยู่ใน SensorEvent นั้นจะเป็นลักษณะของ FloatArray โดยที่จำนวนข้อมูลใน FloatArray นั้นจะขึ้นอยู่กับว่าเป็น Sensor แบบไหน

จากตัวอย่างข้างบน เจ้าของบล็อกอ่านค่าจาก Sensor.TYPE_LIGHT ซึ่งมีข้อมูลตัวเดียวเป็นค่า Lux ดังนั้นจะต้องใช้คำสั่งแบบนี้

val sensorEventListener = object : SensorEventListener {
    override fun onSensorChanged(event: SensorEvent) {
        val lux: Float = event.values.getOrNull(0) ?: 0f
        // Do something
    }

    /* ... */
}

และถ้าเป็น Sensor.TYPE_ACCELEROMETER ก็จะดึงค่าในรูปแบบเดียวกัน แต่จำนวนใน FloatArray ต่างกัน

val sensorEventListener = object : SensorEventListener {
    override fun onSensorChanged(event: SensorEvent) {
        val x: Float = event.values.getOrNull(0) ?: 0f
        val y: Float = event.values.getOrNull(1) ?: 0f
        val z: Float = event.values.getOrNull(2) ?: 0f
    }

    /* ... */
}

โดยค่าที่ได้จาก Sensor แต่ละประเภทสามารถดูข้อมูลเพิ่มเติมได้จาก Value - Sensor Event [Android Developer]

SensorEvent | Android Developers

และนอกจากค่าที่ต้องการแล้ว ใน SensorEvent ก็ยังมีข้อมูลอื่นๆให้ด้วย

val event: SensorEvent = /* ... */
val accuracy: Int = event.accuracy
val sensor: Sensor = event.sensor
val timestamp: Long = event.timestamp

Sensor บางตัวก็ต้องขอ Permission ก่อนใช้งาน เนื่องจากเกี่ยวข้องกับ User Privacy

บนแอนดรอยด์เวอร์ชันใหม่ๆอย่าง Android 10 เป็นต้นไปที่ให้ความสำคัญกับเรื่อง User Privacy มากขึ้น จึงทำให้การอ่านข้อมูลจาก Sensor ที่เกี่ยวข้องกับข้อมูลส่วนตัวของผู้ใช้ จำเป็นจะต้องขอ Permission จากผู้ใช้ก่อน ซึ่งจะประกอบไปด้วย

  • android.permission.BODY_SENSORS : สำหรับ Sensor.TYPE_HEART_RATE
  • android.permission.ACTIVITY_RECOGNITION : สำหรับ Sensor.TYPE_STEP_COUNTER หรือ Sensor.TYPE_STEP_DETECTOR

สรุป

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

แหล่งข้อมูลอ้างอิง