分类 无线电 下的文章

        clockworkpi属于极客玩具,自然不会像一般的消费电子产品那样面面俱到,你要喜欢它的极客属性,就要忍受极客的不便,比如键盘太硬等问题。我完全不同意有的人提出的这东西只是一个玩具完全没法拿来干活的观点,比如domoticz的自定义界面我就是在clockworkpi上安装的vscode上修改调试出来的,键盘太硬对我来说也完全不是问题,但还是有一些影响使用效率的问题,解决了就更加好用,以下就是一些clockworkpi特有的问题和解决方案,记录一下,以免以后忘了。

  • apt update和upgrade失败问题

            apt升级和更新是常规操作,但用所附TF的系统命令行操作时失败,修改源也没有用,发现是raw.githubusercontent.com无法解析导致。尝试了很多办法,有时行有时不行,最后是修改/etc/hosts解决问题,增加如下代码,直接告知这个网址的IP地址:

    151.101.72.249 github.ssl.fastly.net
    192.30.255.112 github.com
    185.199.109.133 raw.githubusercontent.com
    185.199.110.133 raw.githubusercontent.com
    185.199.111.133 raw.githubusercontent.com

            github在这个机器上也是几乎无法访问,把IP地址一并加进去。需要注意的是这些网址的IP地址可能是会变化,如果又连不上了就在网上查一下现在的IP地址,在这个文件里修改过来。修改后要重启dhcp服务。

  • 键盘背光问题

            clockworkpi的uConsole的键盘是有背光的,组装时就可以看到键盘上有LED。但怎么打开一直不得其法,官网上也没有明确说明,搜索后发现是Fn+space键控制开启。每次启动时背光是关闭的,用快捷键可以开启两档背光,但背光不均匀,有的字亮有的字暗,能看清,缺点就是橙色字体部分不透光,不过晚上不开灯使用是足够的。

  • 鼠标左键问题

            uConsole的鼠标设计和一般的笔记本不一样,中部靠右是一个黑莓手机那种微型轨迹球,中部靠左是四个光标间和LR两个键对应鼠标左键右键。轨迹球是可以按下去的,但是在菜单栏按下没有反应,在浏览器中按压链接会在新页面中打开链接,也不知道是怎么设置的。有大神找到了将轨迹球按下改为鼠标左键的办法,很简单,在命令行中输入一条指令即可:

    xinput set-button-map "ClockworkPI uConsole Mouse" 1 1 3 4 5 6 7 8 9 10 11 12

            但是这条命令在关机重启后就失效了,每次重启都要重新输入一遍还是麻烦,尝试过crontab中设置定时任务,没有成功,修改crontab -e和/etc/crontab都没有用,后来用了设置增加开机自启动程序解决的,在.config/autostart子目录下,增加一个比如叫keyboard.desktop文件,输入如下代码:

    [Desktop Entry]
    Type=Application
    Name=keyboard
    Comment=设置轨迹球按压为鼠标左键
    NoDisplay=true
    Exec=/usr/bin/xinput set-button-map "ClockworkPI uConsole Mouse" 1 1 3 4 5 6 7 8 9 10 11 12
    NotShowIn=GNOME;KDE;XFCE;

            这样每次重启就自动设置好了。修改后方便不少,右手可以完成除了鼠标右键的各种操作,左手摆在光标键上可以不动,不用在光标键与鼠标左键之间来回移动。

  • 连接蓝牙耳机问题

            uConsole是有两个内置微型喇叭的,还很精致,但不知道是不是因为组装后壳体没有开孔的缘故,声音很小,100%音量比笔记本30%的音量还小,更比不上手机外放时的音量,看论坛是通病,那就用蓝牙耳机代替喇叭吧,没想到树莓派的蓝牙设置还颇费周折,如此这般才连接成功:
            安装驱动(其实系统是有蓝牙驱动的,可能主要安装蓝牙的图形设置界面吧,用命令行也行,就是麻烦)

    sudo apt install bluetooth pi-bluetooth bluez blueman

            安装后菜单栏上就有一个蓝底白色蓝牙标志的图标,这就是蓝牙配置的GUI,点击后选择设备,打开设备发现界面,然后将蓝牙耳机设置为配对状态,在设备界面中出现后就可以点击完成配对。我的韶音AS800配对后并不能听见声音,播放视频时仍然是内部扬声器发声,要在菜单栏音量图标那里右键点击,选择将声音输出改为韶音耳机后才能听到声音。
            蓝牙耳机连接不算稳定。有时候重启可以自动重连耳机,有时候就连不上,需要进到设备发现界面,让耳机再进入配对状态,选中连接后才能重连。

  • 内置喇叭声音太小问题

            邮件询问clockworkpi官网,内置喇叭声音太小怎么办,回复如下:

    使用系统音量调节可以将音量调到超过100%。
    但是由于扬声器单元功率比较小,因此在非常嘈杂的环境下,建议使用蓝牙或有线耳机。
            但是无论是UI界面还是用alsamixer命令行命令都最多只能将音量调整为100%,搜索说音量要想调整为100%以上要安装音效插件:
    sudo apt install pavucontrol

            安装后在界面里找到pulseAudio音量控制进去,可以将音量调整到最大153%(11DB),声音确实比原来大了很多。
            邮件说的内置扬声器功率太小所以声音小我是不认同的,这两个喇叭任何一个都比手机里的外放扬声器大多了,但是音量却小多了。估计还是没有开孔的缘故。

        本文将不定期更新。

        Domoticz的网页显示适合管理,不适合展示数据内容,自定义显示方式十分有必要。Domoticz本身是可以自定义页面的,https://www.domoticz.cn/wiki/%E8%87%AA%E5%AE%9A%E4%B9%89%E7%8A%B6%E6%80%81%E9%A1%B5就讲了如何设置自定义页面的方法,但说得不清不楚,在试验成功的基础上,总结下自定义页面的做法。

        首先找一个喜欢的自定义页面,把页面html代码拷贝出来,比如下面这个:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Domoticz</title> 
<link href="http://fonts.googleapis.com/css?family=Orbitron:700" rel="stylesheet" type="text/css">
<script src="https://cdn.staticfile.org/jquery/1.12.0/jquery.min.js"></script>
<style type="text/css">
div#cnt {
    width: 990px;
    margin: 0 auto;
    text-align: center;
}
a, a:visited { text-decoration: none; color: #336699; }
a:hover { text-decoration: none; color: #003366; }
#frame {
    float: left;
    margin: 0px;
    padding: 0px 2px 0px 2px;
}
#label_lg {
    font-family: arial;
    font-weight: normal;
    color: #999;
    font-size: 17px;
    margin-top: -20px;
    height: 25px;
    line-height: 10px;
}
#itemp {
    font-family: Orbitron, serif;
    font-weight: bold;
    width: 570px;
    padding: 5px;
    border: 1px solid #666;
    height: 217px;    
    line-height: 210px; 
    font-size: 180px;    
    color: darkorange;
    margin: 0px ;
}
#oFloor, #purifier, #uv, #ihum, #astat, #astatw, #otemp, #crte,
#uv, #rain, #ohum, #ocld, #wtxt, #ctime, #brLight, #ctemp, #lrLight, 
#waterHeater, #eHeating, #tmpc, #crte, #whWatt, #wind, #hWatt, 
#cpuTemp, #ram, #space, #cpuUsage {
    font-family: Orbitron, serif;
    font-weight: bold;
    width: 162px;
    padding: 10px;
    border: 1px solid #666;
    height: 90px;
    line-height: 75px;         
    font-size: 90px;
    color: #999;
}
#ctime, #brLight, #lrLight, #waterHeater, #eHeating, #tmpc, #crte, #whWatt, #uv, 
#rain, #ohum, #ocld, #wtxt, #otemp, #wind, #astat, #awatt, 
#astatw, #hWatt, #cpuUsage, #cpuTemp, #ram, #space {
    height: 65px;
    line-height: 55px;
    color: #999;
}
#hWatt, #cpuUsage, #crte, #wind { 
    width: 184px; 
}
#wtxt { 
    width: 560px; 
    font-size: 40px;
    line-height: 55px;
    color: darkorange;
}
#al { 
    margin-left: -35px; 
    margin-top: 55px; 
    width: 1050px;height: 570px;    
}
</style>
</head>
<div id="images"></div>
<body bgcolor="#000">
<div id="cnt">
<div style="margin-bottom:5px;">
<div>
<div>
<div id="frame">
    <div id="ihum" style="color:lightblue;font-size:50px;">--</div>
    <div id="label_lg">室内湿度 (%)</div>
    <div id="purifier" style="font-size:35px;">--</div>
    <div id="label_lg">空气净化器</div>
</div>
<div id="frame">
    <div id="itemp">--</div>
    <div id="label_lg" style="font-size:25px;margin-top:-30px;">卧室温度 (º<span class="degsign">C</span>)</div>
</div>
<div id="frame">
    <div id="oFloor" style="color:teal;font-size:50px;">--</div>
    <div id="label_lg">一楼温度 (º<span class="degsign">C</span>)</div>
    <div id="ctemp" style="color:#6666FF;font-size:50px;">--</div>
    <div id="label_lg">地下室温度 (º<span class="degsign">C</span>)</div>
</div>
</div>
<div>
<div id="frame">
    <div id="brLight" style="font-size:40px;">--</div>
    <div id="label_lg">卧室灯</div>
</div>
<div id="frame">
    <div id="lrLight" style="font-size:40px;">--</div>
    <div id="label_lg">客厅灯</div>
</div>
<div id="frame">
    <div id="eHeating" style="font-size:40px;">--</div>
    <div id="label_lg">电暖气</div>
</div>
<div id="frame">
    <div id="hWatt" style="font-size:40px;">--</div>
    <div id="label_lg">电暖气功率</div>
</div>
<div id="frame">
    <div id="waterHeater" style="font-size:40px;">--</div>
    <div id="label_lg">热水器</div>
</div>
</div>
<div>
<div id="frame">
    <div id="space" style="font-size:40px;;color:#3333cc;">--</div>
    <div id="label_lg">硬盘占用</div>
</div>
<div id="frame">
    <div id="ram" style="font-size:40px;color:#009933;">--</div>
    <div id="label_lg">内存占用</div>
</div>
<div id="frame">
    <div id="cpuUsage" style="font-size:40px;color:#ff3333;">--</div>
    <div id="label_lg">CPU使用率</div>
</div>
<div id="frame">
    <div id="cpuTemp" style="font-size:40px;color:#ff3333;">--</div>
    <div id="label_lg">CPU温度 (º<span class="degsign">C</span>)</div>
</div>
<div id="frame">
    <div id="whWatt" style="font-size:40px;color:#ff3333;">--</div>
    <div id="label_lg">热水器功率</div>
</div>
</div>
<div>
<div id="frame">
    <div id="rain" style="font-size:40px;">--</div>
    <div id="label_lg">日降雨量 (MM)</div>
</div>
<div id="frame">
    <div id="uv" style="font-size:40px;color:#C34A2C;">--</div>
    <div id="label_lg">紫外线指数 (UVI)</div>
</div>
<div id="frame">
    <div id="wind" style="font-size:40px;">--</div>
    <div id="label_lg">风速 (<span class="windsign">km/h</span>)</div>
</div>
<div id="frame">
    <div id="otemp" style="font-size:40px;color:darkorange;">--</div>
    <div id="label_lg">户外温度 (º<span class="degsign">C</span>)</div>
</div>
<div id="frame">
    <div id="ohum" style="font-size:40px;color:teal;">--</div>
    <div id="label_lg">户外湿度 (%)</div>
</div>
</div>
<div>
<div id="frame">
    <div id="astat" style="font-size:40px;">--</div>
    <div id="label_lg">净化器功率</div>
</div>
<div id="frame">
    <div id="wtxt">--</div>
    <div id="label_lg">天气</div>
</div>
<div id="frame">
    <div id="astatw" style="font-size:40px;">--</div>
    <div id="label_lg">气压 (hPa)</div>
</div>
</div>
</div>
<div id="all"></div><br>
</div>
</div>
<script type="text/javascript" charset="utf-8">
$.roomplan=0;
$.domoticzurl="http://127.0.0.1:8080";
function RefreshData(){
    clearInterval($.refreshTimer);
    var jurl=$.domoticzurl+"/json.htm?type=devices&plan="+$.roomplan+"&jsoncallback=?";
    $.getJSON(jurl,
        {
            format: "json"
        },
        function(data) {
            if (typeof data.result != 'undefined') {
                if (typeof data.WindSign != 'undefined') {
                    $('.windsign').html(data.WindSign);
                }
                if (typeof data.TempSign != 'undefined') {
                    $('.degsign').html(data.TempSign);
                }
                $.each(data.result, function(i,item){
                    for( var ii = 0, len = $.PageArray.length; ii < len; ii++ ) {
                        if( $.PageArray[ii][0] === item.idx ) {
                            var vtype=$.PageArray[ii][1];
                            var vlabel=$.PageArray[ii][2];
                            var vdata=item[vtype];
                            if (typeof vdata == 'undefined') {
                                vdata="??";
                            }
                            else {
                                vdata=new String(vdata).split(" ",1)[0];
                            }
                            $('#'+vlabel).html(vdata);
                        }
                    }
                });
            }
        });
    $.refreshTimer = setInterval(RefreshData, 10000);
}
$(document).ready(function() {
    $.PageArray = [
        //格式: idx, value, label, comment
        ['19','Temp','itemp','woonkamer'],        //卧室温度
        ['0','Humidity','ihum','woonkamer'],    //室内湿度
        ['4','Barometer','astatw','woonkamer'],    //气压
        ['9','ForecastStr','wtxt','woonkamer'],    //天气
        ['4','Temp','otemp','buiten'],            //户外温度
        ['17','Humidity','ohum','buiten'],        //户外湿度
        ['0','Temp','oFloor','room setpoint'],    //一楼温度
        ['15','Data','cpuTemp','cputemp'],        //CPU温度
        ['16','Data','cpuUsage','cpuusage'],    //CPU占用
        ['7','Rain','rain','rain'],                //降雨量
        ['12','Data','ram','ram'],                //内存占用
        ['6','UVI','uv','uv'],                    //紫外线
        ['8','Data','wind','wind'],                //风速
        ['2','Status','brLight','light'],        //卧室灯
        ['3','Status','lrLight','light'],        //客厅灯
        ['14','Data','space','HardDriver']        //硬盘占用
    ];
    RefreshData();
});  
</script>
</body>
</html>

        修改html当中的一些参数:

        首先修改房间号:设置了多个房间的下面改为对应的编号,跨多个房间的数据要显示的,选择0

$.roomplan=0;

        其次改domoticz地址,在内网部署的话,一般是

$.domoticzurl="http://192.168.1.XXX:8080";

        然后观察图片
Frontpage.jpg
中各个数据显示块的内容,把对应位置的名称改为自己想要展示的数据名称

<div id="label_lg">室内湿度 (%)</div>

        数据命名行中的id记清楚,然后到$.PageArray中修改或加入对应的JSON格式数据

['19','Temp','itemp','woonkamer'],        //卧室温度

        第一个单引号内是domoticz中相关硬件设备的idx,第二个单引号内是对应硬件设备反馈JSON数据中要显示哪个栏目的内容,第三个单引号内是对应数据命名行中的id,第四个单引号内是描述,可以不用管它。每个要显示的数据栏需要有这么一行数据。

        其中容易出问题的是第二个单引号的填写内容,如果错误或没有对应栏目,数据处会显示??。用http://192.168.1.XXX:8080/json.htm?type=devices&plan=0来看所有设备的状态数据,找到对应idx设备的数据,在{}之间的json数据中找到要展示的栏目,把栏目名称填进去。对传感器而言,温度一般是Temp,湿度一般是Humidity,气压一般是Barometer,其他自己定义的传感器如果没有具体的栏目可以填写Data。开关状态显示填Status。

        这个html文件修改调试好后,比如叫a.html,把它移动到/home/pi/domoticz/www/views目录下,这样在浏览器中输入网址http://192.168.1.XXX:8080/views/a.html就可以在浏览器中显示自定义页面内容了。我调整过的页面如下:
domoticz自定义页面.png

可以设置开机自动显示,在终端界面中输入指令:

cd /home/pi/.config/
mkdir autostart
cd /autostart
sudo nano my.desktop

输入如下指令:

[Desktop Entry]
Type=Application
Exec=chromium-browser  --disable-popup-blocking --no-first-run --disable-desktop-notifications  --kiosk "http://192.168.1.XXX:8080/views/a.html"

重启生效。因为是全屏模式,遮蔽了菜单界面等,如要退出按Alt+F4。

为了实现内网穿透,几年前买了一个花生壳盒子,能用,慢,不另外花钱只能用两个链接,虽然很不满意,因为还能用,也就将就着用下来了。

然后半年前忽然就无法访问了,进到官网一看,修改了协议,要补充这个那个,补充完了还是不行,逼着我付费?不惯它,宁可不用。但是不用还是不方便,网上查了一圈,用ddns-go可以实现内网穿透,IPV6真正派上用场,原来我以为的IPV6光打雷不下雨是我错怪了它。这个方法不用额外花钱,速度极快,而且能够穿透的设备数和端口数没有限制,让那些付费的工具见鬼去吧!

先注册一个域名;
在域名服务商那里申请API,获得ID和TOKEN;
树莓派上部署:
安装docker:
sudo curl -sSL https://get.docker.com | sh
检查是否安装正确:
docker -v
设置开机自启动docker:
sudo systemctl enable docker
拉取图形界面镜像:
sudo docker pull portainer/portainer-ce
创建相关卷:
sudo docker volume create portainer_data
启动图形界面容器:
sudo docker run -d -p 9000:9000 --name portainer --restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce
拉取ddns-go镜像:
sudo docker pull jeessy/ddns-go
启动ddns-go容器:
sudo docker run -d --name ddns-go --restart=always --net=host -p 192.168.1.XXX:9876:9876 jeessy/ddns-go -l :9876 -f 1400
进入界面设置:
192.168.1.XXX:9876
填写dnspod,ID,token,选择IPV6,通过网卡获取地址,240X为公网IPV6地址,填写域名,访问:域名:XXXX(根据你的应用而定)

避坑指南:一个API只能对应一台硬件,因为它是解析该硬件的公网IPV6地址,如果内网有多个设备需要穿透,需要申请多个API,一一对应才能成功,一个硬件设置后,该硬件上所有端口都可以使用,比如用22进行SSH等。

        clockworkpi是一家中国公司生产的可以独立运行的树莓派掌上电脑,集成了CM4核心卡,一块720P的屏幕,一个全键盘,以及微型轨迹球,两节18650电池等,工业朋克风的设计风格让人爱不释手。一直想用树莓派做个微型电脑,用来学习linux和对家里的诸多树莓派进行一些命令行的操作,然而工作量不是一般的大,合适的屏幕和配件就很难找到,而这种东西一旦凑合用了通用的配件,一下子格调就降下来了,变成手工耿那样的手工粗糙DIY了。clockworkpi设计很不错,外壳框架都是铝合金CNC出来的,看得出来成本不低,虽然块头大但考虑了人体工学设计,双手端着操作熟练了倒也相当舒服。不枉我等了八个月啊!
微信图片_20240209124339.jpg

        第二次因为修改raspberry的系统文件造成sudo无法使用了。第一次是因为修改了/etc/sudoers导致,那一次是用以下语句救回来的:
pkexec --pi nano /etc/sudoers

        这一次是因为python脚本执行问题,怀疑导入的模块环境变量不对,修改了/etc/profile导致。用上面这个语句无法救回,试验了半天,用下面这句:
export PATH=/usr/bin:/usr/sbin:/bin:/sbin:/usr/X11R6/bin
        运行这一句后,sudo语句暂时可以运行,赶紧进/etc/profile把添加进去的环境变量路径删掉,然后用如下语句刷新:
source /etc/profile
        系统sudo语句恢复正常。

        所以raspberry的系统文件修改有风险!还有就是要事先设置好root账户的密码,这样sudo用不了以后可以用root账户登录,不用sudo语句就可以修改。

        需要修改环境变量的缘起,是将流星监测站的叠加图上传由sm图床改为腾讯云COS自建图床,当时试验是好的,后面就没有能够收到图片推送,今天putty登录人工运行python脚本发现报错缺少qcos模块,可是我已经安装过cos-python-sdk-v5模块了啊,pip freeze也能看到安装的模块,最后各种添加环境变量都没有用,还是用pip uninstall以后再install这个模块才解决了这个问题。记住,以后再有这样的幺蛾子,先卸载,再安装试试。

  • Arduino的开发板是拿来搞开发的,不是拿来搭建应用系统的,nano这样小体积的板子除外。批量应用用ATMEGA328P或ATMEGA2560单片机最小系统搭建即可。

    • 网上找的电路图注意看是对应哪种封装的,DIP封装和TQFP封装的管脚定义不同。PU是DIP封装,AU是TQFP封装。
    • 用ICSP接口灌装bootloader,之后用UART0灌装程序。UART0接口要附带reset,否则无法成功烧录。
    • 如果自制的板子不工作,在电路本身无错误的情况下,大概率是ATMEL芯片引脚出现虚焊、短连等焊接质量问题。
    • 不能盲信嘉立创的自动布线,布线完成后一定要仔细核查DRC,可能出现短连等非正常情况。
    • 绝对不要把系统的复位电路和其他模块的复位电路直接并联,如果电平或复位逻辑不一致会导致复位管脚电平处于浮动状态,会导致一系列匪夷所思的故障。
    • D0口要做外部上拉。会大幅提升供电可靠性。
  • Arduino的内存没那么容易用完。如果编译时提示动态内存太低,肯定是哪个地方没弄对。

    • 用自定义函数,函数内使用局部变量,减少全局变量使用。
    • Serial.print语句用到的固定字符串用F()包起来,这样固定字符串就只占用程序空间而不会占用内存空间。
  • 串口读取数据语法升级。按照一般的Arduino教材(包括中文互联网中)说明,串口读取只能采用read()方法,一次读取一个字节,如果需要读取字符串需要人工构建算法,但实际上现在Arduino已有readString()方法,可直接读取串口接收到的字符串,无需人工构建算法。用英文在google中搜索可以找到相应信息。
  • 有关Arduino扩充串口缓冲区的设置。当一次接收串口数据数百个字节时,因Arduino串口默认发送、接收缓冲区各只有64字节,如接收到的字节数超出64字节,其后的将会被舍弃。因此,必须在Arduino的HardwareSerial.h中设置发送、接收缓冲区尺寸,将字节数扩充到450字节。需要注意的是,必须修改以下目录中的HardwareSerial.h才有效!C:\user\xxxx\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.23\cores\arduino\HardwareSerial.h
    (其中XXXX为所安装机器的用户名) 修改方法:在HardwareSerial.h中开头增加两个宏定义:

    #define SERIAL_RX_BUFFER_SIZE 450
    #define SERIAL_TX_BUFFER_SIZE 450
    两个缓冲区尺寸必须一样,否则无法通过编译。 Arduino IDE如果升级了,一定要再次进入该目录进行修改,否则串口响应会出现问题。
    *《ARDUINO技术内幕》是本好书,深度开发必看。

本记录持续更新中。