HC-04是树莓派上用的比较广泛的超声波传感器,可以用来测量设备和物体之间的距离。可惜网上的资料很少有针对Windows 10的,几乎都是Python和C在Linux上的例子。

hackster.io上有一篇用HC-04做雷达的例子,但是设计的部件太多了,对单纯学习HC-04传感器的使用来说这个例子比较复杂。搜了一圈,英文资料最好的是这篇:http://www.guruumeditation.net/en/digital-io-with-windows-10-iot-raspberry-pi-2-and-the-ultrasonic-ranging-module-hc-sr04/ 国内中文资料介绍Windows 10上HC-04传感器的文章似乎还没有,所以就简单总结一下步骤。

一、物理连接

对于没有多少物理和电子知识基础的人(比如我)来说,第一步连线是比较难的。很多文章都一笔带过说“这很简单,没啥好说的”,尼玛。。。

其实要解决的问题是这样的:HC-04需要接入5V电源才能工作,它的响应输出是ECHO,这个要接到树莓派的GPIO端口上使用,但是ECHO的输出是5V,GPIO只能接受3.3V左右的输入,5V会烧掉树莓派,所以必须加电阻来爆一下,至于怎么加,我总结了一个最直观的示意图,如下:

简单来说,你需要2个电阻,一个4.7K欧,一个10K欧。为什么要这样呢,是因为根据电压分配定律,如上图所示,4.7K和10K电阻的组合可以得到3.4V的输出。这对GPIO来说是可以接受的。如果你有别的值的电阻可以让输出正好3.3V也是可以的。我在淘宝买的常用电阻包没有办法配出正好3.3V,所以只能用4.7K和10K的组合。(上图的4.3K R1标错了)

然后,传感器和树莓派的连接也可以看上面这张图来连。VCC接树莓派的5V输出,TRIG接GPIO 23,ECHO通过一个4.3K电阻之后接GPIO 24,GND接Ground。

二、代码

爆代码就可以用一个现成的类,这是国外的 http://www.guruumeditation.net/en/digital-io-with-windows-10-iot-raspberry-pi-2-and-the-ultrasonic-ranging-module-hc-sr04/  这篇文章里抄过来的。(稍微改进过)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Gpio;

namespace HC04
{
    public class HC04UltrasonicDistanceSensor
    {
        private GpioPin Pin_Trig, Pin_Echo;
        public double? Distance { get; }

        /// <summary>
        /// Available Gpio Pins. Refer: https://ms-iot.github.io/content/en-US/win10/samples/PinMappingsRPi2.htm
        /// </summary>
        public enum AvailableGpioPin : int
        {
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 29
            /// </summary>
            GpioPin_5 = 5,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 31
            /// </summary>
            GpioPin_6 = 6,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 32
            /// </summary>
            GpioPin_12 = 12,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 33
            /// </summary>
            GpioPin_13 = 13,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 36
            /// </summary>
            GpioPin_16 = 16,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 12
            /// </summary>
            GpioPin_18 = 18,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 15
            /// </summary>
            GpioPin_22 = 22,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 16
            /// </summary>
            GpioPin_23 = 23,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 18
            /// </summary>
            GpioPin_24 = 24,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 22
            /// </summary>
            GpioPin_25 = 25,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 37
            /// </summary>
            GpioPin_26 = 26,
            /// <summary>
            /// Raspberry Pi 2 - Header Pin Number : 13
            /// </summary>
            GpioPin_27 = 27
        }

        public HC04UltrasonicDistanceSensor(AvailableGpioPin trigPin, AvailableGpioPin echoPin)
        {
            var gpio = GpioController.GetDefault();

            Pin_Trig = gpio.OpenPin((int)trigPin);
            Pin_Trig.SetDriveMode(GpioPinDriveMode.Output);
            Pin_Trig.Write(GpioPinValue.Low);

            Pin_Echo = gpio.OpenPin((int)echoPin);
            Pin_Echo.SetDriveMode(GpioPinDriveMode.Input);
        }

        public double GetDistance()
        {
            var mre = new ManualResetEventSlim(false);

            //Send a 10µs pulse to start the measurement
            Pin_Trig.Write(GpioPinValue.High);
            mre.Wait(TimeSpan.FromMilliseconds(0.01));
            Pin_Trig.Write(GpioPinValue.Low);

            var time = PulseIn(Pin_Echo, GpioPinValue.High, 500);

            // multiply by speed of sound in milliseconds (34000) divided by 2 (cause pulse make rountrip)
            var distance = time * 17000;
            return distance;
        }

        private double PulseIn(GpioPin pin, GpioPinValue value, ushort timeout)
        {
            var sw = new Stopwatch();
            var swTimeout = new Stopwatch();

            swTimeout.Start();

            // Wait for pulse
            while (pin.Read() != value)
            {
                if (swTimeout.ElapsedMilliseconds > timeout)
                    return 3.5;
            }
            sw.Start();

            // Wait for pulse end
            while (pin.Read() == value)
            {
                if (swTimeout.ElapsedMilliseconds > timeout)
                    return 3.4;
            }
            sw.Stop();

            return sw.Elapsed.TotalSeconds;
        }
    }
}

使用方法如下:

public sealed partial class MainPage : Page
{
    public HC04UltrasonicDistanceSensor Hc04UltrasonicDistanceSensor { get; set; }
    private DispatcherTimer _timer;

    public MainPage()
    {
        this.InitializeComponent();
        Hc04UltrasonicDistanceSensor = new HC04UltrasonicDistanceSensor(
            HC04UltrasonicDistanceSensor.AvailableGpioPin.GpioPin_23, 
            HC04UltrasonicDistanceSensor.AvailableGpioPin.GpioPin_24);
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        _timer = new DispatcherTimer();

        _timer.Tick += (sender, o) =>
        {
            var distance = Hc04UltrasonicDistanceSensor.GetDistance();
            var s = $"Distance : {distance} cm";
            TxtDistance.Text = s;
            Debug.WriteLine(s);
        };

        _timer.Interval = TimeSpan.FromSeconds(1);

        _timer.Start();
    }
}

简单解释一下:

MainPage里的timer是用来定时每一秒取一次HC-04传感器返回的距离的,这个值可以自己改。这个距离是public double GetDistance()这个方法计算的。计算方法见代码注释。

三、运行