×

在树莓派上搭建VPN、网络服务器和HC SR04超声波距离传感器

消耗积分:0 | 格式:zip | 大小:0.10 MB | 2022-12-16

分享资料个

描述

没有静态IP?没问题。一步一步,我们将在树莓派上搭建VPN、网络服务器和HC-SR04超声波距离传感器。

最终效果演示

 

所需的硬件和软件

  • 树莓派与 Raspbian
  • HC-SR04超声波距离传感器
  • 3x 1kΩ 电阻器(或 1x1kΩ 1x2kΩ)
  • 电缆
  • Husarnet VPN
  • 阿帕奇
  • RPi.GPIO Python 库
  • jQuery(获取 jQuery CDN 的链接)

我正在安装大部分软件。

如果觉得本教程中的代码片段难以阅读,请查看本文的 Github 版本(它们的片段格式更好,支持更多语言)。存储库链接在此文本的底部。

组装电路

pYYBAGOF6qKAPE8FAACXxh4VtZ4677.png
 

如您所见,我们需要使用一些电阻来降低电压,因为高于 3.3V 的电压可能会损坏 RPi,传感器将无法正常工作。

Husarnet VPN

我们的目标是能够从任何地方读取数据,因此我们现在需要设置 VPN。该解决方案更安全,因为我们不会将设备暴露在开放的互联网流量中。

我假设您已经在https://husarnet.com/ 创建了一个帐户。

如果是这样,让我们​​创建一个网络并添加我们的设备。

1.在 RPi 以及其他设备(例如您的计算机)上安装 Husarnet 客户端,以便您稍后可以显示该网站。要安装客户端,请使用以下命令:

curl https://install.husarnet.com/install.sh | sudo bash

安装过程完成后,建议重启Husarnet:

sudo systemctl restart husarnet

2.登录https://app.husarnet.com/,点击创建网络,成功完成该过程后,在网络面板中点击添加元素

3.点击后会弹出一个菜单。如您所见,有很多选项可用于将设备添加到网络。其中最普遍的是使用*加入代码*。复制它并继续到 RPi 上的命令行。

sudo husarnet join  

设置易于记忆的设备名称是个好主意,因为您以后可以使用它们而不是长而难看的 IPv6 地址。

4. 对要添加到网络的所有其他设备执行相同操作。

成功配置的网络应该与此类似:

poYBAGOF6qSAdX4hAAAfygwFRkQ922.png
 

您现在可以通过 ping 设备来测试您的网络是否正常工作。

例如,在我的笔记本电脑设备上,我会运行:

ping6 -c 5 rasp

rasp发送 5 个 ping

传感器软件

所有代码都可以在本页底部列出的我的存储库中找到。

在开始编写代码之前,我们需要安装 RPi.GPIO。这非常简单:

pip3 install RPi.GPIO

互联网上有很多可用于 HC-SR04 的代码示例。

在本例中,我们将使用以下 python3 程序:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
TRIG = 23
ECHO =24

GPIO.setup(TRIG, GPIO.OUT)
GPIO.setup(ECHO, GPIO.IN)
file = '/var/www/html/data.txt'

try:
while True:
    GPIO.output(TRIG,False)
    time.sleep(0.1)
    GPIO.output(TRIG, True)
    time.sleep(0.0001)
    GPIO.output(TRIG, False)

    while GPIO.input(ECHO) == 0:
        pulse_start = time.time()

    while GPIO.input(ECHO) == 1:
        pulse_end = time.time()

    pulse_duration = pulse_end - pulse_start
    dist = pulse_duration * 17150
    dist = round(dist, 2)

    print("distance:", dist, end='\r', flush=True)
    with open(file,'w') as f:
        f.write(str(dist))

except KeyboardInterrupt:
    print("cleanup")
    GPIO.cleanup()

我强烈建议你玩一下 first 的持续时间,time.sleep这样你的刷新率就不错了,而且你不会遇到很多错误。对我来说,~10Hz 的刷新率非常好。

如您所见,我们只是将计算出的距离保存在 Apache 目录中的 .txt 文件中。这是迄今为止我能想到的访问传感器数据的最简单方法,而无需编写精美的后端服务。

要在 Raspberry Pi 上安装和启用 Apache,请执行以下操作:

sudo apt update
sudo apt install apache2 -y

安装后,您可以重新启动 Apache 以确保服务正常运行:

sudo service apache2 restart

执行类似的命令

curl localhost

应该会得到默认的 Apache index.html 页面。

网站

现在我们有了一个可以从中获取数据的工作界面,我们可以继续将其呈现给用户。为此,我们需要 jQuery 和一些基本的 CSS/JS。

检索传感器数据

让我们从从文本文件中动态提取数据开始。假设我们将每秒刷新一次数据。

<script>
    var hostname = document.location.origin;
    var interval = 100; // it's reasonable to set this to the same value as the refresh rate of the sensor
    function get_data() {
        $.ajax({
            url: hostname+"/data.txt",
            type: "GET",
            dataType : "text",
            success: function(data){
                d = document.getElementById('sensor-data');
                d.innerHTML = data;
                bar.set_value(data);
            setTimeout(function(){
            get_data();
            }, interval);
            }
        });
    }
get_data();
script>

我们正在使用 AJAX,它是 jQuery 库的一个组件,用于动态请求和刷新数据,而无需重新加载页面。现在,不要担心从 geElementById 开始的三行,我们稍后会谈到。

我们的函数 get_data 只是向```/data.txt``` 发出请求,这是我们传感器数据的目录。```setTimeout``` 构造设置了一个早先定义的间隔(100ms),之后整个函数逻辑被重复。

显示数据

现在让我们绘制一个进度条。我们将从定义 3 个 div 开始。

<div class="progress-bar">
    <div class="result" id ="sensor-data">div>
    <div class = "progress-bar-fill">div>
div>

我们将快速对其进行样式设置,以便将数据很好地呈现给用户(最后将 css 移动到外部文件中)。

<style>
.progress-bar{
    width: 100%;
    height: 200px;
    border: 1px solid black;
    position: relative;
}
.progress-bar-fill{
    height: 100%;
    background: lightblue;
    transition: width 0.5s;
}
.result{
    position: absolute;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    font-size: 40px;
    font-family: sans-serif;
}
style>

我们现在需要编写一些 Javascript 代码来改变进度条填充 div 的长度。长话短说,我们要更改宽度样式参数(值从 0-100%)。

为此,我们将编写一个名为 ProgressBar 的类(当然是在脚本标签内)。

class ProgressBar{
    constructor(element, intial_value){
        this.value_elem = element.querySelector('.result');
        this.fill_elem = element.querySelector('.progress-bar-fill');
        this.set_value(intial_value);
    }
    set_value(new_value){
        this.value_scaled = new_value * 0.25; //100% of the bar is now 400cm, scaled for better visibility
        //0.25=100% * 1/400
        this.value = new_value;
        if (this.value_scaled < 0) {
            this.value_scaled = 0;
        }
        if (this.value_scaled > 100) {
            this.value_scaled = 100;
        }
        this.update();
    }
    update(){
        const percentage = this.value_scaled + '%';
        this.fill_elem.style.width = percentage;
        this.value_elem.textContent = this.value + 'cm';
    }
}
bar = new ProgressBar(document.querySelector('.progress-bar'), 200);

此类的构造函数将样式元素 (*progress-bar-fill* div) 和初始进度条值作为参数。初始值并不重要,因为它会立即被我们的代码更改。

如果您仔细观察,您会注意到我已经初始化了 ProgressBar 类 ( bar ) 的一个实例。这与之前的片段之一(在get_data函数内)中出现的变量相同。

我们的类有两个方法。第一个是 set_value ,它将以厘米为单位的距离缩放为 % (0-100)。我选择将值缩放到 400cm,即 100%,但理论上你可以一直到 4000cm,因为这是 HC-SR04 的最大范围。第二种方法是更新。这设置了我们 div 的实际宽度。

距离限制

现在让我们通过添加一些动态功能来增强我们的网站 - 浏览器选项卡中的通知和可调整的阈值,它将确定我们是否距离传感器足够远。

首先,我们应该在文档中添加几个 div(就在 *progress-bar* 下方):

<div style="margin-top: 200px; width: 100%; text-align: center">
    

id="info"> Threshold slider

div> <div class="slidecontainer" style="margin-top: 50px;"> "range" min="1" max="100" value="50" class="slider" id="myRange"> div> <div id="slider-value">div>

我还设置了 div 的样式,以便它们在网站上看起来更好。

为避免代码混乱,我们应该拆分文件。创建一个名为style的目录,并在该目录中创建一个文件 slider.css。这是slider.css的代码:

.slidecontainer {
    width: 100%;
}
.slider {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 25px;
    background: #d3d3d3;
    outline: none;
    opacity: 0.7;
    -webkit-transition: .2s;
    transition: opacity .2s;
}
.slider:hover {
    opacity: 1;
}
.slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 25px;
    height: 25px;
    background: #4CAF50;
    cursor: pointer;
}
.slider::-moz-range-thumb {
    width: 25px;
    height: 25px;
    background: #4CAF50;
    cursor: pointer;
}

为了使滑块工作,我们需要添加一些用于动态更新的 Javascript。

var slider = document.getElementById("myRange");
var output = document.getElementById("slider-value");
// Display the default slider value
output.innerHTML = "Current threshold value: " + slider.value*4 + "cm";
// Update the current value
slider.oninput = function() {
    output.innerHTML = "Current threshold value: " + this.value*4 + "cm";
}

我在bar提醒存储库的源文件中提供完整代码)的初始化下添加了此代码。

好的,还有一件事要做 - 浏览器标签通知。在浏览器的顶部,在我们的网站选项卡中,我们希望有类似的内容:favicon:(distance) Page title

要实现动态favicon的效果,我们需要编写一个Javascript函数:

function set_favicon(happy) {
    icon = 'img/happy.ico';
    if (!happy){
    icon = 'img/angry.ico';
}

var link = document.createElement('link');
var old_link = document.getElementById('favicon');

link.id = 'favicon';
link.rel = 'icon';
link.href = icon;

if (old_link){
    document.head.removeChild(old_link);
}

document.head.appendChild(link);
}

我们将有两个可能的网站图标 - 愤怒和快乐(由快乐参数确定)。

您可以从我的存储库下载图标。如您所见,我在/var/www/htmlimg 中创建了一个新目录,用于存储图标。代码非常简单——我们确定要使用哪个图标,我们创建一个链接元素,检查一个是否已经存在并用一个新的替换它。

我们现在需要回到我们的好老朋友那里get_data做一些调整:

function get_data() {
    $.ajax({
        url: hostname+"/data.txt",
        type: "GET",
        dataType : "text",
        success: function(data){
        d = document.getElementById('sensor-data');
        d.innerHTML = data;
        bar.set_value(data);
//changes start here
        if (slider.value*4 > data){
            document.getElementById('info').innerHTML = "Move away";
            document.getElementById('bar-fill').style.backgroundColor = "red";
            set_favicon(false);
        } else{
            document.getElementById('info').innerHTML = "OK";
            document.getElementById('bar-fill').style.backgroundColor = "#4CAF50";
        set_favicon(true);
        }
        setTimeout(function(){
            get_data();
        }, interval);
    }
    });
}

if 语句确定是否超过了阈值,并取决于我们是否被通知(通过一个愤怒的表情符号和一个微妙的后退提示)我们离得太近或被告知我们的距离是合适的。

滑块值乘以 4,因为滑块被设计为具有 0-100 的值(有点像进度条),如前所述,我已确定 400 厘米为最大范围。

最后一点:这是可选的,但看起来很酷——让我们让进度条在超过阈值时改变颜色。这只是两行代码(上面的片段)。

就是这样!

我们现在可以测试我们的网站了。再一次,所有放在一个文件中的 HTML/JS 片段都可以在存储库中使用(get.html文件,记得将其复制到/var/www/html目录)。

在您的 RPi 上,运行读取传感器数据的 python 脚本。在脚本所在的目录中运行:

sudo python3 script.py

现在您可以在您的计算机上打开该网站 -

我们去吧!

poYBAGOF6qiAHnbLAAB9kSsWCec720.png
 
pYYBAGOX69SAX8jsAACE3UdzoSg703.png
 

 


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.lene-v.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'在树莓派上搭建VPN、网络服务器和HC SR04超声波距离传感器',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.lene-v.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);