This post covers the design of a low-cost solar irradiance sensor. This type of sensor is called a ‘pyranometer‘, (from the Greek “pyr” (fire) and “ano” (sky/heaven)) and there are a wide range of units available, typically very expensive, but accurate! I wanted to design a lower cost unit which can be easily interfaced with a microcontroller. I wanted to measure in-plane solar irradiance, so that I could compare the output from solar panels with the light level.

There are quite a few ways of doing this, with the three main techniques being: (1) use a solar cell that is accurately calibrated (a solar cell is also a photo diode) and measure the output current, (2) use a thermopile, where two dissimilar metals are used with a hot and cold side to measure a very small generated voltage from the difference in heat from the solar radiation and (3) use a photo-resistor, which has a resistance that varies with light level. Most highly accurate pyranometers use the thermopile, which have the widest spectral response , but this requires specialist glass covering and highly accurate voltage measurement. So some form of photo diode will probably be the final design.

Available units

First lets have a look at what is already available. These are the main suppliers of laboratory-grade pyranomteres. These are the ones you want if you need high accuracy and have the budget:

The next few here are either designed for hand-held measurements of solar irradiance, for example when checking PV output during installation checks, or they are slightly lower cost sensors:

DIY designs

Here are some plans for DIY pyranometers. Lots of great information here, but there are loads of ideas and plans out there:

Sensors available

To get going I ordered a wide range of different light level sensors. Not all of these were suitable, which I try to highlight here.

BPW21 Silicon Photodiode

This is a ‘metal can’ ambient light sensor from Osram. This is around £10 (!) per unit. It has a large sensor area.

Grove Light Sensor SEN08211P

 This is a Light Dependant Resistor with buffer amplifier. This type of sensor is not really suitable for accurate irradiance sensing. It has a nice ‘Grove’ connector for quick wiring to prototypes.

BPX65 Silicon Photodiode

This is another ‘metal can’ ambient light sensor from Osram. This is around £6.50 in single quantities, so pretty expensive! I used this in my prototype and it worked really well. But maybe too expensive for deployment. This is a silicon based photodiode, so would have a similar response to a silicon solar photovoltaic panel.

Grove Digital Light Sensor SEN10171P

This uses a light-to-digital converter, the TSL2561. This has dual photo diode to cover infra-red and visible light spectrum.

Adafruit TSL2561 Luminosity Sensor

This is a similar break out board to the unit above, but supplied by Adafruit. This is a discontinued product.

BH1750 Light Sensor Module

This is designed for use in mobile electronics to adjust screen brightness. It has an I2C connection so easy to interface with a micro-controller. But this is designed for human eye light level sensing, so outputs lux rather than irradiance

Photosensitive Diode Sensor Module SM29302S

 This is a module using a low-cost photo-diode and an amplifier and a comparator. This means we can set a light level and have a digital trigger. Great for sensing light changes and setting an alarm, but not too useful here. The diode is unspecified so unsure about accuracy and calibration here, so probably not suitable.

SFH203 Photodiode Unfiltered

This is a much lower cost unfiltered photo diode from Osram (code: Q62702-P0955), at around £1.50 per unit. This seems to be a suitable unit with a decent spectral response.

SI1145 UV / IR / visible light sensor from Adafruit

This uses the SI1145 from SiLabs with a calibrated light sensing algorithm that can calculate UV Index. This is great for UV monitoring (for sunlight exposure, for example) but is not really suitable here.

The paper here by Martínez et al gives a good comparison of the sensors that they short listed, which is probably what I should have read in the first place!

Comparison of different photodiodes for solar irradiance sensing.

I decided to use a photo-diode for my unit. This needs correct biasing in order to give a good linear output.
I found some information here:

The main take-away is that the photo diode outputs a current which varies with light level. This can then be biased to ensure a linear voltage output. The third link gives a good review of a simple op-amp circuit to give an output voltage proportional to the photodiode current. This is shown here:

From the third link (Analog Devices) in the above list.

They highlight the main requirements which are: very low input current op-amp (such as JFET or CMOS) and a rail-to-rail op-amp with very low offset voltage. I had both the TLC2252 and the LMC662 types in stock and tried both of them and they both had similar outputs and both worked well, as far as I could see on my ‘scope. The main issue with both of these op-amps is that they need 4.4V or more. I will look at single side CMOS or JFET op amps that will work with 3.3V, so the unit can then be run at 3.3V or 5V which makes it more compatible. I searched and found that the TSV911 is a low cost, single supply op-amp working at 2.5-5.5V and with low bias current and low offset voltage, so this should work!

I made up a prototype circuit, first on breadboard and then (when working) I put it onto strip board. This is not great for noise, but seems to work well for now! I will design the PCB further down the line, when I know this is reading accurately.

The initial prototype with three sample sensors. The ADC and op amp are on the strip board.

Sensor Choice

After review the above samples and various links, I settled on using a photo-diode with a simple transimpedance amplifier. The sensor choice came down to two sensors, which actually both gave decent results. These were the BPX65 and the SFH203 both manufactured by Osram. The spectral response of these two sensors is given below, along with a typical silicon solar PV spectral response.

It can be seen that the BPX65 has a response that is more similar to the silicon PV cell, but both have a relatively good and similar spectral response. The main issue here is that the BPX65 costs in the region of £6.50 each and the SFH203 is around £1.50… I might design this to work with either and then people can choose the sensor for their final application.

Temperature Measurement

In order to correct the unit for any temperature related effects, I need to also measure the temperature of the sensor. I decided to use the DS18B20 1-wire temperature sensor. This provides an accurate temperature, but is quite a big case, so may have a thermal lag. It is also measuring the air temperature, rather than direct on the sensor. But within a couple of degrees I think this might work?

When I started to put the unit together a bit more, then I realised that it might be better to have an I2C sensor on the PCB, rather than a 1-wire unit. I’m already implementing I2C, so why not use this for the temperature sensor. I found the LM75B from NXP. This is a low cost I2C temperature sensor, designed for device temperature monitoring. It’s not amazingly accurate for a direct value, but good for relative change. As long as we can calibrate for this then it should be OK? The PCB design will have one of these IC’s onboard, connected to the I2C bus.

So the LM75B I2C temperature sensor was added to the PCB. This reads temperature in degrees C with 0.5C resolution. To correct the light sensor data we need to know how the output of the light sensor is affected by temperature. This comes from the light sensor datasheet. I have settled on the SFH-203 from OSRAM. This is rated at 25C and has coefficients for voltage and short circuit current. We are using the short circuit current which is measured by the op-amp. The temperature coefficient of the short circuit current is 0.18 % / K. So to compensate the reading for temperature we need to adjust up/down depending upon the temperature. The adjustment is:

Adjustment = (LM75B temperature – 25) * 0.18 %

We then need to apply this factor to our irradiance value:

Real irradiance = Measured Value – Measured Value * (Adjustment / 100)

Lets try and example: We are reading a value of 700 W/m2. But this is at 40C. So our adjustment factor is (40-25) * 0.18 = 2.7%. So we are acutally reading 2.7% above the real value, due to temperature effects. So the real irradiance is 700 – (700 * (2.7/100)) = 681.1 W/m2. Not a big change, but worth keeping in mind. The unit will be out in the sun, so it will get warm which will affect the readings.

This is included in the Arduino code in the github repository below, along with a config file parameter to say if the unit corrects for temperature or not. You could record temperature and measured irradiance and apply the conversion afterwards.


The sensors given here are very sensitive and are likely to be overloaded with too much irradiance. We want the sensor to work in a ‘linear’ region where the output is proportional to the irradiance. Most commercial sensors have diffusers in order to reduce the light reaching the sensor. I read that this is usually made form a standard thickness PTFE, so I ordered some samples on different thickness PTFE (0.25, 0.5 and 1mm). I built a small enclosure to house the prototype sensor and found that the 1mm PTFE reduced the output quite well, although it still reached saturation (in my case, hit the maximum of the op-amp output) with a very bright light close to the sensor (in the region of > 2000W/m2). I want a sensor that can read 0-2000W/m2 range (typical outdoor irradiance values of up to 1300). I will mess around with the PTFE thickness when moving forwards with the prototype.

In the end I needed to use 6mm of PTFE diffuser into a 5mm hole to ensure the unit can read up to 2000 W/m2. I’m looking at decent waterproof outdoor enclosures for this.

One note here: Do NOT cut PTFE on your laser cutter. It will release fluorine gas which is highly poisonous. I cut my sample down to size with a knife and drill. I will look to CNC these if I need to make lots.

After figuring out that about 6mm of PTFE was the right diffuser for this sensor, I looked at different ways of creating the pyranometer sensor dome. I found that a ‘roller castor’ unit (designed to roll things about on) had a nice metal domed shape. Once the roller is removed (along with some bearings that drop out!) I found the dome was pretty good. I cut out a circle of 6mm thick PTFE and then laser cut some spacers and a rubber water proof seal.

I used the opportunity for getting my CNC up and running again, as PTFE cannot be cut on the laser cutter. I designed a 3D model of the diffuser, including a ‘step’ to go through the hole in the dome. This will allow it to align and seal much better. I used Freecad to design the shape, Kiri:Moto to create the g-code for the CNC, Chilipeppr to control the CNC and GRBL as the CNC machine controller. All are open source (I think) and all are free tools. This creates a decent tool chain for CNC milling a 2.5D part.

Validation and Calibration

In order to initially test my prototype, I used another (uncalibrated!) irradiance sensor that I own. I looked at the output values from my sensor and that with the irradiance sensor. This allowed my to create a ‘map’ of sensor values proportional to the irradiance. I then added this to the basic Arduino code to start to give me irradiance readings out. I wanted to test repeatability and also any temperature changes.

*** to do: add this data!!! ***

PCB Design

I designed a PCB layout for this sensor using KiCAD (an open source PCB design package). I started off trying to make a full unit with display and buttons etc. I then decided to strip this back and just make a sensor unit that can communicate via a serial communication. I initially decided to use the ATTiny85 IC, as this is one I am familiar with and have lots of samples lying around! I wanted the sensor to be configurable and either report data back as a stream or as a single value when requested. I also needed to be able to easily calibrate the sensor with simple serial commands. Also I wanted the unit to have a configurable address, so there can be more than one sensor on a serial bus.

Running I2C on the ATTiny85 requires a few additional bits of information, as given in the following links:

The main point is that the ATTiny85 does not have a built in I2C controller – you need to configure the Universal Serial Interface. It seems like the most used library for this is the Adafruit version of TinyWire, which is TinyWireM. I added this to my code and it worked first time! You do need to ensure your SDA and clock signals go to the correct pins, as per the schematic for this project.

I also need to service serial requests from the monitoring device. This needs to implement a software serial port. Here are some posts on getting started with software serial on the ATTiny85:

I’m using the internal 8MHz oscillator, so timing for high speed serial will not be great. I am limiting the baud rate here to 9600 only, to stop any communication timing issues. Using the Arduino SoftwareSerial library seems to just work. I wired this up and tried it out. There is not much memory on the ATTiny85, so I need to be careful with serial strings and ensure I am efficient with code! I added SoftwareSerial and connected to a USB-Serial converter at 9600 baud and I could see data sent from the ATTiny85 out via the serial port. This is a good step!

I needed to sleep most of the time to save a bit of power. But I want to wake up either on a watch dog timer (for regular reading of the sensor) or if any data comes in on the serial port. I had done code to enable the watch dog timer before, so that was quite easy (see code example), but I also needed to have an interrupt pin on the Rx pin of the SoftwareSerial. This means data into the unit will wake it up, as it will trigger the external interrupt. I needed to set up an interrupt on pin D3 and have an interrupt service routine that services that interrupt.

I mainly used the information here ( relating to interrupts on the ATTiny85. This explains that INT0 (pin PB2/D2) can be used as an external interrupt. But pins PB0, PB1, PB2, PB3, PB4 and PB5 can all be configured to be a pin-change interrupt, which can be triggered by change in state of an external source. We need to ensure the Global Interrupt Enable is set within SREG. We also need to set the General Interrupt Mask Register is correctly configured, to enable PCIE (pin change). We need to set the PCMSK : Pin Change Mask Register so it knows that we want a pin-change interrupt on pin PB3. Everything shares the same ISR, so we will need to check why the unit was triggered (either watch dog timer or interrupt) and service the request correctly.

Maybe this already happens with SoftwareSerial which uses pin change interrupts? Yes – it does. Basically the SoftwareSerial will interrupt when data is received. This means it already wakes up and we don’t need to do anything, just put it to sleep again after processing a serial command.

I managed to get nearly everything working on the ATTiny85, but the 8kB code limit stopped me implementing some of the more useful features (such as calibration curves and different baud rates). So I upgraded the design to use the ATMega328 (the same as used in the Arduino Nano/Uno). This gives me a whopping 32kB to play with! I needed to change the circuit board from a circular one to a square one, but its the same diameter (45mm). I managed to cram the additional components on, including the crystal oscillator required.

The firmware will be very similar to the ATTiny85 version, but I have more space to do things, such as implement floats for data conversion, have more commands for adjusting the unit, run at different and much higher baud rates (up to 57600 for an 8MHz crystal and operating at 3.3V), and also implement much more accurate calibration curves to convert the sensor reading into the irradiance output. The firmware will be updated when I have some PCB samples to play with!

For long distance cables this should really be changed to an RS485 serial communication. This is a relatively simple add on with a MAX485, but this needs thought about the power supply voltage. To start with I’ll just get a serial unit designed.

Information Repository

All the PCB design and Arduino code is held in the github repository here:

2 responses to “Low-Cost DIY Solar Irradiance Sensor

  1. Hello,
    very interesting and informative.
    I am not able to understand for you are converting the ADC value of the sensor to Wm-2 value. Can you please elaborate?

    1. Hi,
      That is a good question and I do not answer it in my blog post yet!
      The problem is that we need to calibrate the ADC reading to W/m2.
      I have done this with a cheap irradiance sensor, but I’d like to do it on better equipment (and I’m trying to work on that!).
      So we get a voltage from the sensor which is then converted into the ADC reading. This ADC reading needs conversion into W/m2. I think this is quite linear, but it might not be.
      At the moment I dont know the exact conversion. It will depend upon the sensor itself, how well the sensor aligns with the apparture (hole) for light coming in, the size of the appature and also the thickness of the PTFE diffuser.
      Its a work in progress!
      I will try and keep the article updated as I find out more and do more testing.
      Best of luck with your projects!

Leave a Reply

Your email address will not be published.