在很多楼道中,声控灯的应用很广泛,拍一下手就可以开灯。现在有了树莓派3和Windows 10 IoT,要自己实现一个声控灯也很简单。
我买的声音传感器型号是FC-04,这个网上Windows 10 IoT的资料几乎没有,不过根据它的电器说明还是很容易琢磨出来用法的。和其他传感器类似,首先第一步是校准。
一、校准FC-04声音传感器
在安静的环境中将VCC接入树莓派的DC 3.3输出v,GND接入电源负极(Ground),这时候电源指示灯会亮起,证明你买到的FC-04没有爆掉。
如果你的开关指示也亮起,是正常现象,说明声音太灵敏,在安静的环境中也识别出了声音,需要校准。
用指甲或者螺丝刀慢慢旋转蓝色菊花的菊花芯子,一边旋转一边对着传感器头部的麦克风说“微软大法好”,直到开关指示熄灭的一瞬间,那个阈值就是介于你的安静环境(基准)和有声音(你说话)之间正正好好的值。
这时候你可以试试发出别的声音,比如“FARK”,“XIET”,或者拍手,开关指示灯在有声音的情况下应该会闪动,证明校准完成。
二、连接树莓派
这个传感器有2个输出,DO和AO,如果你买到的FC-04只有一个输出也没问题,用法完全一样。这里我只用到DO,意思是数字输出。
前面第一步已经把VCC和GND接好了,剩下的就是把DO连接到树莓派的一个GPIO端口上,我用的是GPIO 06。
然后还有个灯要连,随便搞个LED小灯,把短脚(负极)接到GROUND,和FC-04的GROUND共用一条线路,把LED的长脚(正极)连接到树莓派的一个GPIO端口上,我选择的是GPIO 05。
注意,很多LED的例子里接法和我是相反的,比如微软Windows 10 IoT的第一个例子 Blinky 里面是通过向LED输出低电平(控制短脚)来开关LED的,我个人比较喜欢输出高电平表示LED亮起,低电平表示熄灭,所以我才把长脚接到GPIO上去。
三、爆代码
首先,我们有2个接在GPIO上的元器件,FC-04和LED,所以爆2个属性出来,代表他们的GPIO针脚。
打开MainPage.xaml.cs,把这两行代码敲进去
public GpioPin LedPin { get; set; } public GpioPin SoundPin { get; set; }
然后还有个变量保存LED的开关状态:
public bool IsLightOn { get; set; }
接下来注册Loaded事件:
public MainPage() { this.InitializeComponent(); Loaded += OnLoaded; }
初始���GPIO控制器
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs) { var controller = GpioController.GetDefault(); if (null != controller) { } }
LED是高电平输出表示开,所以第一次启动的时候要关灯,输出低电平
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs) { var controller = GpioController.GetDefault(); if (null != controller) { LedPin = controller.OpenPin(5); LedPin.SetDriveMode(GpioPinDriveMode.Output); LedPin.Write(GpioPinValue.Low); // set light to off at start up } }
FC-04的GPIO 06端口是接受信号的,所以是输入
SoundPin = controller.OpenPin(6); SoundPin.SetDriveMode(GpioPinDriveMode.Input);
然后问题就是怎么检测有没有输入了。主要有两种做法,一种是通过事件:
// // Summary: // Occurs when the value of the general-purpose I/O (GPIO) pin changes, either because // of an external stimulus when the pin is configured as an input, or when a value // is written to the pin when the pin in configured as an output. public event TypedEventHandler<GpioPin, GpioPinValueChangedEventArgs> ValueChanged;
一种是拿个DispatcherTimer定时去查询状态。这里我用事件来做,因为Timer并不能实时监测声音传入,interval大了,灯会变迟钝,只有连续的声音才能启动和关闭。如果你把interval调成很小,那么你的灯炮开关会在很短的时间内完成启动和关闭,基本没法用。
事件处理代码如下:
SoundPin.ValueChanged += (pin, args) => { var pinValue = SoundPin.Read(); if (pinValue == GpioPinValue.Low) { Debug.WriteLine("Sound Detected!"); LedPin.Write(IsLightOn ? GpioPinValue.Low : GpioPinValue.High); IsLightOn = !IsLightOn; } };
这里有个技巧,args参数自带的args.Edge不要去用,它和Read()方法得到的值不是一个意思,不能判断有没有声音的。最实时的做法就是用ValueChanged事件+Read()。
然后因为FC-04检测到声音时候会在DO输出低电平,所以我判断的是pinValue == GpioPinValue.Low。开灯关灯的代码很直白,没啥好说的。
最后完整代码如下:
using System.Diagnostics; using Windows.Devices.Gpio; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace SoundLight { public sealed partial class MainPage : Page { public GpioPin LedPin { get; set; } public GpioPin SoundPin { get; set; } public bool IsLightOn { get; set; } public MainPage() { this.InitializeComponent(); Loaded += OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs routedEventArgs) { var controller = GpioController.GetDefault(); if (null != controller) { LedPin = controller.OpenPin(5); LedPin.SetDriveMode(GpioPinDriveMode.Output); LedPin.Write(GpioPinValue.Low); // set light to off at start up SoundPin = controller.OpenPin(6); SoundPin.SetDriveMode(GpioPinDriveMode.Input); SoundPin.ValueChanged += (pin, args) => { var pinValue = SoundPin.Read(); if (pinValue == GpioPinValue.Low) { Debug.WriteLine("Sound Detected!"); LedPin.Write(IsLightOn ? GpioPinValue.Low : GpioPinValue.High); IsLightOn = !IsLightOn; } }; } } } }
别忘了在你的XAML中加入纳德拉的头像,保证DEMO时候得到微软大法的保佑,不容易爆。
四、运行
启动工程之后,拍手,开灯。再拍手,关灯。
拍手时候FC-04的开关指示会亮起: