@CoreStaff
ROHM Sensor Evaluation Kit is an Arduino-compatible expansion board (shield) equipped with 8 sensors: accelerometer, barometric pressure sensor, geomagnetic sensor, ambient-light/proximity sensor, color sensor, hall-effect sensor, temperature sensor, and ultraviolet light sensor. The sensor shield setup guide was covered in ROHM Sensor Evaluation Kit Overview. Arduino libraries with detailed documentation for the individual sensors can be downloaded from the official website: http://www.rohm.com/web/global/sensor-shield-support
The listed libraries on the website require for every sensor you want to use to include another library in Arduino sketch. This is totally fine if you plan to use only one or two sensors, but what if you decide to use more (e.g. 6 sensors) with a single Arduino UNO at the same time?!
The above link is a GitHub repository dedicated to RohmMultiSensor – a single Arduino library that allows you to control all the sensors in ROHM Sensor Evaluation Kit, so it saves your time from including every library separately. Furthermore, this library allows you to set up the sensors as per your current requirements – a feature that the original ROHM libraries lack currently. As an added bonus, it’s compliant with Arduino 1.5 IDE specifications. It also includes custom syntax highlighting, a detailed readme file and lots of examples!
Let’s assume you want to interface with all of the sensors using the libraries provided by ROHM. Not only will this result in a fairly long Arduino sketch, there’s actually quite a lot of unnecessary operations going on in the background. For example, each of the libraries has its own methods for controlling the I2C bus, but all of them are identical. All of this will increase the overall Arduino program size and memory usage. Can we have just a single set of I2C methods? The following is the compiler output for sketch that uses all the 6 ROHM libraries we need:
Figure 1. Compiler output for all the sensors using the libraries provided by ROHM
9886 bytes doesn’t seem like a lot, but remember, this sketch doesn’t do anything else other than pull data out of the sensors and display them on the serial port. What this number is really telling us is that the third of the storage space has been used already, despite the apparent simplicity of the task. This might be an issue when adding more functionality.
Now, let’s compare the compiler output of a sketch that does exactly the same, but uses the RohmMultiSensor library:
Figure 2. Compiler output for all the sensors using the RohmMultiSensor library. Notice the reduction in both flash storage size and RAM requirements.
You can see that we saved more than 2000 bytes of flash storage and 100 bytes of RAM!
Let’s take a look at the example mentioned above – running 6 ROHM sensors on a single Arduino. The following sensors will be used: accelerometer (KX022-1020), barometric pressure sensor (BM1383GLV), ambient-light/proximity sensor (RPR-0521RS), color sensor (BH1745NUC), temperature sensor (BD1020HFV) and UV sensor (ML8511A).
Figure 3. 6 sensors can be used simultaneously.
The reason for using these sensors is fairly simple: all of them work on 3V. Since we can only set one voltage level on the shield for all the sensor to use, we have to go for the voltage level supported by the most sensors. When using RohmMultiSensor library, the code will look like this:
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
// definition #define INCLUDE_KX022_1020 #define INCLUDE_BM1383GLV #define INCLUDE_RPR_0521RS #define INCLUDE_BH1745NUC #define INCLUDE_BD1020HFV #define INCLUDE_ML8511A // inclusion #include <RohmMultiSensor.h> // instantiation KX022_1020 acc; BM1383GLV bar; RPR_0521RS als; BH1745NUC rgbc; BD1020HFV temp; ML8511A uv; void setup() { Serial.begin(9600); Wire.begin(); // initialization acc.init(); bar.init(); als.init(); rgbc.init(); temp.init(ANALOG_1); uv.init(ANALOG_2); Serial.println("X[g]\tY[g]\tZ[g]\tp[hPa]\tPS[cnt]\tALS[lx]\tR[-]\tG[-]\tB[-]\tC[-]\tt[dg C]\tUV[mW/cm^2]"); void loop() { //measurement float* accelValue = acc.measure(); float pressValue = bar.measure(); float psValue = als.measure(PS); float alsValue = als.measure(ALS); unsigned int* rgbcValue = rgbc.measure(); float tempValue = temp.measure(); float uvValue = uv.measure(); Serial.print(accelValue[0]); Serial.print('\t'); Serial.print(accelValue[1]); Serial.print('\t'); Serial.print(accelValue[2]); Serial.print('\t'); Serial.print(pressValue); Serial.print('\t'); Serial.print(psValue); Serial.print('\t'); Serial.print(alsValue); Serial.print('\t'); Serial.print(rgbcValue[0]); Serial.print('\t'); Serial.print(rgbcValue[1]); Serial.print('\t'); Serial.print(rgbcValue[2]); Serial.print('\t'); Serial.print(rgbcValue[3]); Serial.print('\t'); Serial.print(tempValue); Serial.print('\t'); Serial.println(uvValue); delete[] accelValue; delete[] rgbcValue; delay(100); } |
And this is what the output looks like on Arduino IDE Serial Plotter. Of course, an output like this can hardly be used for any practical purposes. However, it shows that all the sensors are working and measuring data.
Figure 4. Serial Plotter output for all the 6 sensors.
When using this library, you have to follow the five steps: definition, inclusion, instantiation, initialization and measurement. This sounds a lot more complicated than it actually is, so let’s take a look at each of them in more detail:
1. Definition
The first step is to #define all the sensors you want to use. The reason why these definitions have to be the first is explained in the following step.
To shorten the code a little bit, you can use one of the preprogrammed shorthand definitions. For example, #define INCLUDE_ALL_1V8_SENSORS will define all sensors which have recommended supply voltage of 1.8 V. Other options are #define INCLUDE_ALL_3V0_SENSORS or #define INCLUDE_ALL_5V0_SENSORS for 3 V or 5 V, respectively.
2. Inclusion
This is where you actually #include the library header file. The reason that all the sensor #define statements had to be done before this step is simple: the header file (RohmMultiSensor.h) includes other sensor-specific files, based on whether the appropriate sensor was defined. This allows the library to dynamically change its size based on user’s requirements, saving the Arduino flash storage space and RAM.
3. Instantiation
Since each of the sensors has its own class, you have to create an instance of that class before you can access its methods. This process is called instantiation and all you have to do is to type the sensor class name and then the name of the instance. For example:
BM1383GLV bar;
This will create an instance of the class BM1383GLV, which you will be able to access with the name bar.
4. Initialization
Each sensor has to be initialized before use. All you have to do is to call the .init() method for each of the sensors. This method returns 0 on successful completion and 1 if there was any problem. You can use the following code to check if all the sensors initialized successfully:
1 2 3 4 5 6 7 8 9 |
void setup() { if(bar.init() == 1) { // there was an error, you have to fix it! } // everything went fine, you can start measuring! |
The .init() method is used for one additional function: changing the sensor settings. You can pass various arguments to change the sensor behavior.
Let’s say we want to change the operating mode of the pressure sensor. The default mode will take measurements at 200 ms, then return their average. If we want the sensor to run faster (it can get a little less accurate), we can call .init() using the following argument:
bar.init(BM1383GLV_CONTINUOUS_100_MS);
Now, the measurement will only take at 100 ms. Note that all the settings are reset to the default each time the sensor is restarted. Since this will usually be tied to a complete Arduino reset (i.e. pressing the reset button), it shouldn’t be too much of an issue.
Please refer to the GitHub Library reference for a complete list of currently implemented settings for each of the sensors.
Note: These are optional; usually, default settings will suffice (i.e., calling the .init() without any argument).
5. Measurement
At this point, everything is ready to start measuring data. You can do this by calling .measure() method for sensor(s) you want to take measurements with. This method takes no arguments and the return data type depends on which sensor you are measuring with. Some sensors will return a single number – either float or some sort of an integer number.
float pressValue = bar.measure();
The above example will measure pressure with BM1383GLV and return the value in hPa.
However, there are some sensors that need to return more than just a single number. For example, the KX022-1020 accelerometer measures acceleration in three axes: X, Y and Z. Obviously, we need to use an array to return all the values. The memory for the arrays is allocated dynamically, so we have to free the memory again when we don’t need the array.
In the following example, we define, include, instantiate and initiate the KX022-1020 accelerometer. Then, we create a dynamic array of float numbers called accelValue. Then, when we don’t need the array anymore, we can deallocate the memory with delete[]. This ensures that there are no memory leaks. Memory leak is a situation in which dynamically allocated memory isn’t deallocated properly and remains inaccessible for as long as the Arduino isn’t reset. In this example, you can see how to properly free the dynamically allocated memory.
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 48 49 50 51 52 53 54 55 56 57 58 59 |
#define INCLUDE_KX022_1020 #include <RohmMultiSensor.h> KX022_1020 acc; void setup() { Serial.begin(9600); Wire.begin(); acc.init(); Serial.println("X[g]\tY[g]\tZ[g]”); } void loop() { // dynamically create an array float* accelValue = acc.measure(); // now we can access the elements in accelValue array Serial.print(accelValue[0]); Serial.print('\t'); Serial.print(accelValue[1]); Serial.print('\t'); Serial.println(accelValue[2]); //safely deallocate the memory delete[] accelValue; delay(100); } |
Since you’re working with Arduino, you’ve most likely encountered an interrupt. If you didn’t, don’t worry, interrupts are easy to work with, that is, if they’re set properly.
Simply put, interrupt is a signal that will make the Arduino “jump” to a specific part of the code. This part of code is a special function called interrupt service routine, commonly referred to as ISR. The most common use of interrupts is to allow precise timing control, so the ISR function should be as short as possible. Also, it shouldn’t take any arguments and it shouldn’t return anything.
Let’s take a look at BM1422GMV geomagnetic sensor that needs the proper setup of the interrupts.
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#define INCLUDE_BM1422GMV #include <RohmMultiSensor.h> BM1422GMV mag; void isr(void) { mag.setFlagDrdy(); } void setup() { Serial.begin(9600); Wire.begin(); mag.init(isr); Serial.println("X[uT]\tY[uT]\tZ[uT]"); } void loop() { float* magValue = mag.measure(); Serial.print(magValue[0]); Serial.print('\t'); Serial.print(magValue[1]); Serial.print('\t'); Serial.println(magValue[2]); delete[] magValue; delay(100); } |
There’s memory deallocation (since BM1422GMV, much like the accelerometer, returns values for all three axes), as well as ISR. You can see that the only thing it does is raising a flag inside the BM1422GMV class, telling there are new data ready to be collected. The .setFlagDrdy() method is a part of the library. Please note that you have to supply the ISR name to the .init() method.
However, just setting the interrupt service routine in the code is not enough. We need to select correct settings on the shield itself.
Arduino UNO has two external interrupts: INT0 (located on pin D2) and INT1 (on pin D3). We have to connect the appropriate pin with the INT pin on the sensor. You can select which of the two interrupts you want to use in the class instantiation. The default value is INT_0 to use the interrupt on Arduino pin D2, or you can change it to INT_1 in case the interrupt on Arduino D2 is already used by something else.
Figure 5. Overview of all the pins responsible for interrupt settings.
To connect the sensor interrupt to Arduino, we will use the J3 and J4 pin headers. J3 is used to connect an interrupt to Arduino D2 and J4 to D3. Each of the headers has pins labeled INT1 to INT5, as well as pins labeled INTR1 to INTR5. The reason for this is because the sensors can have two different types of output for the interrupts:
Let’s revisit the example code above. We want to connect the Arduino INT0 to the INT pin on BM1422GMV. Let’s assume that the sensor is connected to the I2C_1 slot. In this case, we will short out the pin INTR1 on J3.
Figure 6. Interrupt setting for BM1422GMV connected to I2C_1 slot.
In case we need to use the interrupts on any other sensor(s) – let’s say BH1745NUC color sensor – the interrupt setting will be as follows: If we connect the sensor to I2C_3 and want to use Arduino interrupt INT1, we will have to short out pins INT3 on J4 and INT3 on J16, since this sensor needs an external pull-up.
Figure 7. Interrupt setting for BH1745NUC connected to I2C_3 slot.
Please refer to Notes section in the library README for further details on interrupts and their setting for the different sensors.
1. The geomagnetic sensor (BM1422GMV) is the only sensor that requires interrupts to work. However, the interrupts can be enabled on all the other I2C sensors as well. Enabling them isn’t necessary for the library to work, but it can be very useful in some applications. Currently, the interrupt features aren’t implemented in the library for sensors other than the magnetometer.
2. When using the hall-effect (BD7411G) sensor, you have to disconnect the sensor before uploading the sketch to Arduino. Otherwise, you will get an error while uploading the sketch: avrdude: stk500_getsync() error. This is because OUT pin on BD7411G is connected directly to Arduino pin D0, which is also the Serial RX pin. Since BD7411G output is HIGH when there’s no magnetic field detected, it will block any incoming communication from the Serial port – including sketch upload.
Figure 8. An example of the error you may encounter with BD7411G. Note that if you have verbose output turned off in the Arduino IDE settings, you will only get a message saying “An error occurred while uploading the sketch”. To turn verbose on, go to Arduino IDE -> File -> Preferences and tick “Show verbose output during upload”.
3. When using one of the analog sensors (either BD1020HFV temperature sensor or ML8511A UV sensor), you have to provide the name of the slot in which the sensor is connected to. There are two analog slots on the shield: ANALOG_1 and ANALOG_2. When calling the .init() method for one of the analog sensors, you have to supply the name of that slot to the .init() method (i.e., call .init(ANALOG_1) to initiate analog sensor connected to ANALOG_1 slot). As a safety measure, in case you don’t provide anything, the default .init() value will be ANALOG_1, however, the sensor has to be connected to ANALOG_1 slot. If you’re getting weird data from your analog sensors, make sure that the slot name you provided is the slot the sensor is plugged into.
The main purpose of this article was to provide a potential alternative to the source codes provided by ROHM. As the name suggests, the library RohmMultiSensor will be especially useful if you need to handle multiple sensors in the ROHM Sensor Evaluation Kit – something I certainly plan on doing for my next big project!
If you have suggestions for improvements, share them on GitHub, either by creating a pull request, or in the Issues tab. Creating GitHub Issues is very easy and provides a valuable feedback. Also, if you like the library and if it makes your code just a bit prettier, consider giving the RohmMultiSensor repository a star. Enjoy!