I purchased a GY-30 light sensor recently, this model is also known as "BH1550FVI". There seems no sample for this sensor on Windows 10 yet, so I tried to make one. The light sensor looks like this:
My board is a Raspberry Pi 3, the GPIO layout is shown in this table:
Because GY-30 is an I2C device, so we can not use GPIO to drive it directly, we must use the I2C ports. In addition, because of it's an I2C device, it does not support hot plug. We should power off the Raspberry Pi, connect the sensor and then power on the Raspberry Pi, so that the system can identify the sensor.
The physical connections are:
- VCC - PIN01 - 3.3V
- ADO - [NONE] - Not using
- SDA - PIN 03- SDA1 I2C
- SCL - PIN 05 - SCL1 I2C
- GND - PIN 06 - Ground
And then we could start coding. The official sample of I2C operations on Windows 10 IoT website is this:
I2C
Pin 3 - I2C1 SDA
Pin 5 - I2C1 SCL
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;
public async void I2C()
{
// Get a selector string for bus "I2C1"
string aqs = I2cDevice.GetDeviceSelector("I2C1");
// Find the I2C bus controller with our selector string
var dis = await DeviceInformation.FindAllAsync(aqs);
if (dis.Count == 0)
return; // bus not found
// 0x40 is the I2C device address
var settings = new I2cConnectionSettings(0x40);
// Create an I2cDevice with our selected bus controller and I2C settings
using (I2cDevice device = await I2cDevice.FromIdAsync(dis[0].Id, settings))
{
byte[] writeBuf = { 0x01, 0x02, 0x03, 0x04 };
device.Write(writeBuf);
}
}
There's one thing interesting in the code, that is:
// 0x40 is the I2C device address
var settings = new I2cConnectionSettings(0x40);
So how do we get this address of "0x40"? I did some research and found every I2C devices are using different addresses. Generally, you will find it in their product manual. If you can not find it, you can use this method in Raspbian system:
http://www.raspberrypi-spy.co.uk/2014/11/enabling-the-i2c-interface-on-the-raspberry-pi/
My GY-30 sensor's I2C address is 0x23, if I am right, every GY-30 sensor that connecting using DC3.3v power will have the address of 0x23.
My complete code for the GY30LightSensor (This code is referring the Ardunio version here):
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;
namespace GY30LightSensor
{
public class GY30LightSensorEventArgs : EventArgs
{
public int? Lux { get; set; }
public GY30LightSensorEventArgs(int? lux)
{
Lux = lux;
}
}
public class GY30LightSensor
{
public int Bh1750Address => 0x23;
public I2cDevice I2CLightSensor { get; private set; }
private Timer PeriodicTimer { get; set; }
public int TimerIntervalMs { get; set; }
public event ReadingEventHandler Reading;
public delegate void ReadingEventHandler(object sender, GY30LightSensorEventArgs e);
private void OnReading(int lux)
{
Reading?.Invoke(lux, new GY30LightSensorEventArgs(lux));
}
public GY30LightSensor(int timerIntervalMs = 100)
{
TimerIntervalMs = timerIntervalMs;
}
public async Task InitLightSensorAsync()
{
string aqs = I2cDevice.GetDeviceSelector();
/* Get a selector string that will return all I2C controllers on the system */
var dis = await DeviceInformation.FindAllAsync(aqs);
/* Find the I2C bus controller device with our selector string */
if (dis.Count == 0)
{
throw new FileNotFoundException("No I2C controllers were found on the system");
}
var settings = new I2cConnectionSettings(Bh1750Address)
{
BusSpeed = I2cBusSpeed.FastMode
};
I2CLightSensor = await I2cDevice.FromIdAsync(dis[0].Id, settings);
/* Create an I2cDevice with our selected bus controller and I2C settings */
if (I2CLightSensor == null)
{
throw new UnauthorizedAccessException(string.Format("Slave address {0} on I2C Controller {1} is currently in use by " +
"another application. Please ensure that no other applications are using I2C.", settings.SlaveAddress, dis[0].Id));
}
/* Write the register settings */
try
{
I2CLightSensor.Write(new byte[] { 0x10 }); // 1 [lux] aufloesung
}
/* If the write fails display the error and stop running */
catch (Exception ex)
{
Debug.WriteLine("Failed to communicate with device: " + ex.Message);
throw;
}
PeriodicTimer = new Timer(this.TimerCallback, null, 0, TimerIntervalMs);
}
private void TimerCallback(object state)
{
var lux = ReadI2CLux();
OnReading(lux);
}
private int ReadI2CLux()
{
byte[] regAddrBuf = new byte[] { 0x23 };
byte[] readBuf = new byte[2];
I2CLightSensor.WriteRead(regAddrBuf, readBuf);
// is this calculation correct?
var valf = ((readBuf[0] << 8) | readBuf[1]) / 1.2;
return (int)valf;
}
}
}
Here is how to use it:
public GY30LightSensor Gy30LightSensor { get; set; }
public MainPage()
{
this.InitializeComponent();
Gy30LightSensor = new GY30LightSensor();
Loaded += OnLoaded;
}
private async void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
await Gy30LightSensor.InitLightSensorAsync();
Gy30LightSensor.Reading += (o, args) =>
{
var task = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
TxtLux.Text = args.Lux + " lx";
});
};
}
Now, if you have some light shining on the sensor, you will get Lx value.
P.S. I have not verified if the calculation of the Lx is correct:
var valf = ((readBuf[0] << 8) | readBuf[1]) / 1.2;
Original Blog Post (Chinese) on my blog:
http://edi.wang/post/2016/3/26/windows-10-iot-gy-30-light-sensor
Comments