最近买了个GAY-30光线传感器,这个型号也叫BH1750FVI。结果网上搜了一圈,没人写过Windows 10 IoT驱动这个传感器的例子,只能自己爆一个出来了。
光线传感器外观是这样的:
我用的板子是树莓派3,GPIO布局如下:
因为GAY-30是个I2C设备,所以我们不能用GPIO端口操作,必须用I2C方式去爆。并且因为这个特性,不支持热插拔。必须把树莓派关机,连接好传感器,再开机才能找到设备。这是坑了我很久的一个地方。
连接如下:
GAY-30针脚 | 树莓派端口 | 备注 |
VCC | PIN 01 | 3.3V电源正极 |
ADO | 不接 | 没有什么卵用 |
SDA | PIN 03 | SDA1 I2C |
SCL | PIN 05 | SLC1 I2C |
GND | PIN 06 | 接地线,电源负极 |
然后就可以爆代码了:
先来看看Windows 10 IoT官网操作I2C的代码是这样的
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); } }
这里面有个神器的东西,就是
// 0x40 is the I2C device address var settings = new I2cConnectionSettings(0x40);
这个0x40的地址是怎么得到的呢。。。
经过一番爆破,发现每个I2C硬件用的地址都不一样,一般要到对应硬件的说明文档里找。如果找不到,可以在Raspbian系统里按照这个方法去找:
http://www.raspberrypi-spy.co.uk/2014/11/enabling-the-i2c-interface-on-the-raspberry-pi/
我的这个GAY-30传感器的I2C地址是0x23,如果不出意外,所有这个型号的传感器在连接3.3V电源的时候都应该用0x23.
不多废话,经过一番爆破,贴出完整的GY30LightSensor驱动代码:
代码是参考Ardunio的版本写的。原帖在:http://blog.simtronyx.de/en/measurement-of-illuminance-with-a-bh1750fvi-breakout-board-gy-30-and-an-arduino-uno/
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; } } }
使用方法:
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"; }); }; }
现在当有光线照射到传感器上的时候就可以得到流明(Lx)值了。
我也是刚刚才弄完,还没完整的验证过。我的代码里可能有个坑爹的地方就是Lx的计算。我不确定这个计算方式对不对:
var valf = ((readBuf[0] << 8) | readBuf[1]) / 1.2;
如果有错还请读者指出~