小成開發日記----物聯網項目LoveTv實現web網頁傳輸數據到單片機(技術棧涉及web前端,php後端,c/c++ socket,嵌入式前後端)

作者:小成Charles
原創作品
轉載請標註原創文章地址:https://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
原創作品
轉載請標註原創文章地址:https://blog.csdn.net/weixin_42999453/article/details/113502220

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章