可能不少有树莓派的小伙伴都遇到过这样一个问题,长期不用但已经配置过的树莓派,刚开机会自动连接已经识别的wifi,如果没有wifi的管理密码,手头又没有可以显示的设备,手边也没有能读tf卡的机器,就只能面对默默亮灯的树莓派望洋兴叹了。
如果能在每次树莓派开机的时候自动获取连接的ip地址,并发送到自己的微信上,岂不是快捷又方便?就像这样 ↓ ↓ ↓ ↓↓↓ ↓↓↓ ✿(。◕ᴗ◕。)✿
怎么实现呢?Please follow me~
文章目录
1. 思路(可行性分析):
- 通过树莓派shell命令,可以获取ip地址
- 通过crontab定时任务功能,可实现开机执行特定脚本
- 企业微信号注册很方便,提供群发api接口,只需注册一个企业微信号,成员只有自己一个人,调用api接口可以向自己发送消息
so,完全可以很方便的实现~
2. 获取ip地址
这个应该是最简单的一步了吧,为了防止后面我忘了,就写在前面吧。
ifconfig
命令可以查看网络连接状态,用|grep 192.168
筛选其中包含192.168的信息(毕竟我们只需要这个192.168.x.x的地址),再用|awk -F net '{print $2}'
选择其中第一个地址,就是我们树莓派连接的ip地址了。
即:ifconfig|grep 192.168|awk -F net '{print $2}'
ifconfig
命令执行后包含192.168.x.x的信息应该只有两处,第一处是我们要用的ip地址inet,第二处则是broadcast。这两个信息都是在一行展示,所以用grep筛选后会是如下形式:
inet 192.168.1.4 netmask 255.255.255.0 broadcast 192.168.1.255
所以我们要再用awk的截取操作,我们发现要提取的ip地址处于两个“net”字符之间,因此我们设置截取的分隔符为“net”,则会将这段信息截成三部分,第一部分是"i",第二部分是"192.168.1.4",第三部分是"mask 255.255…",我们要取的是第二部分,因此print $2
- awk使用格式为:
|awk -F 分隔符 '{命令}'
- grep使用格式为:
|grep 筛选内容
3. 注册企业微信号(无门槛)
点击进入→:企业微信官网
按要求注册就行了,没有任何门槛,非常简单,略过不提。
4. 调用api发送信息
4.1 新建应用
在“应用管理>应用>自建”一栏中点击“创建应用”,根据提示创建一个应用即可。如图:
4.2 记住几个关键信息
- 应用ID(AgentId)
- 秘钥(Secret) (以上两个均可点进刚才创建的应用列表里找到)
- 企业ID(corpID) (在“我的企业>企业信息”最下方找到“企业ID”)
然后我们在树莓派上一个方便的目录下,创建一个.sh文件,比如叫作 AutoDetectIP.sh
打开编辑,首行按规矩先输入:
#!/bin/bash
回车换行,然后我们将上述信息赋值成变量:
AgentId=xxxxx
Secret=xxxxx
corpId=xxxxx
注1: shell赋值时等号左右不能空格!
注2: 调用时要在变量前面加“$”,如输出AgentId:echo $AgentId
4.3 获取access_token
需要用 get 方式请求网址,命令格式如下:
curl https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
其中ID和SECRET分别填写上面记住的企业ID和秘钥
curl请求时,不加参数默认就是get请求,后面直接跟网址即可。
调用前面的变量,于是命令为:
curl https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$corpId&corpsecret=$Secret
如果正确,会返回一串信息,其中包含我们需要的access_token和其生效时间。
我们可以把这段信息赋值给一个变量temp,于是命令又进一步改为:
temp=`curl https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET`
在shell脚本中反引号 ` 括起来的内容表示这是一段命令而不是字符串,也可以用$()括起来,但最好用前者。
- 注意: 要区分 $(命令) 和 ${变量}
这时temp中内容就包含了我们的access_token,接下来要截掉多与信息,依然使用前面提到的|awk
操作:
观察所给信息格式,大致如下:
如果错误,返回格式大致为:
{"errcode":40001,"errmsg":"invalid credential, hint: [16051035.........], from ip: 111...., more info at https://open.work.weixin.qq.com/devtool/query?e=40001"}
如果正确,返回格式大致为:
{"errcode":0,"errmsg":"ok","access_token":".........................","expires_in":7200}
我们可以将连续的双引号、冒号、双引号,即 “:” 作为分隔符,如果错误则只能得到2部分,如果正确则能得到3部分。且第三部分中如果我们再以 “,” 作为分隔符,则其中的第一部分就是纯净的access_token。因此,我们借助条件语句,代码如下:
if [ -n `echo $temp|awk -F \":\" '{print $3}'` ];then
access_token=`echo $temp|awk -F \":\" '{print $3}'|awk -F \",\" '{print $1}'`
fi
- 如果上述命令直接在控制台或要写成一行输入,则 fi 前面必须加分号;
- if 后的判断框[ ]和里面的条件语句必须左右留有一个空格,即 [ 的右边和 ] 的左边必须空一格
- 参数 -n 表示如果参数长度不为0,则执行then,否则执行else(shell中如果else下没有命令就不能写,不可命令留空);相反,参数 -z 表示如果参数长度为0,则执行then,否则执行else
- 双引号前面要加转义符 \
至此,我们得到了access_token并将其存入了变量access_token中。
4.4 发送信息
需要用post方式请求网址,并用json传递结构化参数:
官方文档给出的参数示例为:
{
"touser" : "UserID1|UserID2|UserID3",
"toparty" : "PartyID1|PartyID2",
"totag" : "TagID1 | TagID2",
"msgtype" : "text",
"agentid" : 1,
"text" : {
"content" : "你的快递已到,请携带工卡前往邮件中心领取。\n出发前可查看<a href=\"http://work.weixin.qq.com\">邮件中心视频实况</a>,聪明避开排队。"
},
"safe":0,
"enable_id_trans": 0,
"enable_duplicate_check": 0,
"duplicate_check_interval": 1800
}
post地址为:https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN
(ACCESS_TOKEN为刚获取到的内容)
将地址赋值给变量(要不然之后的命令太长了):
PostURL="https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$access_token"
用curl进行post请求,并以json传递参数,格式为:
curl -H "Content-type: application/json" -X POST -d ‘{参数}’ 网址
其中,参数内容中如果要引用变量,需要先用双引号,再用单引号,再用双引号括起来,然后$变量名,例如:
-X POST -d '{"touser":"@all","msgtype":"text","agentid":"'"$AgentId"'","text",{"content":"IP地址为'"$IP"'"}}'
参数中,msgtype、agetid、text、content是必须包含的参数,touser、toparty、totag三者至少需要包含一个
(IP变量没定义的记得先定义,方法如第2节所述命令,格式如前述:变量名=$`命令` <反引号括起来的都是命令>)
故发送消息命令为:
curl -H "Content-type: application/json" -X POST -d '{..如上..}' $PostURL
保存退出。
4.5 赋予文件可执行权限并执行编写的脚本
赋予权限:
sudo chmod +x AutoDetectIP.sh
+号代表赋予权限,-号代表剥夺权限,x表示可执行权限,r表示可读权限,w表示可写权限
执行脚本文件:
./AutoDetectIP.sh
默认用bash执行,执行时默认检查的是/etc、/bin等这些文件夹,一般我们用户操作的文件夹都不再检查范围内,所以要用 ./ 来强调从本文件夹中查找文件并执行(当然,得先进入文件夹,或者输入完整路径)
出现如下提示且手机收到消息(需要提前扫描企业二维码关注企业,二维码在“我的企业>微信插件>邀请关注”中),则发送成功。
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 277 100 277 0 0 601 0 --:--:-- --:--:-- --:--:-- 602
{"errcode":0,"errmsg":"ok","invaliduser":""}
5. 设置开机自动运行
我们使用crontab定时任务功能来实现。
在控制台终端输入
crontab -e
即可启动定时任务配置文件,第一次配置会提示选择编辑器,选择你习惯的编辑器或者直接回车也ok,即可进入文件进行配置,文件默认注释部分就是说明和示例,可以创建每隔多久运行一次的任务,只需按规则在文件后添加即可,且保存后如果机器重启,定时任务仍然有效。
格式为:
如:
0 0 * * * /home/pi/backup.sh
表示每天00:00运行备份程序
*/5 * * * * python /home/pi/test.py
表示每5分钟用python运行一次test.py程序
0 5 * * 1 sudo apt-get update -y; sudo apt-get upgrade -y
表示每周一天05:00时更新apt软件包
- 星号(*):代表所有可能的值,例如month字段如果是星号,则表示在满足其它字段的制约条件后每月都执行该命令操作;
- 逗号(,):可以用逗号隔开的值指定一个列表范围,例如,“1,2,5,7,8,9”;
- 短横(-):可以用在整数之间表示一个范围,例如“2-6”表示“2,3,4,5,6”;
- 斜线(/):可以用正斜线指定时间的间隔频率,例如h位置处填“0-10/2”表示0点到10点间每两小时执行一次。同时斜线可以和星号一起使用,例如*/10,如果用在m位置,表示每十分钟执行一次。
如果想要每次开机执行任务,只需在文件最后添加:
@reboot /home/pi/AutoDetectIP.sh
这里面需要根据你之前创建文件的位置输入完整路径,即表示每次开机自动运行此文件
保存退出即可。
6. 重启自动执行不成功?
到这里可以说基本已经全部完成了。可能有的小伙伴兴冲冲地重启树莓派,满心期待的望着手机,结果却发现等了个寂寞。。。
怎么回事呢?
如果检查sudo service cron status
确定cron计时任务正在运行的话,那么主要有两个原因:
- 系统开机时默认的环境变量和系统完全启动后用户使用的环境变量不同,因此无法执行脚本
- 开机时执行脚本,但当时网络还没有配置好,并不能成功获取ip并发送消息
因此,针对这两种情况我们分别进行调整。
6.1 添加环境变量,使cron开机能成功执行脚本
首先我们要确定在当前用户执行成功时,脚本所用到的环境变量。
我们可以在脚本末尾添加一行代码:(调试成功后记得删掉)
echo $PATH
再次运行,就可以得到当前运行所使用的环境变量,比如我的为:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games
那么我们只需把这些环境变量手动添加到脚本中即可。
在脚本正文的第一行(在#!/bin/bash
之下)添加如下代码即可:
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games"
6.2 添加循环判断,在未连接时不断检测,连接后发送消息
我们使用shell脚本中的while循环语句:
while true; do (echo `ifconfig|grep 192.168`)&& break;sleep 1;done;
如上,表示不断循环,直到匹配192.168的内容非空时,跳出循环,循环间隔1s。
我们将此句加入脚本文件中合适位置,就可以在运行后一直等待连接网络成功的第一时间向微信发送消息。(此句至少得放在所有使用curl这样与网络相关的命令之前)
其次,为了保证启动时程序不影响其他启动内容,或不被系统过快切断,因此我们将crontab中的命令修改为nohup + 命令+ &
这样的静默不挂起运行方式:
控制台输入crontab -e
进入编辑定时任务,将原来的命令修改为:
@reboot nohup /home/pi/AutoDetectIP.sh &
7. 成功!
至此,所有步骤都结束了。我们就可以在每次树莓派开机时,通过微信自动收到所连接的ip地址消息提示了。
8. 关于IP地址获取的改进
上面关于本机IP地址的操作主要有两个,一是通过是否包含“192.168”字段来判断是否连接wifi,二是通过|grep
、|awk
等操作截取纯净的ip地址信息。
其中第一个判断方式应该来说是较为准确且稳定的,符合我们的使用目标。
但第二个提取操作却有可能因为网络环境的变化,或网络设置的调整,可能会有字段的位置或是中间内容的调整,使得截取出来的不一定再是纯净的ip地址。因此我们可以更换一种更为快捷的获取IP地址的方式:
hostname -I
很简单的命令不用任何操作,就可以直接输出树莓派所连接的ip地址。
当然,如果你连接的网络给你分配了ipv6地址的话,也会一并展示出来,如果需要再按需提取即可。比如我的输出如下:
pi@raspberrypi:~ $ hostname -I
192.168.1.4 2409:80XXXXX(公网ip手动打码)XXXXX
如果只需要提取前面的ipv4地址,那么同上用awk截取:
echo `hostname -I|awk -F ' ' '{print $1}'`
如果要赋值给变量$IP,那么用内外双重反引号会造成歧义,故可以在外面用$(命令)的方式:
IP=$(echo `hostname -I|awk -F ' ' '{print $1}'`)
如此即可提取出纯净的ipv4地址
最终效果如图:
9.附:完整示例代码:
#!/bin/bash
###########################################
##### created by RichardYann #####
##### 2020-11-11 #####
##### EmaiL:yanrichard500@gmail.com #####
###########################################
# set PATH
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games"
# Global Variables
corpid=CORPID # change to your own corpid
AgentId=AGENTID # change to your own agentid
Secret=SECRET # change to your own secret
getTokenURL="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$corpid&corpsecret=$Secret"
# check netstat
while true; do $(echo `ifconfig|grep 192.168`)&& break;sleep 1;done;
# get token
temp=`curl $getTokenURL`
if [ -n `echo $temp|awk -F \":\" '{print $3}'` ];then
access_token=`echo $temp|awk -F \":\" '{print $3}'|awk -F \",\" '{print $1}'`
fi
# get postURL
PostURL="https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$access_token"
# get ip; change $1 to $2 if you wanna get ipv6 addr.(if you had)
IP=$(echo `hostname -I|awk -F ' ' '{print $1}'`)
# send post
curl -H "Content-type: application/json" -X POST -d '{"touser":"@all","msgtype":"text","agentid":"'"$AgentId"'","text":{"content":"树莓派已经启动啦!!\n\nIP地址是:'"$IP"'\n\n快点连接控制吧~~"},"safe":0}' $PostURL