Notice:
This post is older than 5 years – the content might be outdated.
In the previous parts of this series we had a look at how a kernel driver works. This time we continue to better documented areas. While Google gave the design of the kernel module into the hands of the hardware manufacturers they define a clear interface for the Hardware Abstraction Layer (HAL). This is well documented with explanations of the most important functions, so in this article I will only delve into the lesser known parts.
Implementing a new Sensor Class
For HAL, the implementation is straight forward. Google provides an interface that needs to be implemented to access the existing infrastructure for sensors, called the SensorBase class. To use the interface, we need to write an abstract proximity sensor class that inherits from this base class. It supports all official sensor types Google defines; The proximity sensor class then manages the access to our kernel driver.
The first interesting part in this class is the constructor. On instance creation, the constructor of the base class is called. To do so we give the SensorBase class „SRF02 input event module“ as a second argument. This is the name we chose for our input events. SensorBase calls a private function which is looking for an entry in /dev/input that matches the given name and if it exists it returns a file descriptor. And just like that we have the first part of the connection to our kernel module.
We just set a few private variables such as mPendingEvent. That’s a structure of the type sensors_event_t for storing the various sensor data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
ProximitySensor::ProximitySensor () : SensorBase (NULL, "SRF02 input event module"), mEnabled (0), mInputReader((size_t)(4)), mHasPendingEvent(true) { mPendingEvent.sensor = ID_PX; mPendingEvent.type = SENSOR_TYPE_PROXIMITY; mPendingEvent.distance = 5; memset(mPendingEvent.data, 0, sizeof(mPendingEvent.data)); enable(0, 1); } |
Turning on the hardware
Let’s continue to the last function called by the constructor. Basically enable() works the same way we enabled our sensor in the last part of this series: Writing 1 or 0 to the sysfs file activates the implemented methods. The character array “sysfs“ contains the path to this file in the sysfs. We open this file and write the command to it as a string.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
int ProximitySensor::enable (int32_t handle, int en) { // ... if (newState != mEnabled) { if (!mEnabled && dev_name != NULL) { open_device(); } char sysfs [PATH_MAX]; strcpy (sysfs, I2C); strcat (sysfs, "value_now"); int fd = open (sysfs, O_RDWR); // ... else { char buf [2]; buf [0] = newState ? '1' : '0'; buf [1] = ''; write (fd, buf, sizeof(buf)); close (fd); setInitialState(); } } mEnabled = newState; mHasPendingEvent = true; // ... } |
Catching the events
Let’s look at the most interesting part in this class: reading out the events generated in the kernel. To do so we use a class InputEventCircularReader. In the constructor we created an instance of this class called mInputReader. From this class we call the function fill() with the file descriptor we got for our entry in /dev/input. As long as input events are generated, we can fetch them with mInputReader.readEvent(&event). Now the events get evaluated by their types: EV_ABS stands for absolute values. If we get such an event it should have the event code ABS_DISTANCE that describes distances between the sensor and an interaction surface. In this case, the event should be one of those we generated in the kernel. The reading event contains the value of the last ranging, so in mPendingEvent we set the structure to store sensor data, some information about our sensor and the distance mentioned above. Event of the type EV_SYN are markers to separate events. With mInputReader.next(); we continue to the next event.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
int ProximitySensor::readEvents (sensors_event_t* data, int count) { // ... ssize_t n = mInputReader.fill(data_fd); // ... while (count && mInputReader.readEvent(&event)) { int type = event->type; if (type == EV_ABS) { if (event->code == ABS_DISTANCE) { mPendingEvent.sensor = ID_PX; mPendingEvent.type = SENSOR_TYPE_PROXIMITY; mPendingEvent.distance = (float) event->value; // ALOGD("sensor srf02 - value is : %d \n ", event->value); } } else if (type == EV_SYN) { // ... } // ... mInputReader.next(); mHasPendingEvent=true; } return numEventRecieved; } |
Stay tuned
Almost there! In the final part we will learn how to connect our sensor to the Android Sensor Framework. Until then head over to our website to learn more about embedded development and the IoT.
The Whole Story
- Part 1: Sensor Stack and Kernel Module
- Part 2: Sensor Readings
- Part 3: HAL
- Part 4: The Android Sensor Framework
5 Kommentare