UV Meter

UV is important to reptiles (and people) in processing vitamin D3 which is needed to absorb calcium. Without it reptiles often suffer from Metabolic Bone Disease (MBD) which leads to weak bones and can be fatal.  Thankfully, pet shops have the answer, UV producing globes which can stave off the dreaded MBD.  These are in the form of T8 florescent tubes or compact florescent globes and go for $50+ each.  And, yeah, they need to be replaced periodically.    How do I know if I replacing a globe while it is still producing enough UV?  How do I know I haven’t waited too long?  The standard wisdom is to replace them every 6 to 12 months which can add up across multiple tanks not to mention the environmental impact of whatever goes in to making them.

Or, I can measure it.  UV meters sell for $400 but I thought I might be able to do something myself.

I had an Arduino Uno R3 and a TFT touchscreen lying around so I picked up a UV sensor and gave it a go.


The sensor

There are quite a few UV sensors around, most with a narrow frequency response.  While the wavelength of vitamin D3 absorption is around 300nm (UVB), UVA is also important for reptiles.  For this reason and to avoid missing the relatively narrow peaks in the output of commercial UV lamps I decided to go with the  ML8511 which seems to have the broadest wavelength band of the UV sensors I could find.  Reminder: Always read the datasheet, ideally before you buy it or you could be disappointed


ML8511 frequency response


Example UV lamp output (Exo Terra : Exo Terra CF lamp)

It produces an analog voltage relative to the UV intensity detected.  It is then easy enough to map the voltage to an intensity after sampling the analog voltage a few times to smooth it.  Some code was reused from a DFrobot example.

A quick walk outside and I could see the intensity to go from 0 to 3 mW/cm^2 which seemed reasonable for a mostly sunny winter’s morning in Melbourne.

Breakout for ML8511 from my usual go-to : ML8511 from core electronics


I used an Arduino UNO R3 clone and wired the sensor:

  • Power : 5V
  • Ground : Ground
  • Analog Output : A4 input.  (to avoid clashing with A0 to A3 which can be used by some of the display/keypad shields)

I soldered (well actually my friend did who has much steadier hands) it straight to the Arduino board to avoid the extra thickness of a prototype board.



Adafruit make these cool TFT resistive touchscreen shields for Arduino and I couldn’t resist buying one.  The screen update is pretty slow (visible flickering) when used with SPI bus but works really nicely in this project when I only redraw elements when they change.

The library provided with the screen and touch sensor work like a charm so integration was pretty straight forward.  It provides basic drawing functions like line, circle, rectangle and text but you need to build text box and button classes yourself if you want to simplify building a UI.

Adafruit TFT resistive touch screen available here.




The intensity varies (by the inverse square law) over distance so an intensity without a distance is not meaningful when judging to the output of the globe.  Instead of being forced to measure a distance each time I decided to measure the output right at the globe which involves sticking the device up and into each cage.  To avoid having to read off the screen while performing a yoga move I made the main output an output of the max output registered so I can put the device in the cage up to the globe and then pull it out to get a measurement.  I included a smaller instantaneous output and voltage output as well.  To reset the captured measurement I hit reset button on the screen.

The Max and Instantaneous values are displayed in a color indicating if they are in the range expected of a “good” globe.  Green for good, yellow for reduced and red for “its dead Jim”.

The tubes provide light across the length of the tube while the CFs provide a more concentrated source so the expected output for each is different at the globe.  To account for this the user can select CF or TUBE to change the assessment thresholds.

Finally, the zero mW/cm2 point seems to fluctuate a little (~0.02V) so I also include a calibration button which set the current instantaneous measurement to “0.0 mW/cm2”.



The Code

It is interesting writing Arduino code as it is C++ without the std template library (for memory reasons) so we have classes but no built in container classes for example.

It is a pretty simple application so I didn’t get too carried away.  I created a textbox class and then extended that to a button and provided just the visual customisation I needed (background colour, text colour & size, textbox size, textbox position).  I extended that for buttons with a simple isIn function for when the screen is touched and a selected flag to give it two states.

Check out the code here: UVMeter on GitHub

I update the display at 2Hz but only redraw objects that have changed, this avoids the flickering you get if you redraw objects each time.  Note, if a text value (i.e. a numerical display) is changing you need to redraw the background before rewriting the text as the text just redraws over the top leading to a mess of drawn pixels – hence a lot of text boxes with black background.

What is left

The device is not in a case yet and I currently power the Arduino with a mobile phone power pack.  I hope to build a case with a battery pack with 3D printing but it could take a while to get to the top of the list so if anymore is keen to do it let me know and I’m happy to be “first to be second” as they say.


This turned out to be pretty straight forward and useful.  Most of the time on the project will be in building a case for it!