Introduction: SilverLight: Arduino Based Environmental Monitor for Server Rooms

About: Never stops thinking.

Once I was given the task to look for an environmental probe for monitoring the temperature in the server room of my company. My first idea was: why not just use a Raspberry PI and a DHT sensor, it can be setup in less than an hour including the OS installation. For this I got the cold response from blindfolded bossy people that we will not do that because it would cost more in work hours to set it up than buying a device. Having to accept narrow minded people like this for a part of my life was one thing and I ordered some enterprise grade EATON junk off Ebay and call it but I decided at that moment that for my own server room I will build a completely Open Source Arduino based device which will be much better than what I just ordered.

This project is code named SilverLight, don't ask me where I get these names from :) I just looked at the shiny half acrylic box and decided with this name, it has nothing to do with the microhoof product what I found out about after.

Step 1: Hardware Design

Commercial hardware overview.

Ok so I don't even get started with whose great idea was to put an environment monitor inside an ups but obviously there is a market for it so let's see what these can do:

Environmental monitoring device COMPATIBILITY

The 10/100Mb Network-MS, PXGUPS, PXGPDP, and PXGMS.

The 10/100Mb ConnectUPS-X, ConnectUPS-BD and ConnectUPS-E with FW V3.01 and higher. DIMENSIONS (LXWXH)

2.26 x 1.48 x 1.15 (inches) 57.6 x 37.6 x 29.3 (mm) WEIGHT

1.19 oz (34 g)

That's very useful information isn't it? No worries tho because they can't do much. To even get started your UPS will need to have another expensive addon-card for this which connects this with the environmental sensor you buy separately, usually with standard CAT5 cable (don't even try to plug anything into that port because there is nothing standard about it). They claim the device needs 10 minutes to "warm up" that in reality was hours and once it did voila it showed up in their slowly updating java interface and we have temperature and humidity. Setting up alerts based conditions was easy from this point but who cares let's build something better.

This project is a conjunction of multiple of my projects: Natalia weather station, Shadow of phoenix. The box is able to monitor the following environmental constraints:

  • Temperature/humidity/heat index
  • LPG, Smoke, Alcohol, Propane, Hydrogen, Methane and Carbon Monoxide concentrations in the air (MQ2)
  • Solar sensitivity (is the light on in the server room?)
  • Motion PIR sensor (you can even turn the lights on/off automatically from now on thanks to the motion sensor when someone enters the room)

All of this data nicely displayed on an LCD screen while also relayed to a computer (Orange PI Zero) for further processing and alerts. Although it would be possible to hook up digital sensors such as the DHT and the digital pin of the MQ2 to the OrangePI directly, I always prefer using dedicated micros for these tasks and when you need to update the LCD as well and do other low level stuff the Arduino is just unbeatable and can reliably run non-stop for many years (matter of fact not a single Arduino which runs 24/7 failed on me yet). The OrangePI with it's shortcomings (let's face it it's a 10$ computer) like unusable for heavy workload, no bsd support, integrated wifi is bloated etc can easily handle small workload like taking sensor readings through serial (USB) and processing them.

This is a very simple project hardware wise which require the following components:

  • Arduino PRO Micro
  • LCD screen 2x16 character RGB
  • AC-DC isolating switch power module 220V to 5V HLK-5M05 (these are very good for Arduino/ESP projects), this is the 5V/5W version!
  • 2x300ohm resistors
  • 2xleds (red/green)
  • PIR motion sensor
  • MQ2 sensor
  • DHT22
  • LDR
  • 2X10Kohm resistor
  • Buzzer
  • Orange PI Zero
  • mini USB data cable

I did not even bother making a PCB for this just used regular breadboard because the components can be simply connected to the Arduino (see pictures attached):

-DHT22 will require a 10K pullup to VCC (digital)

-LDR will require a 10K pulldown to GND (analog)

-MQ2 can be directly connected to any analog pin (analog) < prefer using analog because why not when we have an MCU with analog pins where we can get the exact value instead of adjusting some pot on the back of the device to get HIGH or LOW out of it, due to the gluing in my design that is inaccessible anyway. Check:

-PIR can be directly connected to any pin (digital)

-LCD: can be driven with 4 pins, can be connected to any pin (digital) will need +2 RS/E (digital)

-Buzzer: can be directly connected to any Arduino pins (digital)

The pinout I used can be seen in the code. Connecting everything together after this is pretty straight forward, you can also do them one by one, make sure 1 sensor works perfectly then proceed to the next one, all what you can get wrong is by mistake connecting wires to wrong places (eg swapping vcc/gnd for a sensor, so far this never killed any of my devices). What I would note here that there were too many VCC and GNDs stacked up for me, I could not squeeze them through a terminal strip so I soldered them all.

Also about the DHTs don't forget from my other projects: if you put the DHT library in your code and the DHT sensor is not connected or wrong DHT connected (eg 11 defined in code you are using 22) that can lead to the program to hang forever at start.

About the PIR motion detection sensors, as you can see on my picture there are tons of fake counterfeits of these, matter of fact I would even find it hard to buy a genuine one from Ebay. The fakes work just as well, even on the long run but they have their circuit mirrored which causes the + and - pins to be reversed, also these are easy to recognize from: coming with blue pcb not the usual green, missing the labels for the potmeters. I was lucky to find a genuine in my box otherwise changing the position would cover the 2 leds for me. I have found that both pots cranked to midway works for me. This will give you long enough range for sensing also when there is motion the digital leg will be kept in HIGH position for about a minute, so you don't have to make up in the code for this. On the fakes it is easy to determine which side is the - and + just look at the corresponding legs for the electrolytic caps connected to the pins.

For cutting the box I used diamond dremel head (which was an overkill but worked great) and regular drilling machine. These junction boxes are easy to work with and although I don't like gluing I did not have screws and bolts at hand when building this so took the bargain of gluing things together (which can be also easily reheated and taken apart later by using the same gluegun without fillament in it).

Step 2: Software Design

The Arduino code is simple as well, it basically pulls all the sensor readings at the beginning of every loop. Turns on the LEDs if there is motion or smoke and also plays an alarm sound on the buzzer if there is smoke (this is the only blocking code so I made it short), then displays the data on LCD and finally sends it over the PC with a 10 seconds hold period, not to flood the port.

This project uses a one way communication from Arduino->OrangePI, there are no commands of any kind implemented. Although this would be perfectly possible to do so as I did it in one of my other project where the computer can send LCD_PRINT1 or LCD_PRINT2 to overwrite one lines of the LCD screen with it's own message (eg: ip address, uptime, system date, cpu usage), the screen area is so small for displaying data from 3 sensors that I didn't even bother. The SOL and SMK values can both go up to 4 digits 0000-1023 taking already up 8 valuable characters on the screen.

With the LCD you can notice a small trick in the code that after each measured value a print of white spaces (" ") are applied, then I move the cursor to fixed positions to place the new icons and data. These are there because the LCD is not that smart to understand numbers, it just draws what it gets and for example if you had a solar value of 525 which all the sudden decreased to 3 then it will display 325 leaving the old junk on the screen there.

A C control code running on the OrangePI and logging the environmental data and sending email alerts when needed.

The OrangePI is running Armbian (which at the time of writing based on Debian Stretch). I will include this in the software part regarding it was a hw problem what it solved. Here are the average power drain of the device:

0.17 A - Arduino only + sensors

0.5-0.62 A - OrangePI booting

0.31 A - Orange PI in idle

0.29 A - Orange PI powered off (can't really shut it down, it doesn't have ACPI or anything like that)

0.60 A - Stress test 100% CPU usage on 4 cores

I had this OrangePI in a box since a long time. With the old kernel the device drained so much current (as the meter said peaked around 0.63 A) what he PSU probably could not provide that it simply did not boot in, the boot process was stuck and I got the 2 ethernet leds being light up constantly and doing nothing.

Now this is kind of annoying as the HLK-5M05 claims it can do 5W on 5V making it being able to provide 1 Amp but with these devices coming out of China you just never know, the 0.63 A peak was way lower than the rated max value. So I was running down simple reboot tests, from 10 reboots the OrangePI would only boot up twice successfully, which almost made me throwing it out from the project since I don't like buggy inconsistent behavior in circuits. So I started googling around maybe there is a way to lower the power consumption at boot time from software (since it was only an issue then) and found some article talking of tweaking the script.bin but it was for the Orange PI PC and the files were missing from the storage so whatever as a last resort I have done the magical "apt upgrade" to upgrade the firmware, kernel and everything else, hoping that it will drain less and the device can boot in and:

Linux silverlight 4.14.18-sunxi #24 SMP Fri Feb 9 16:24:32 CET 2018 armv7l GNU/Linux

Linux silverlight 4.19.62-sunxi #5.92 SMP Wed Jul 31 22:07:23 CEST 2019 armv7l GNU/Linux

It worked! Throwing hardware to a software problem is usually the lazy java developers to go place but in this case we have resolved a hardware problem with software what a great success. I have done like 20 more reboot tests the device booted every single case. I would still note that the power surge from turning on the Opi (connecting/disconnecting) is so big that it will reset the Arduino any given time (a simple reboot will just flicker the LCD but cause no further issues), but this issue remains hidden since the 2 will be booted together.

I have also looked at the kernel modules:

usb_f_acm u_serial g_serial libcomposite xradio_wlan mac80211 lima sun8i_codec_analog snd_soc_simple_card gpu_sched sun8i_adda_pr_regmap sun4i_i2s snd_soc_simple_card_utils ttm sun4i_gpadc_iio snd_soc_core cfg80211 snd_pcm_dmaengine industrialio snd_pcm snd_timer snd sun8i_ths soundcore cpufreq_dt uio_pdrv_genirq uio thermal_sys pwrseq_simple

What do we really need from these? Ok the pwr and thermal might be useful but sound, serial port, wifi (broken hw already) we don't need all of these can be blacklisted. I will also create a custom kernel with only the necessary modules later on.

What we do need and it's not loaded by default is the CDC ACM to communicate with the Arduino, enable it with:

echo "cdc-acm" >> /etc/modules

After this you can already test the connection with:

screen /dev/ttyACM0 9600

You should see the status data being sent in every 10 seconds.

Alerts and monitoring

As of alerts I just put in system() calls in the C control code which receives the data from serial so no external tools are required. Some example alerts:

- Temperature goes over 30 C

- Humidity goes over 70 % (not healthy for the servers)

- Motion detected in the room (this can be annoying if you keep going in your server room)

- Smoke or gas detected (alerts over 100 can be taken seriously, I have played around with this sensor and it turns on for lots of stuff, for example creating smoke next to the sensor with soldering iron resulted a bit over 50 while smoking cigarette next o it spiked up to 500, it even detected gas from regular deodorant from far away)

For keeping historical data I did not bother developing a tool because why to reinvent the wheel when we got excellent monitoring frameworks out there. I will show an example of how to integrate this into my personal favorite, Zabbix:

apt-get install zabbix-agent

Add to the end of: /etc/zabbix/zabbix_agentd.conf

UserParameter=silverlight.hum, head -1 /dev/shm/silverlight-zbx.log | awk -F"," '{ print $1 }'
UserParameter=silverlight.tmp, head -1 /dev/shm/silverlight-zbx.log | awk -F"," '{ print $2 }'
UserParameter=silverlight.sol, head -1 /dev/shm/silverlight-zbx.log | awk -F"," '{ print $4 }'
UserParameter=silverlight.mot, head -1 /dev/shm/silverlight-zbx.log | awk -F"," '{ print $5 }'
UserParameter=silverlight.smk, head -1 /dev/shm/silverlight-zbx.log | awk -F"," '{ print $6 }'

Running zabbix_agentd -p should return now the proper values:

silverlight.hum                               [t|41]
silverlight.tmp                               [t|23]
silverlight.sol                               [t|144]
silverlight.mot                               [t|0]
silverlight.smk                               [t|19]

The heat index, I do collect it but don't see any practical use of it so it is just logged. In the C control code I have implemented 2 logging functions, the first will log all the data in user friendly format:

[SILVERLIGHT] Data received at 2019-09-10 23:36:08 => Humidity: 44, Temp: 22, Hi: 25, Solar: 0, Motion: 0, Smoke: 21
[SILVERLIGHT] Data received at 2019-09-10 23:36:18 => Humidity: 44, Temp: 22, Hi: 25, Solar: 0, Motion: 0, Smoke: 21
[SILVERLIGHT] Data received at 2019-09-10 23:36:29 => Humidity: 44, Temp: 22, Hi: 25, Solar: 0, Motion: 0, Smoke: 22
[SILVERLIGHT] Data received at 2019-09-10 23:36:39 => Humidity: 44, Temp: 22, Hi: 25, Solar: 0, Motion: 0, Smoke: 21

The second one:

void logger2(char *text) {

 FILE *f = fopen("/dev/shm/silverlight-zbx.log", "w");

 if (f == NULL)
    printf("Error opening memory log file!\n");
 fprintf(f, "%s", text);


This will put a 1 liner log in memory (eliminate unnecessary rw operations on the sdcard) which will be always overwritten next time. This log will only contain the 6 data columns and no timestamp, it is easily readable for Zabbix.

As a final bonus: how to program the Arduino directly from the OrangePI so you don't have to walk up to the device every time and plug in your laptop.

There are 2 ways:

-Easy way: Install full Arduino IDE and libraries use some remote desktop like X11 with forwarding, Xrdp, Xvnc, Nxserver etc

-Hard way: Install the Arduino IDE and use the command line

We going to do the hard way this time since I'm not fond of installing X11 on servers. For this you will need 6 components:

1, Arduino IDE for ARM 32 bit ->

2, Python serial -> apt-get install python-serial

3, Arduino Makefile project -> git clone

4, DHT library

5, Sparkfun board definitions

6, SilverLight.ino, main code

To make it easier I have bundled the files needed for the last 4 points (sketchbook.tgz) so you will only need the first 2.

First it's the best to create a regular user which have rw access to the USB port:

adduser silver

usermod -a -G dialout silver

SCP the sketchbook.tgz to the device in the newly created user's home directory and extract it right there:

cd /home/silver

tar xvzf sketchbook.tgz

To understand a bit what's goin on under the hood when you are using the graphical IDE:

The build workflow of building a Arduino sketch when using the Arduino IDE is described on the Arduino website and in more detail here:

Generally, the standard Arduino build process is:

Combine .ino files into the main sketch file. Transformation of the main sketch file: add the #include statement; create function declarations (prototypes) of all functions in the main sketch file; append the contents of the main.cxx file of the target to the main sketch file. Compile the code to object files. Link the object files to produces a .hex file ready for uploading it to the Arduino.

There are some slight differences between the Arduino standard build process and the build process using Arduino-Makefile:

Only one .ino file is supported. Function declarations are not automatically created in the .ino file. The user has to take care of creating the correct function declarations.

The heart of the build process is the Makefile. Don't worry, everything is prepared for you, it is a bit more complicated when compiling this way for non standard boards like the SparkFun series.

BOARD_TAG = promicro
BOARD_SUB = 16MHzatmega32U4
USER_LIB_PATH = /home/silver/sketchbook/libraries
ARDUINO_DIR = /opt/arduino-1.8.9
include /home/silver/sketchbook/Arduino-Makefile/

And all you need to type is a: make upload (which will build the .hex files first then uses avrdude to upload them), it will end up with something like:

mkdir -p build-promicro-16MHzatmega32U4
make reset
make[1]: Entering directory '/home/silver/sketchbook'
/home/silver/sketchbook/Arduino-Makefile/bin/ard-reset-arduino --caterina /dev/ttyACM0
make[1]: Leaving directory '/home/silver/sketchbook'
make do_upload
make[1]: Entering directory '/home/silver/sketchbook'
/opt/arduino-1.8.9/hardware/tools/avr/bin/avrdude -q -V -p atmega32u4 -C /opt/arduino-1.8.9/hardware/tools/avr/etc/avrdude.conf -D -c avr109 -b 57600 -P /dev/ttyACM0 \
        -U flash:w:build-promicro-16MHzatmega32U4/sketchbook.hex:i

Connecting to programmer: .
Found programmer: Id = "CATERIN"; type = S
    Software Version = 1.0; No Hardware Version given.
Programmer supports auto addr increment.
Programmer supports buffered memory access with buffersize=128 bytes.

Programmer supports the following devices:
    Device code: 0x44

avrdude: AVR device initialized and ready to accept instructions
avrdude: Device signature = 0x1e9587 (probably m32u4)
avrdude: reading input file "build-promicro-16MHzatmega32U4/sketchbook.hex"
avrdude: writing flash (11580 bytes):
avrdude: 11580 bytes of flash written

avrdude: safemode: Fuses OK (E:CB, H:D8, L:FF)

avrdude done.  Thank you.

Well thank you avrdude, and now our Arduino is reset and programmed with the new code, what you can just edit with vi or your favorite editor locally, no need for any IDEs. I would note that you should close both the C control program, screen or anything else accessing the arduino while uploading, otherwise the port will come back as /dev/ttyACM1 after reset.

Step 3: Closure and Todo List

Although I did create this environmental sensor box for server rooms you can use it for chemistry/electronic labs, warehouses, regular rooms and anything else. And yeah since it's using TCP/IP it's an IoT device, G I should've put that to the title as well to make it more enterprisy :)

You can easily modify both the hardware and software to be able to also turn on the lights in the room automatically. Take a look at my other project: Shadow of phoenix how does that work for light control, you have all the hardware at hand to do the same thing (it uses hold timers to keep the lights on as long as there was motion detected within a time period, if there is motion again a timer will be bumped up).

With the OrangePI running a full stack Armbian the possibilities are limitless, you can create a local web interface written from scratch in php to display historic data on graphs. Isn't this already better that you have a completely Open Source device monitoring your server room what you can be proud of building, if you think so build it yourself!