小成開發日記----物聯網專案LoveTv實現web網頁傳輸資料到微控制器(技術棧涉及web前端,php後端,c/c++ socket,嵌入式前後端)

語言: CN / TW / HK

作者:小成Charles
原創作品
轉載請標註原創文章地址:http://blog.csdn.net/weixin_42999453/article/details/113502220

一、前言

因為看了B站上稚暉君的嵌入式開發視訊,搞得我這個軟體開發的也想弄一弄嵌入式,然後就設計了一下,做了我這個第一個物聯網專案,我稱之為LoveTV,這個是做了送給女朋友的,為了方便控制它以及傳輸資料,這裡想到就直接用網頁傳輸資料,這樣很方便!伺服器是要搭建在我的騰訊雲伺服器,也就是理論上來說,可以在任何地方實現資料傳輸。話不多說,上專案圖!
在這裡插入圖片描述

在這裡插入圖片描述

二、設計思路

上面只是雛形,後期會新增和美化更多功能!這裡我實現網頁控制換頁的方法是網頁通過點選按鈕會執行相應的PHP檔案,php後端會建立一個udp協議,然後將資料傳送給用C++建立的Udp服務端,服務端收到訊息後會把訊息再轉發給微控制器建立的Udp客戶端,微控制器客戶端收到資料後會做相應的處理。這就是大致的思路,為了方便理解,我大致的流程畫個圖。在這裡插入圖片描述

三、核心程式碼(非完整程式碼)

(1)php後端
這裡就是建立socket,指定為udpsocket,然後直接將資料傳輸給指定的伺服器IP,注意php使用socket要到php.ini檔案裡面把extension=sockets前面的分號去掉,這樣才能使用socket。

<?php
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$msg = '1';
$len = strlen($msg);
socket_sendto($sock, $msg, $len, 0, serverIp,serverPort);
header("Location: http://lovetv/index.html");
socket_close($sock);
?>

(2)c++後端
這裡主要就是實現資料的轉發,基於C++的框架Qt寫得
tvServer.h

#ifndef TVSERVER_H
#define TVSERVER_H

#include <QUdpSocket>
#include <QObject>
#include <QList>

class tvServer : public QObject
{
   
   
    Q_OBJECT
public:
    explicit tvServer(QObject *parent = nullptr);
    QUdpSocket *udpServer;
    void bindServer(quint16 port);
    quint16 port;

    QHostAddress tvAddr;
    quint16 tvPort;

    QHostAddress webAddr;
    quint16 webPort;

    bool enter;

signals:

public slots:
    void onReadyRead();
};

#endif // TVSERVER_H

tvServer.cpp

#include "tvserver.h"

tvServer::tvServer(QObject *parent) : QObject(parent)
{
   
   
    udpServer=new QUdpSocket ();
    //test ip
    tvAddr.setAddress("192.168.1.91");
    tvPort=1520;
    enter=false;
    bindServer(1520);
    connect(udpServer,&QUdpSocket::readyRead,this,&tvServer::onReadyRead);
}
void tvServer::bindServer(quint16 port)
{
   
   //bind value
//    QHostAddress addr;
//    addr.setAddress("192.168.1.110");

    if(udpServer->bind(port))
    {
   
   
        qDebug()<<"successful bind!";
    }else {
   
   
        qDebug()<<"falied to bind!";

    }



}
void tvServer::onReadyRead()
{
   
   //server to revecive
    QByteArray   datagram;
    datagram.resize(udpServer->pendingDatagramSize());
    QHostAddress    peerAddr;
    quint16 peerPort;
    udpServer->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
    if(QString(datagram.data()) =="tv"&&enter==false)
    {
   
   
       qDebug()<<datagram.data()<<peerAddr<<peerPort;
       tvAddr=peerAddr;
       tvPort=peerPort;
       udpServer->writeDatagram("replay tv",peerAddr,peerPort);
       enter=true;
    }
    if(QString(datagram.data()) =="web"){
   
   
        webAddr=peerAddr;
        webPort=peerPort;
        qDebug()<<datagram.data()<<peerAddr<<peerPort;
}
//send msg to tv;

    qDebug()<<datagram.data()<<peerAddr<<peerPort;
    udpServer->writeDatagram(datagram.data(),tvAddr,tvPort);

}

(3)嵌入式c後端
程式設計IDE用的是Arduino,然後主機板用的是ESp32,螢幕就是透明的OLED螢幕,前端不多說了,主要是利用u8g2這個圖形框架寫得,後端先連線wifi,然後利用WiFiUDP這個庫建立客戶端,一直迴圈去監聽訊息,收到訊息後做出相應的判斷;

這裡是開啟udp

  //開啟udp工具
    if(udp.begin(WiFi.localIP(),udpLocalPort))
    {
   
   
      Serial.printf("現在收聽IP:%s, UDP埠:%d\n", WiFi.localIP().toString().c_str(), udpLocalPort);
      //將wifi資訊傳輸給伺服器
      udp.beginPacket(udpServerAddr, udpServerPort);//配置遠端ip地址和埠
      udp.print("tv");//把資料寫入傳送緩衝區
      udp.endPacket();//傳送資料
     }else{
   
   
      Serial.println("監聽失敗");
      }

getUdpMsg()這裡就是一直獲得解析包,可以在loop裡面去一直呼叫這個函式獲得返回資料。

String Network::getUdpMsg()
{
   
   
//    udp.beginPacket(udpServerAddr, udpServerPort);//配置遠端ip地址和埠
//    udp.print("mesg!");//把資料寫入傳送緩衝區
//    udp.endPacket();//傳送資料

  int packetSize = udp.parsePacket();//獲得解析包
  if (packetSize)//解析包不為空
  {
   
   
    //收到Udp資料包
    //Udp.remoteIP().toString().c_str()用於將獲取的遠端IP地址轉化為字串
    Serial.printf("收到來自遠端IP:%s(遠端埠:%d)的資料包位元組數:%d\n", udp.remoteIP().toString().c_str(), udp.remotePort(), packetSize);
      
    // 解析UDP資料包中的所以資料,以字串格式返回
    String udpStringVal = udp.readString(); 
    
    // 然後向串列埠列印返回的字串
    Serial.print("開發板接收到UDP資料中的字串 "); Serial.println(udpStringVal);

    return udpStringVal;
  }
   }

這裡用了一個Chrono執行緒庫,讓這個函式一直處於一個單獨的執行緒執行
hasPassed()裡面給的引數越小,延遲就越小,單位為毫秒

  if (timeChrono.hasPassed(10) ) {
   
    // elapsed(1000) returns 1 if 1000ms have passed.
    timeChrono.restart();  // restart the Chrono 
   String msgVal= wifi.getUdpMsg();
   if(msgVal.toInt()!=0){
   
    
   nowPage=msgVal.toInt();
   Serial.println(nowPage);
}

四、總結和擴充套件

目前還處於雛形階段,就是理論已經成型了,接下來就是完善和優化了,需要改進的就是Udp協議雖然面型無連線但是由於微控制器的效能太低,網路訊號差,特別容易丟包,導致資料接收不到,那麼改進就是換成TCP協議或者對UDP協議做一個類似於TCp三次握手的資料是否接受的檢測來保證資料傳輸到了。

作者:小成Charles
原創作品
轉載請標註原創文章地址:http://blog.csdn.net/weixin_42999453/article/details/113502220