微信相框-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 17240|回复: 11

[项目] 微信相框

[复制链接]
发表于 2017-10-20 23:55 | 显示全部楼层 |阅读模式
前言
之前试了微信点灯,觉得可以做个微信相册试试。

步骤

1. 材料准备:树莓派,显示屏
1.jpg
2. 找个外壳,又是个纸盒,把手机牌子挡住
2.jpg
3. 确定位置
3.jpg
4. 切割
4.jpg
5. 安装屏
5.jpg
6. 找个礼品包装纸来装饰下更好看
6.jpg
7. 包装好
7.jpg
8. 然后,把驱动和树莓派往盒里塞的时候,悲剧了……我,我,我去年买了个屏……屏线断了
8.jpg
9. 好吧,先不管屏了。服务端写好,找个服务器部署一下吧。外面有免费试用的服务器大家可以申请试用。我就拿另一个树莓派来当服务器,只是做实验嘛。自己家中的网络还需要做内网穿透,网上能找到不少,这里就不打广告了。
安装好 LAMP 环境后,把服务端程序放到 html 目录就好了(以后有空改成服务比较适合)。
10.jpg
10. 公众号配置指向我们的服务器地址,首先要先用官网的 valid 示例来让公众号配置通过验证,然后再改为自己的程序。具体操作网上很多教程了,就不多啰嗦啦
0.jpg
11. 客户端程序也上传到客户端的树莓派上
9.jpg
12. 这是客户端
11.jpg
13. 这是服务器[捂脸]
12.jpg
14. 小显示屏坏了,我还有大的呢,嘿嘿!
看看全家福:电视上面的 pi 是服务器,下面的 pi 是客户端,手机端的微信,最重要的最后说,大显示屏!
total.jpg
15. 手机上传照片到公众号
17.jpg
16. 大屏幕的微信相册开始显示啦!
13.jpg
14.jpg
15.jpg
16.jpg
代码
服务端主程序
[mw_shl_code=php,true]
<?php
// ----------------------------------------------------------------------------
// wxAlbum.php
//
// Created 2017-10-12
// By seesea <seesea2517#gmail#com>
//
// 微信相框服务端
// 时间仓促,只做演示用,待完善的功能:
// 1. 用户绑定、解绑、查询相册
// 2. 用户照片管理功能
// 3. 相册内置 id,启动时显示 id 及二维码(不过个人公众号未开放扫码功能,可以用扫码得到 id 内容后粘贴发文本的方式来绑定)
// 4. 用户、相册、照片的数据库管理
// ----------------------------------------------------------------------------

//define your token
define("TOKEN", "seesea");

define("DIR_PHOTO",                 "/var/www/html/album");

define("LOG_FILE",                  "/var/www/html/log.txt");

define("MSG_INVALID_TYPE",          "无效的消息类型,仅接收文本和图片。");
define("MSG_NO_COMMAND",            "未输入命令。");
define("MSG_INVALID_SERIAL_NUM",    "无效的序列号。");
define("MSG_HELP",                  "帮助:\n上传:直接发图\n绑定:发送文本“绑定 序列号”\n解绑:发送文本“解绑 序列号/序号”,直接发送“解绑”会列出所有已绑定的相框序列号并标记序号\n全部解绑:发送文本“解绑全部”");

function logMsg($logText)
{
    file_put_contents(LOG_FILE, $logText . "\n", FILE_APPEND);
}

function downloadFile($url, $newName)
{
    $content = file_get_contents($url);
    file_put_contents($newName, $content);
}

logMsg("\n\nstart....\n");

$wechatObj = new wxAlbum();
$wechatObj->processMessage();

class wxAlbum
{
    private $xmlMsgObj;
    private $fromUsername;
    private $toUsername;
    private $msgType;

    public function __construct()
    {
        $dirs = array(DIR_PHOTO);
        foreach($dirs as $dir)
        {
            if (! file_exists($dir))
            {
                mkdir($dir);
            }
        }
    }

    public function valid()
    {
        $echoStr = $_GET["echostr"];

        //valid signature , option
        if($this->checkSignature())
        {
            echo $echoStr;
            exit;
        }
    }

    public function processMessage()
    {
        //get post data, May be due to the different environments
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
        logMsg($postStr);

        if (empty($postStr))
        {
            echo ""; // 回复 "success" 或空 ""
            exit;
        }

        /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection,
           the best way is to check the validity of xml by yourself */
        libxml_disable_entity_loader(true);
        $this->xmlMsgObj    = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        $this->fromUsername = $this->xmlMsgObj->FromUserName;
        $this->toUsername   = $this->xmlMsgObj->ToUserName;
        $this->msgType      = $this->xmlMsgObj->MsgType;

        switch ($this->msgType)
        {
            case "image":
                $this->processImageMessage();
                break;

            case "text":
                $this->processTextMessage();
                break;

            default:
                $this->responseUserByText(MSG_INVALID_TYPE);
        };
    }

    private function responseUserByText($msg)
    {
        $time         = time();
        $msgType      = "text";
        $textTpl      = "<xml>
                         <ToUserName><![CDATA[%s]]></ToUserName>
                         <FromUserName><![CDATA[%s]]></FromUserName>
                         <CreateTime>%s</CreateTime>
                         <MsgType><![CDATA[%s]]></MsgType>
                         <Content><![CDATA[%s]]></Content>
                         <FuncFlag>0</FuncFlag>
                         </xml>";

        $reponseXML = sprintf($textTpl, $this->fromUsername, $this->toUsername, $time, $msgType, $msg);
        echo $reponseXML;
    }

    private function processImageMessage()
    {
        $picUrl = $this->xmlMsgObj->icUrl;
        $filePathName = DIR_PHOTO . "/" . $this->xmlMsgObj->MsgId;
        downloadFile($picUrl, $filePathName);
        // 调试用 $this->responseUserByText("Down file: " . $filePathName);
        $this->responseUserByText("已上传服务器,文件名: " . $this->xmlMsgObj->MsgId);
    }

    private function processTextMessage($xmlMsgObj)
    {
        $keyword = trim($this->xmlMsgObj->Content);
        if (empty($keyword))
        {
            $this->responseUserByText(MSG_NO_COMMAND);
        }

        $contentStr = "收到命令:" . $keyword;
        $this->responseUserByText($contentStr);
    }

    private function showHelp()
    {
        $this->responseUserByText(MSG_HELP);
    }

    private function checkSignature()
    {
        // you must define TOKEN by yourself
        if (!defined("TOKEN"))
        {
            throw new Exception('TOKEN is not defined!');
        }

        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];

        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);

        // use SORT_STRING rule
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode( $tmpArr );
        $tmpStr = sha1( $tmpStr );
        logMsg($tmpStr);

        if( $tmpStr == $signature )
        {
            return true;
        }else{
            return false;
        }
    }
}

?>
[/mw_shl_code]
服务端辅助程序:查询相册服务端的照片数
[mw_shl_code=php,true]<?php
// ----------------------------------------------------------------------------
// countPhotos.php
//
// Created 2017-10-21
// By seesea <seesea2517#gmail#com>
//
// 计算服务端相应相册 id 的照片
// 简单演示用,以后有空完善根据相册 ID 来获取该相册的相片数
// ----------------------------------------------------------------------------

echo (count(scandir("/var/www/html/album")) - 2)
?>
[/mw_shl_code]
服务端辅助程序:列出相册服务端的照片名
[mw_shl_code=php,true]<?php
// ----------------------------------------------------------------------------
// countPhotos.php
//
// Created 2017-10-21
// By seesea <seesea2517#gmail#com>
//
// 列出服务端相应相册 id 的照片
// 简单演示用,以后有空完善根据相册 ID 来获取该相册的相片
// ----------------------------------------------------------------------------

echo (implode(" ", array_diff(scandir("/var/www/html/album"), array(".", ".."))))
?>
[/mw_shl_code]

客户端脚本
[mw_shl_code=bash,true]#!/bin/bash
# ----------------------------------------------------------------------------
# startAlbum.sh
#
# Created 2017-10-20
# By seesea <seesea2517#gmail#com>
#
# 相册客户端
# ----------------------------------------------------------------------------

PIC_DIR=pics
HTTP_SERVER="http://你的服务器地址"
COUNT_PHOTO_URL="$HTTP_SERVER/countPhotos.php"
LIST_PHOTO_URL="$HTTP_SERVER/listPhotos.php"
DOWN_PHOTO_URL="$HTTP_SERVER/album"
# QIV_COMMAND="qiv -smfD -d1 $(ls -r $PIC_DIR/*) --display :0"
mkdir $PIC_DIR 2>/dev/null

# bash -c "$QIV_COMMAND" &
# $QIV_COMMAND &
qiv -smfiD -d1 $(ls -r $PIC_DIR/* 2>/dev/null) --display :0 >/dev/null &
qiv_pid=$!

while true
do
    local_photo_count=$(ls $PIC_DIR | wc -l)
    remote_photo_count=$(curl "$COUNT_PHOTO_URL" 2>/dev/null)
    if (( local_photo_count < remote_photo_count ))
    then
        all_photo_list=$(curl "$LIST_PHOTO_URL" 2>/dev/null)
        local_photo_list=$(ls -1 $PIC_DIR)

        if [ -z "$local_photo_list" ]
        then
            local_photo_list="xxxxxxxxxxx"
        fi

        for name in $(echo -e "${all_photo_list// /\\n}" | grep -vE "$local_photo_list")
        do
            wget -q -P$PIC_DIR "$DOWN_PHOTO_URL/$name"
        done

        kill $qiv_pid
        qiv -smfiD -d1 $(ls -r $PIC_DIR/* 2>/dev/null) --display :0 >/dev/null &
        qiv_pid=$!
    fi

    sleep 1; # 测试用的间隔短一些,方便更快看到结果
done[/mw_shl_code]
视频

 楼主| 发表于 2017-11-18 18:12 | 显示全部楼层

衣服和抽奖奖品到啦!秀出来

IMG_8494.PNG IMG_8495.PNG


发表于 2017-11-24 11:38 | 显示全部楼层
你好,你的客户端的树莓派是怎么和显示屏连接的?
 楼主| 发表于 2017-11-24 12:44 来自手机 | 显示全部楼层
牙牙乐 发表于 2017-11-24 11:38
你好,你的客户端的树莓派是怎么和显示屏连接的?

电视有hdmi接口^_^
发表于 2017-11-28 20:55 | 显示全部楼层
本帖最后由 牙牙乐 于 2017-12-6 10:11 编辑
seesea 发表于 2017-11-24 12:44
电视有hdmi接口^_^

你好,能加你好友,请教你一下吗?谢谢了
发表于 2017-12-6 10:18 | 显示全部楼层
你好,我想请教你一下。按照你的实验过程,我做了一遍,基本能够实现功能,只不过大多数时候从微信公众号中上传到的图片会保存在服务器上,但是客户端并不下载,只有将pics文件夹下的照片删掉几张之后才会显示刚上传的图片。而且终端下一直提示
line 37:grep:command not found
line 42;kill:No such process.
能帮我分析分析是哪里有问题吗?代码没变,对这个很感兴趣,但是shell语言不太熟,看不太懂。
发表于 2017-12-6 10:22 | 显示全部楼层
还有就是,客户端的这个脚本是只执行一次就行了是吗?我想要停止播放的时候就按ESC退出,然后会又运行一遍这个shell脚本,不知道是不是这个原因,会看到pics这个文件夹下的同一张图片会有好几张。
 楼主| 发表于 2017-12-11 21:28 | 显示全部楼层
牙牙乐 发表于 2017-12-6 10:18
你好,我想请教你一下。按照你的实验过程,我做了一遍,基本能够实现功能,只不过大多数时候从微信公众号中 ...

抱歉有点忙,没怎么上线。

你这提示 grep command not found 有点奇怪,检查一下文件的命令拼写是不是正确,看有没有带上特殊的符号什么的,如果文件没有问题,那你在控制终端下执行  grep 命令看看是不是能用,这个提示是说你系统没有 grep 命令。

kill 这个提示是不是你另外有手工杀进程导致找不到脚本之前启动的 qiv 子进程了。
 楼主| 发表于 2017-12-11 21:34 | 显示全部楼层
牙牙乐 发表于 2017-12-6 10:22
还有就是,客户端的这个脚本是只执行一次就行了是吗?我想要停止播放的时候就按ESC退出,然后会又运行一遍 ...

同一个图片有好几张?不都是同名的嘛,应该要覆盖才是。另外,上一个问题中的  grep 命令就是用来过滤已经下载的图片不再下载的,由于有之前的错误所以可能重复下载了。

脚本的执行参考帖子的 11 点的截图,执行一次,加 & 号表示放后台执行,这样就可以了不用管了,退出终端都会继续执行的。

要做停止播放这个功能要另外加代码处理,简单用就这样啦,比较麻烦也没时间做,所以就匆匆完工了。
发表于 2017-12-14 09:39 | 显示全部楼层
您好,真的很感谢您的耐心解答。这几天我看了京东上买的微信相框,有播放视频的功能,我想请教您一下,有没有这样的软件可以支持这样的功能,既能播放图片又能播放视频,还有相应的播放指令,在shell脚本下执行?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 02:32 , Processed in 0.157781 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表