出于学习的需要,配置了一台测试环境用机,铭瑄N3160,配置了一张8111千兆为网卡,这主板板载百兆,不过我的出口只有50Mbps,所以板载口作为WAN口,8111作为LAN口。
我这里已经做好了,所以会和大家操作的有所不同。

创建虚拟机

PVE的安装在这里不再赘述。
由于我的机器不支持VT-D,所以没有写直通,不过也够用了。
首先创建一个虚拟机,在左边选中我们的数据节点,右键新建虚拟机:
212717.jpg

213110.jpg

这里一步一步来:

  • 一般:填写名称,勾选开机自启动,VM ID可指定也可默认
  • 操作系统:不使用任何介质
  • 系统:显卡选择VMWare兼容
  • 硬盘:默认,反正后面会删除
  • CPU:按需分配,这里有CPU权重,就是优先级
  • 内存:按需分配,1G足够
  • 网络:默认vmbr0,模型选择virtio半虚拟化

最后确认创建即可。

创建好后点击我们的虚拟机,点击硬件:

213926.jpg

点击我们新建的硬盘,点击分离它会变成未使用磁盘,再点击它删除,将这个硬盘删除掉。
将我们准备的Openwrt的镜像通过WinSCP上传到我们的PVE上,并执行:

qm importdisk <VM ID> <镜像名称> local-lvm

等完成后回到硬件界面,会发现多了一个未使用磁盘,双击它,直接点击添加,再然后来到选项,双击引导顺序,只选择我们的硬盘。

214823.jpg

这样,虚拟机的配置告一段落。

网络配置

回到服务器节点,点击网络:

215034.jpg

点击创建选择LinuxBridge,名称默认,桥接端口填写物理网卡名称,比方说enp3s0:

215313.jpg

这样,PVE上就有了两个虚拟交换机(网桥),其中默认就有的vmbr0我们作为LAN交换机,新建的vmbr1作为WAN交换机。

我们双击默认交换机vmbr0,更改里面的管理IP与网关,改为我们之后Openwrt的网段,比方说192.168.1.x。

215918.jpg

配置完成后返回Openwrt虚拟机,来到硬件,点击添加,添加网络设备:

220141.jpg

这里分别添加vmbr0与vmbr1,模型选择virtio半虚拟化(更新:不要选E1000,不然性能不行),之后选择节点,点击右上角的重启来生效。

后续配置

连接网线到vmbr0所绑定的网口上。
打开 控制面板\网络和 Internet\网络连接,右键属性,互联网协议4,属性,填写与PVE和将要配置的Openwrt同一网段。
在上面重启后,我们输入PVE的管理IP,回到我们的虚拟机,点击控制台,在里面配置我们的WAN口与LAN口,你可以输入ifconfig来查看端口的MAC地址,再根据硬件选项卡中网络设备所对应的MAC地址来判断,之后在控制台中按照常规配置WAN口与LAN口即可,之后可以通过网页来配置把玩Openwrt。

221032.jpg

后续工作

之后需要配置BIOS来电自启,我这里工作日晚上会断电,所以还要配置提前关机。

(/etc/crontab)
30 22 * * * root /sbin/shutdown -h now

配置单

  • 主板:铭瑄N3160
  • 内存:金士顿DDR3 1333 4GB (这板子挑内存条,手头上的内存条没办法组双通道)
  • 存储:旧的东芝500GB硬盘 (反正对速度没有需求)
  • 网卡:PCIE RTL8111
  • 预计功耗:25w

由MaxMind提供,有ASN,国家与市三种类型,支持IPv4与IPv6,使用mmdb或CSV格式分发。
注意:自2019年12月30起,需要注册账号下载。
https://dev.maxmind.com/geoip/geoip2/geolite2/

Python的使用

安装官方提供的模块:

pip3 install geoip2

示例:

import geoip2.database
gi = geoip2.database.Reader('GeoLite2-Country.mmdb')
gn = gi.country('1.1.1.1')
#city,country,asnisp
#print(gn)
print(gn.country.names['zh-CN'])
print(gn.country.iso_code)
#输出:
澳大利亚
AU

Nginx的使用

Nginx需要编译Geoip2模块(有Geoip模块,但只适用旧版的dat格式)。
https://github.com/leev/ngx_http_geoip2_module

(http)
geoip2 /etc/nginx/geoip2/GeoLite2-Country.mmdb {
    #自动重载
    auto_reload 5m;
    $geoip2_metadata_country_build metadata build_epoch;
    #国家代码
    $geoip2_country_code default=US country iso_code;
    #国家名称
    #$geoip2_country_name country names zh-CN;
}
if($geoip2_country_code != CN) {
    deny all;
}

注意替换example.com与证书路径!
配置如下:

server {
    listen [::]:443;
    listen 443;
    server_name duckgo.example.com;
    ssl on;
    ssl_certificate /root/ssl_cert/duck.crt;
    ssl_certificate_key /root/ssl_cert/duck.key;
    sub_filter_types text/html text/css application/x-javascript text/xml;
    access_log off;

    location / {
        proxy_pass https://duckduckgo.com;
        proxy_set_header Host duckduckgo.com;
        proxy_set_header Accept-Encoding "";
        proxy_set_header Referer "";
        sub_filter "duckduckgo.com" "duckgo.example.com";
        sub_filter_once off;
    }
}

server {
    listen [::]:443;
    listen 443;
    server_name images.duckgo.example.com;
    ssl on;
    ssl_certificate /root/ssl_cert/duck.crt;
    ssl_certificate_key /root/ssl_cert/duck.key;
    access_log off;

    location / {
        proxy_pass https://images.duckduckgo.com;
        proxy_set_header Host images.duckduckgo.com;
    }
}

server {
    listen [::]:443;
    listen 443;
    server_name icons.duckgo.example.com;
    ssl on;
    ssl_certificate /root/ssl_cert/duck.crt;
    ssl_certificate_key /root/ssl_cert/duck.key;
    access_log off;

    location / {
        proxy_pass https://icons.duckduckgo.com;
        proxy_set_header Host icons.duckduckgo.com;
    }
}

server {
    listen [::]:443;
    listen 443;
    server_name ac.duckgo.example.com;
    ssl on;
    ssl_certificate /root/ssl_cert/duck.crt;
    ssl_certificate_key /root/ssl_cert/duck.key;
    access_log off;

    location / {
        proxy_pass https://ac.duckduckgo.com;
        proxy_set_header Host ac.duckduckgo.com;
    }
}

server {
    listen [::]:443;
    listen 443;
    server_name external-content.duckgo.example.com;
    ssl on;
    ssl_certificate /root/ssl_cert/duck.crt;
    ssl_certificate_key /root/ssl_cert/duck.key;
    access_log off;

    location / {
        proxy_pass https://external-content.duckduckgo.com;
        proxy_set_header Host external-content.duckduckgo.com;
    }
}
server {
    listen [::]:443;
    listen 443;
    server_name improving.duckgo.example.com;
    ssl on;
    ssl_certificate /root/ssl_cert/duck.crt;
    ssl_certificate_key /root/ssl_cert/duck.key;
    access_log off;

    location / {
        proxy_pass https://improving.duckduckgo.com;
        proxy_set_header Host improving.duckduckgo.com;
    }
}

引入

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/lrsjng.jquery-qrcode/0.18.0/jquery-qrcode.min.js"></script>

快速使用

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" name="viewport" content="width=device-width,initial-scale=1" />
    <title>QRCode</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/lrsjng.jquery-qrcode/0.18.0/jquery-qrcode.min.js"></script>
</head>
<body>
    <div id="qrcode_demo"></div>
    <script>
        $(function(){
            $('#qrcode_demo').qrcode({text:"https://blog.yeziruo.cn/"});
        });
    </script>
</body>
</html>

参数

render: "table" 或 "canvas"(默认)
text: 文本内容
width: 256 宽
height: 256 高
background: "#ffffff" 背景颜色
foreground: "#66ccff" 前景颜色
typeNumber: -1 计算模式(暂不清楚作用)
correctLevel: QRErrorCorrectLevel.H 纠错等级(不建议指定,否则将不会渲染)
    QRErrorCorrectLevel.L,  (7%)
    QRErrorCorrectLevel.M, (15%)
    QRErrorCorrectLevel.Q, (25%)
    QRErrorCorrectLevel.H, (30%)

中文支持

使用该插件生成二维码,如果内容包含中文,则需要将Unicode(UTF-16)转为UTF-8。

function utf16to8(str) {
    var out, i, len, c;
    out = "";
    len = str.length;
    for(i = 0; i < len; i++) {
        c = str.charCodeAt(i);
        if ((c >= 0x0001) && (c <= 0x007F)) {
            out += str.charAt(i);
        } else if (c > 0x07FF) {
            out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
            out += String.fromCharCode(0x80 | ((c >>  6) & 0x3F));
            out += String.fromCharCode(0x80 | ((c >>  0) & 0x3F));
        } else {
            out += String.fromCharCode(0xC0 | ((c >>  6) & 0x1F));
            out += String.fromCharCode(0x80 | ((c >>  0) & 0x3F));
        }
    }
    return out;
}

缘由

在上一篇文章中,我使用了Python来控制我的键盘,但我的OBS好像有时有反应,有时没反应,所以从抽屉中找到了5年前买的Digispark ,用它来做一个硬键盘。
不得不说幸好当初买了两块,有一块被我改错熔丝锁住了。

Arduino环境部署

文件>首选项>附加开发板管理器网址添加如下:

http://digistump.com/package_digistump_index.json

工具>开发板>开发板管理器中找到Digispark后安装即可。

原理图

  • 由于Digispark只有6个IO,且有2个IO用作USB通信,所以最后只剩下4个可用IO(如果你没有改过熔丝位,那就是3个,P5不可用)。
  • 为了塞下5个按钮,使用了ADC(P5/A0)来检测按键按下。
  • 该板P1接板载红色LED。

IMG114153.png

从KEY1到KEY5的ADC值为(VCC=5v):0|509|520|530|540。

代码

#include "DigiKeyboard.h"

void setup() {
  pinMode(1,OUTPUT);
}

//void button(int key_a,int key_b){}
void loop() {
  int AnalogValue = analogRead(A0);
  if(AnalogValue == 0){
    digitalWrite(1,HIGH);
    DigiKeyboard.sendKeyStroke(KEY_F1,MOD_CONTROL_RIGHT);
    delay(200);
  }
  if(508 <= AnalogValue && AnalogValue <= 512){
    digitalWrite(1,HIGH);
    DigiKeyboard.sendKeyStroke(KEY_F2,MOD_CONTROL_RIGHT);
    delay(200);
  }
  if(518 <= AnalogValue && AnalogValue <= 522){
    digitalWrite(1,HIGH);
    DigiKeyboard.sendKeyStroke(KEY_F3,MOD_CONTROL_RIGHT);
    delay(200);
  }
  if(528 <= AnalogValue && AnalogValue <= 532){
    digitalWrite(1,HIGH);
    DigiKeyboard.sendKeyStroke(KEY_F4,MOD_CONTROL_RIGHT);
    delay(200);
  }
  if(538 <= AnalogValue && AnalogValue <= 542){
    digitalWrite(1,HIGH);
    DigiKeyboard.sendKeyStroke(KEY_F5,MOD_CONTROL_RIGHT);
    delay(200);
  }
  digitalWrite(1,LOW);
  DigiKeyboard.delay(200);
}

成品

IMG103157.jpg

网上有使用PyMouse,PyKeyboard,PyUserInput(前两者的整合,不活跃)的,但发现我并不适用,Pip都装不上,所以寻着PyUserInput的Readme文件找到了Pynput这个库。
这是一篇水文章。

pip install pynput

键盘

from pynput.keyboard import Key,Controller
keyboard = Controller()

#dir(Key) 功能键
#['__class__', '__doc__', '__members__', '__module__', 'alt', 'alt_l', 'alt_r', 'backspace', 'caps_lock', 'cmd', 'cmd_r', 'ctrl', 'ctrl_l', 'ctrl_r', 'delete', 'down', 'end', 'enter', 'esc', 'f1', 'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 'f16', 'f17', 'f18', 'f19', 'f2', 'f20', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'home', 'insert', 'left', 'media_next', 'media_play_pause', 'media_previous', 'media_volume_down', 'media_volume_mute', 'media_volume_up', 'menu', 'num_lock', 'page_down', 'page_up', 'pause', 'print_screen', 'right', 'scroll_lock', 'shift', 'shift_r', 'space', 'tab', 'up']

#按下按键
keyboard.press(Key.space)
#释放按键
keyboard.release(Key.space)
#等待按下按键
with keyboard.pressed(Key.shift):
    keyboard.press('a')
    keyboard.release('a')

鼠标

from pynput.mouse import Button,Controller
mouse = Controller()
#读取位置
print(mouse.position)
#设置一个位置
mouse.position = (1926,2020)
#相对移动
mouse.move(88,-88)
#鼠标按键
mouse.press(Button.left)
mouse.release(Button.left)
#双击
mouse.click(Button.left,2)
#滚轮(向下滚动2格)
mouse.scroll(0,2)

另有监控键盘和鼠标的官方例程,不过我用不上,所以就不复制到文章里了:

https://pynput.readthedocs.io/en/latest/mouse.html#monitoring-the-mouse
https://pynput.readthedocs.io/en/latest/keyboard.html#monitoring-the-keyboard

应用

可以写一个Web宏键盘:
Screenshot_2020-07-25.jpg

安装

多数的文章都是从头编译安装的,但是自Nginx 1.9(具体不知道)后,插件可以热加载,不再需要一开始就要编译入Nginx中,使用我们可以使用包管理器来快速安装:

(Debian/Nginx) sudo apt-get install nginx-full libnginx-mod-rtmp -y

配置

打开/etc/nginx/nginx.conf的配置文件,在末尾加入:

rtmp {
    server {
        listen 1926;
        chunk_size 4096;
        application live {
            #开启直播
            live on;
            #开启HLS
            #hls on;
            #HLS视频流目录
            #hls_path /var/www/html/live;
            #每个视频片段长度
            #hls_fragment 5s;
            #播放列表长度
            #hls_playlist_length 15s;
            #连续模式
            #hls_continuous on;
            #删除多余流片段
            #hls_cleanup on;
            #嵌套模式
            #hls_nested on;
        }
    }
}

若需要HLS,则需要在HTTP配置文件中添加:

#root /var/www/html/;
#这里的路径应与上方视频流目录一致
location /live {
            types {
                    application/vnd.apple.mpegurl m3u8;
                    video/mp2t ts;
            }
            alias /var/www/html/live;
 }

推流

可以使用FFmpeg,但我这里使OBS

rtmp://<your_device_ip>:1926/live

HLS

将以下HTML保存到服务器,可通过游览器查看推流效果:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" name="viewport" content="width=device-width,initial-scale=1" />
    <title>HLS Live Test</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/hls.js/8.0.0-beta.3/hls.min.js"></script>
</head>
<body>
        <video id="video" controls width="100%"></video>
        <script>
            var video = document.getElementById('video');
            var hls = new Hls();
            hls.loadSource('http://<your_device_ip>/live/index.m3u8');
            hls.attachMedia(video);
            hls.on(Hls.Events.MANIFEST_PARSED,function() {
            video.play();
            });
        </script>
</body>
</html>