這兩天測試程式還發現一個bug就是如果用戶端斷開了,應該檢測一下哪個斷開了,資料就不應該發向那個連接配接,,,否則就會報錯,然後子產品會複位重新開機
是以加上這段代碼
conn0:on("disconnection",function(sck,c)
--print(sck)
if sck == connect0 then
connect0 = nil
print("disconnect0")
end
if sck == connect1 then
connect1 = nil
print("disconnect1")
end
if sck == connect2 then
connect2 = nil
print("disconnect2")
end
if sck == connect3 then
connect3 = nil
print("disconnect3")
end
if sck == connect4 then
connect4 = nil
print("disconnect4")
end
end)
放一個全的
ConnectCnt = 0//連接配接計數
ConnectListenFlage=0//監聽标志,隻執行一次監聽
UsartUsFlage = 0//允許序列槽資料發向網口
tmr.alarm(2, 1000, 1, function()
if NetMode == 0 then//TCP Sever模式
if ConnectListenFlage == 0 then
ConnectListenFlage = 1
srv:listen(ConnectPort,function(conn0)
UsartUsFlage = 1
--print(conn0)
if ConnectCnt == 0 then
connect0 = conn0
if connect1 ~= nil then
connect1:close()
end
connect1 = nil
print("0-Connectd")
end
if ConnectCnt == 1 then
connect1 = conn0
if connect2 ~= nil then
connect2:close()
end
connect2 = nil
print("1-Connectd")
end
if ConnectCnt == 2 then
connect2 = conn0
if connect3 ~= nil then
connect3:close()
end
connect3 = nil
print("2-Connectd")
end
if ConnectCnt == 3 then
connect3 = conn0
if connect4 ~= nil then
connect4:close()
end
connect4 = nil
print("3-Connectd")
end
if ConnectCnt == 4 then
connect4 = conn0
if connect0 ~= nil then
connect0:close()
end
connect0 = nil
print("4-Connectd")
end
conn0:on("disconnection",function(sck,c) //斷開連接配接函數
--print(sck)
if sck == connect0 then
connect0 = nil
print("disconnect0")
end
if sck == connect1 then
connect1 = nil
print("disconnect1")
end
if sck == connect2 then
connect2 = nil
print("disconnect2")
end
if sck == connect3 then
connect3 = nil
print("disconnect3")
end
if sck == connect4 then
connect4 = nil
print("disconnect4")
end
end)
conn0:on("receive",function(conn0,payload)
uart.write(0,payload)end)
--print(payload) end)
ConnectCnt = ConnectCnt+1
if ConnectCnt == 5 then
ConnectCnt = 0
end
end)
end
end
序列槽函數裡面
if UsartUsFlage == 1 then
if NetMode == 0 then
if connect0 ~= nil then
connect0:send(RevData)
end
if connect1 ~= nil then
connect1:send(RevData)
end
if connect2 ~= nil then
connect2:send(RevData)
end
if connect3 ~= nil then
connect3:send(RevData)
end
if connect4 ~= nil then
connect4:send(RevData)
end
end
後面說發資料的時候碰到00會自動把00以前的發送到序列槽,這是因為使用的是print函數
可以把這個函數修改為uart.write(0,payload)就不會出現上面的問題了
截個圖看一下
效果
如果想用手機做可以參考這篇文章,自己的協定規定在文章中
android 之TCP用戶端程式設計
---恢複内容開始---
請問一下部落格為什麼又不能直接複制粘貼圖檔了呢............
先看8266的配置,8266我是用的Lua語言寫的,,因為友善快捷....這次寫的當然比以前完善......
關于WIFI子產品可以看這幾篇
ESP8266使用詳解
NodeMCU初探
ESP8266刷AT固件與nodemcu固件
(一)Lua腳本語言入門
(二)Lua腳本語言入門
(三)Lua腳本語言入門
(四)Lua腳本語言入門
(五)Lua腳本語言入門
ESP8266使用詳解--基于Lua腳本語言
最後加上一篇有人的WIFI子產品,有人的做的也不錯,當初項目就是用的有人的
有人WIFI子產品使用詳解
自己的ESP8266是建立伺服器
我的init.lua
lighton=0
pin = 4
gpio.mode(pin,gpio.OUTPUT)
tmr.alarm(0,500,1,function()
if lighton==0 then
lighton=1
gpio.write(pin,1)
else
lighton=0
gpio.write(pin,0)
end
end)
tmr.alarm(1, 3000, 0, function()
print("dofile wifi.lua...")
dofile("wifi.lua")
end)
初始化呢,和我以前的一個地方不一樣
tmr.alarm(1, 3000, 0, function()
print("dofile wifi.lua...")
dofile("wifi.lua")
我讓子產品3s以後再去加載的wifi.lua
說一下原因,,,因為我在wifi.lua裡面設定的序列槽的資料直接發送到網口,,如果沒有這個延時加載,一下子就執行了序列槽的資料直接發送到網口
那麼下次想通過序列槽向子產品發指令或者重新寫入LUA程式就會出現問題,因為直接執行了序列槽的資料直接發送到網口,,有了這個延時我可以在複位的3s之前去操作子產品了
再看一下wifi.lua
ConnectCnt = 0
wifi.setmode(wifi.STATIONAP)
cfg={}
cfg.ssid="HiWifi8266"--子產品的WIFI信号名字
cfg.pwd="11223344"--密碼
wifi.ap.config(cfg)--寫入配置
wifi.sta.config("qqqqq","11223344")--子產品連接配接的路由器名稱和密碼
wifi.sta.connect()
tmr.alarm(2, 3000, 1, function()--3s檢測一次是否連接配接上路由器
if wifi.sta.getip() ~= nil then
tmr.stop(2)
print("Connected, IP is "..wifi.sta.getip())--列印分得的IP
end
end)
srv=net.createServer(net.TCP,28800)--建立TCP服務
tmr.alarm(3, 1000, 1, function()--1s進入一次看是否需要建立新的監聽
if ConnectCnt == 0 then
srv:listen(8080,function(conn0)
print("Connect0")
ConnectCnt = 1
if connect1 ~= nil then
connect1:close()
end
connect0 = conn0
conn0:on("receive",function(conn0,payload)
print(payload)
end)
end)
end
if ConnectCnt == 1 then
srv:listen(8080,function(conn1)
print("Connect1")
ConnectCnt = 2
if connect2 ~= nil then
connect2:close()
end
connect1 = conn1
conn1:on("receive",function(conn1,payload)
print(payload)
end)
end)
end
if ConnectCnt == 2 then
srv:listen(8080,function(conn2)
print("Connect2")
ConnectCnt = 3
if connect3 ~= nil then
connect3:close()
end
connect2 = conn2
conn2:on("receive",function(conn2,payload)
print(payload)
end)
end)
end
if ConnectCnt == 3 then
srv:listen(8080,function(conn3)
print("Connect3")
ConnectCnt = 0
if connect0 ~= nil then
connect0:close()
end
connect3 = conn3
conn3:on("receive",function(conn3,payload)
print(payload)
end)
end)
end
end)
uart.setup(0,9600,8,0,1,0)
uart.on("data",
function(data)
if connect0 ~= nil then
connect0:send(data)
end
if connect1 ~= nil then
connect1:send(data)
end
if connect2 ~= nil then
connect2:send(data)
end
if connect3 ~= nil then
connect3:send(data)
end
end, 0)
這次的建立服務可以連接配接3個用戶端,也不知道到底能連接配接多少個,,,沒測試,,就先寫了3個
注意看一下哈,我是這樣設定的
if ConnectCnt == 0 then
srv:listen(8080,function(conn0)
print("Connect0")
ConnectCnt = 1
if connect1 ~= nil then
connect1:close()
end
connect0 = conn0
conn0:on("receive",function(conn0,payload)
print(payload)
end)
end)
end
conn0連接配接就把conn1關掉,
conn1連接配接就把conn2關掉,
conn2連接配接就把conn3關掉,
conn03接就把conn0關掉,
是以呢現在做的最多可以3個用戶端連接配接,,,,改天試一試能不能做到65535個連接配接......看看最多能連接配接多少個
最後的序列槽發送就不用說了,,,隻是在原先的基礎上先判斷了是不是 nil 然後再發送
那就測試一下
連接配接子產品的無線
其實也可以不用連接配接子產品的無線,,因為子產品連接配接路由器了,是以電腦或者手機連接配接路由器後可以連接配接子產品連接配接路由器後分得的IP來測試
就用這四個來測試,用序列槽助手看資訊,,對了子產品預設内部IP:192.168.4.1
然後我挨個點選連接配接
現在連接配接了三個
現在發送資料
好再連接配接一個
再看一下發送資料
wifi子產品算是做好了
對了可以連接配接路由器測試
隻不過IP位址是剛才子產品連接配接路由器後分得的IP
連接配接路由器的好處是可以做到遠端控制
android客服端+eps8266+單片機+路由器之遠端控制系統
現在做上位機部分
整體規劃成這樣
先寫個函數擷取本機的IP位址,然後填寫到IP位址框
/// <擷取本機 IP 位址>
///
/// </summary>
/// <returns></returns>
private void getIPAddress()
{
IPAddress[] hostipspool = Dns.GetHostAddresses("");
comboBoxIPAdress.Items.Clear();
foreach (IPAddress ipa in hostipspool)
{
if (ipa.AddressFamily == AddressFamily.InterNetwork)
{
comboBoxIPAdress.Items.Add(ipa.ToString());
comboBoxIPAdress.SelectedIndex = comboBoxIPAdress.Items.Count > 0 ? 0 : -1;
}
}
}
程式加載的時候就搜尋一下
private void Form1_Load(object sender, EventArgs e)
{
getIPAddress();
}
然後在點選下拉框IP位址的下拉框的時候再搜尋一下
/// <點選IP下拉框>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void comboBoxIPAdress_DropDown(object sender, EventArgs e)
{
getIPAddress();
}
啟動一下看效果
這兩個IP是這兩個的
現在寫點選按鈕連接配接函數
先定義一個連接配接線程,一個儲存IP位址的變量,一個儲存端口号的變量,一個連接配接斷開的标志變量,還有TcpClient
public partial class Form1 : Form
{
private Thread ConnectThread;//連接配接線程
private IPAddress ipAddress;//ip位址
int Port = 0;//端口号
Boolean ConnectFlage = false;//連接配接标志
private TcpClient myTcpClient = null;// TcpClient
public Form1()
{
InitializeComponent();
}
然後寫個連接配接函數
/// <連接配接線程方法>
///
/// </summary>
private void ConnectMethod()
{
myTcpClient = new TcpClient(); //執行個體化myTcpClient
try
{
myTcpClient.Connect(ipAddress, Port);//連接配接伺服器
buttonConnect.Invoke(buttonConnectDelegate, "斷開");
ConnectFlage = true;
}
catch (Exception)
{
//異常處理函數
ConnectFlage = false;
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { myTcpClient.Close(); }
catch { }
}
}
然後在點選事件中擷取IP和端口号,然後啟動連接配接任務
/// <連接配接點選事件>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonConnect_Click(object sender, EventArgs e)
{
if (ConnectFlage == false)
{
if (string.IsNullOrEmpty(comboBoxIPAdress.Text) == false)
{
ipAddress = IPAddress.Parse(comboBoxIPAdress.Text);//擷取IP位址
Port = Convert.ToInt32(textBoxPort.Text); //擷取端口号
ConnectThread = new Thread(ConnectMethod);
ConnectThread.Start();
}
else
{
MessageBox.Show("請檢查IP位址!", "提示");
}
}
else
{
//斷開處理函數
ConnectFlage = false;
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { myTcpClient.Close(); }
catch { }
}
}
對了寫個按鈕的回調來顯示按鈕的連接配接和斷開
Boolean ConnectFlage = false;//連接配接标志
private TcpClient myTcpClient = null;// TcpClient
private delegate void ButtonConnectDelegate(string str);//定義連接配接按鈕回調
ButtonConnectDelegate buttonConnectDelegate;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
buttonConnectDelegate = new ButtonConnectDelegate(buttonConnectMethod);//執行個體化
getIPAddress();
}
回調函數
/// <打開按鈕回調函數>
///
/// </summary>
private void buttonConnectMethod(string str)
{
buttonConnect.Text = str;
}
現在可以測試一下連接配接子產品
對了這個現在子產品好像是很長時間不和子產品通信,子產品就自動斷開了服務,,,,好像為了低功耗嗎....最後看一下怎麼喚醒他,我現在是複位了一下
現在做一個函數來接收資料,然後把接收的資料顯示到資料接收的顯示框,資料框的回調函數也是必不可少的,還有定義一個接收任務
關于回調可以看一下
C#委托+回調詳解
先定義一個networkstrem用來接收和發送網絡資料流
其實C#的和JAVA的很類似
可以看一下
android 之TCP用戶端程式設計
看一下現在的工程
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace TCPClient
{
public partial class Form1 : Form
{
private Thread ConnectThread;//連接配接線程
private IPAddress ipAddress;//ip位址
int Port = 0;//端口号
Boolean ConnectFlage = false;//連接配接标志
private TcpClient myTcpClient = null;// TcpClient
private delegate void ButtonConnectDelegate(string str);//定義連接配接按鈕回調
ButtonConnectDelegate buttonConnectDelegate;
private delegate void ShowReMsgTcpDelegate(byte[] by);//定義顯示TCP接收消息回調
private ShowReMsgTcpDelegate showReMsgTcpDelegate;
private NetworkStream networkstrem = null;//網絡資料流
private Thread ReceiveThread;//接收消息線程
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
buttonConnectDelegate = new ButtonConnectDelegate(buttonConnectMethod);//執行個體化
showReMsgTcpDelegate = new ShowReMsgTcpDelegate(ShowReMsgTcpMethod);//執行個體化顯示回調
getIPAddress();
}
/// <擷取本機 IP 位址>
///
/// </summary>
/// <returns></returns>
private void getIPAddress()
{
IPAddress[] hostipspool = Dns.GetHostAddresses("");
comboBoxIPAdress.Items.Clear();
foreach (IPAddress ipa in hostipspool)
{
if (ipa.AddressFamily == AddressFamily.InterNetwork)
{
comboBoxIPAdress.Items.Add(ipa.ToString());
comboBoxIPAdress.SelectedIndex = comboBoxIPAdress.Items.Count > 0 ? 0 : -1;
}
}
}
/// <點選IP下拉框>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void comboBoxIPAdress_DropDown(object sender, EventArgs e)
{
getIPAddress();
}
/// <連接配接點選事件>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonConnect_Click(object sender, EventArgs e)
{
if (ConnectFlage == false)
{
if (string.IsNullOrEmpty(comboBoxIPAdress.Text) == false)
{
ipAddress = IPAddress.Parse(comboBoxIPAdress.Text);//擷取IP位址
Port = Convert.ToInt32(textBoxPort.Text); //擷取端口号
ConnectThread = new Thread(ConnectMethod);
ConnectThread.Start();
}
else
{
MessageBox.Show("請檢查IP位址!", "提示");
}
}
else
{
//斷開處理函數
ConnectFlage = false;
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }
catch { }
try { networkstrem.Dispose(); }
catch { }
try { myTcpClient.Close(); }
catch { }
}
}
/// <連接配接線程方法>
///
/// </summary>
private void ConnectMethod()
{
myTcpClient = new TcpClient(); //執行個體化myTcpClient
try
{
myTcpClient.Connect(ipAddress, Port);//連接配接伺服器
networkstrem = myTcpClient.GetStream();//擷取資料流
ReceiveThread = new Thread(ReceiveDataMethod);//啟動接收資料任務
ReceiveThread.Start();
buttonConnect.Invoke(buttonConnectDelegate, "斷開");
ConnectFlage = true;
}
catch (Exception)
{
//異常處理函數
ConnectFlage = false;
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }//銷毀任務
catch { }
try { networkstrem.Dispose(); }//釋放資源
catch { }
try { myTcpClient.Close(); }//關閉TCP
catch { }
}
}
/// <打開按鈕回調函數>
///
/// </summary>
private void buttonConnectMethod(string str)
{
buttonConnect.Text = str;
}
/// <顯示序列槽接收到的資訊--回調函數>
///
/// </summary>
private void ShowReMsgTcpMethod(byte[] by)
{
string getMsg = string.Empty;
if (checkBoxHexShow.Checked)//16進制顯示
{
getMsg = byteToHexStr(by); //用到函數byteToHexStr--位元組數組轉16進制字元串
}
else
{
getMsg = new ASCIIEncoding().GetString(by);
}
textBoxDataRes.AppendText(getMsg);
}
/// <位元組數組轉16進制字元串>
///
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string byteToHexStr(byte[] bytes)
{
string returnStr = "";
try
{
if (bytes != null)
{
for (int i = 0; i < bytes.Length; i++)
{
returnStr += bytes[i].ToString("X2");
}
}
return returnStr;
}
catch (Exception)
{
return returnStr;
}
}
/// <接收消息線程>
///
/// </summary>
private void ReceiveDataMethod()
{
int RecvCnt = 0;
byte[] recvBytes = new byte[1024];
while (true)
{
try
{
if ((myTcpClient.Client.Poll(20, SelectMode.SelectRead)) && (myTcpClient.Client.Available == 0))
{
myTcpClient.Close();//引發異常
}
RecvCnt = networkstrem.Read(recvBytes, 0, recvBytes.Length);
byte[] receive = new byte[RecvCnt];//設定緩沖區 RecvCnt 個位元組
for (int i = 0; i < receive.Length; i++)
{
receive[i] = recvBytes[i];
}
textBoxDataRes.Invoke(showReMsgTcpDelegate, receive);
}
catch (Exception)
{
//異常處理函數
ConnectFlage = false;
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }//銷毀任務
catch { }
try { networkstrem.Dispose(); }//釋放資源
catch { }
try { myTcpClient.Close(); }//關閉TCP
catch { }
}
}
}
}
}
看一下顯示回調函數
/// <顯示序列槽接收到的資訊--回調函數>
///
/// </summary>
private void ShowReMsgTcpMethod(byte[] by)
{
string getMsg = string.Empty;
if (checkBoxHexShow.Checked)//16進制顯示
{
getMsg = byteToHexStr(by); //用到函數byteToHexStr--位元組數組轉16進制字元串
}
else
{
getMsg = new ASCIIEncoding().GetString(by);
}
textBoxDataRes.AppendText(getMsg);
}
這個函數 byteToHexStr(by); //用到函數byteToHexStr--位元組數組轉16進制字元串
/// <位元組數組轉16進制字元串>
///
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string byteToHexStr(byte[] bytes)
{
string returnStr = "";
try
{
if (bytes != null)
{
for (int i = 0; i < bytes.Length; i++)
{
returnStr += bytes[i].ToString("X2");
}
}
return returnStr;
}
catch (Exception)
{
return returnStr;
}
}
那麼接收資料的函數
/// <接收消息線程>
///
/// </summary>
private void ReceiveDataMethod()
{
int RecvCnt = 0;
byte[] recvBytes = new byte[1024];
while (true)
{
try
{
if ((myTcpClient.Client.Poll(20, SelectMode.SelectRead)) && (myTcpClient.Client.Available == 0))
{
myTcpClient.Close();//引發異常
}
RecvCnt = networkstrem.Read(recvBytes, 0, recvBytes.Length);
byte[] receive = new byte[RecvCnt];//設定緩沖區 RecvCnt 個位元組
for (int i = 0; i < receive.Length; i++)
{
receive[i] = recvBytes[i];
}
textBoxDataRes.Invoke(showReMsgTcpDelegate, receive);
}
catch (Exception)
{
//異常處理函數
ConnectFlage = false;
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }//銷毀任務
catch { }
try { networkstrem.Dispose(); }//釋放資源
catch { }
try { myTcpClient.Close(); }//關閉TCP
catch { }
}
}
}
裡面的這裡是判斷TCP是不是斷開了連接配接,,如果斷開了執行myTcpClient.Close()就會引發異常
if ((myTcpClient.Client.Poll(20, SelectMode.SelectRead)) && (myTcpClient.Client.Available == 0))
{
myTcpClient.Close();//引發異常
}
現在連接配接子產品然後用序列槽助手發資料
已經能接收到資料了,現在呢,做發送資料
/// <發送按鈕點選事件>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonSend_Click(object sender, EventArgs e)
{
if (!checkBoxHexSend.Checked)//字元發送
{
byte[] sendbyte = Encoding.Default.GetBytes(textBoxSend.Text);
try { networkstrem.Write(sendbyte, 0, sendbyte.Length); }
catch (Exception) { MessageBox.Show("請檢查連接配接", "提示!"); }
}
else//16形式進制發送
{
byte[] sendbyte = strToToHexByte(textBoxSend.Text);//字元串轉16進制格式,不夠自動前面補零
try { networkstrem.Write(sendbyte, 0, sendbyte.Length); }
catch (Exception) { MessageBox.Show("請檢查連接配接", "提示!"); }
}
}
/// <字元串轉16進制格式,不夠自動前面補零>
///
/// </summary>
/// <param name="hexString"></param>
/// <returns></returns>
private static byte[] strToToHexByte(String hexString)
{
int i;
bool Flag = false;
hexString = hexString.Replace(" ", "");//清除空格
if ((hexString.Length % 2) != 0)
{
Flag = true;
}
if (Flag == true)
{
byte[] returnBytes = new byte[(hexString.Length + 1) / 2];
try
{
for (i = 0; i < (hexString.Length - 1) / 2; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, '0'), 16);
}
catch
{
for (i = 0; i < returnBytes.Length; i++)
{
returnBytes[i] = 0;
}
MessageBox.Show("超過16進制範圍A-F,已初始化為0", "提示");
}
return returnBytes;
}
else
{
byte[] returnBytes = new byte[(hexString.Length) / 2];
try
{
for (i = 0; i < returnBytes.Length; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
}
catch
{
for (i = 0; i < returnBytes.Length; i++)
{
returnBytes[i] = 0;
}
MessageBox.Show("超過16進制範圍A-F,已初始化為0", "提示");
}
return returnBytes;
}
}
現在測試一下發送資料
對了
當點選這兩個的時候,對應資料框裡面的内容應該相應的變化
直接在點選函數裡面設定
/// <發送Hex和字元串選擇>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void checkBoxHexSend_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxHexSend.Checked)
{
try
{
byte[] by = StringToByte(textBoxSend.Text);
textBoxSend.Clear();
textBoxSend.BeginInvoke(showSeMsgTcpDelegate, by);
}
catch (Exception)
{
//MessageBox.Show(ex.ToString());
}
}
else
{
try
{
byte[] by = strToToHexByte(textBoxSend.Text);
textBoxSend.Clear();
textBoxSend.BeginInvoke(showSeMsgTcpDelegate, by);
}
catch (Exception)
{
//MessageBox.Show(ex.ToString());
}
}
}
/// <接收Hex和字元串選擇>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void checkBoxHexShow_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxHexShow.Checked)
{
try
{
byte[] by = StringToByte(textBoxDataRes.Text);
textBoxDataRes.BeginInvoke(showReMsgTcpDelegate, by);
}
catch (Exception)
{
//MessageBox.Show(ex.ToString());
}
}
else
{
try
{
byte[] by = strToToHexByte(textBoxDataRes.Text);
textBoxDataRes.Clear();
textBoxDataRes.BeginInvoke(showReMsgTcpDelegate, by);
}
catch (Exception)
{
//MessageBox.Show(ex.ToString());
}
}
}
其實在優化應是用回調來做,,,,等做下一個畫波形圖的時候在說,現在做界面看不出來卡頓
現在呢做的差不多了,,規定一下協定
上位機發送
0xaa,0x55,0x01,CRC//讀取采集的電壓資料
單片機傳回
0xaa,0x55,0x01,采集電壓資料AD0(4位元組Float型),AD1,AD2,AD3,CRC//單片機傳回
//上位機發送
0xaa,0x55,0x02,設定的輸出電壓(單位元組),CRC//設定輸出的電壓
就寫一個任務,,友善點用定時器吧,,然後如果判斷連接配接上伺服器了每隔100ms發送一次采集資料的指令
/// <定時發送事件>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
byte[] sendbyte = new byte[3];
sendbyte[0] = 0xaa;
sendbyte[1] = 0x55;
sendbyte[2] = 0x01;
SerialSend(sendbyte);
}
/// <網絡發送資料函數>
///
/// </summary>
/// <param name="sendbyte"></param>
private void SerialSend(byte[] byt)
{
int crc = 0;
byte[] sendbyte = new byte[byt.Length + 2];
for (int i = 0; i < byt.Length; i++)
{
sendbyte[i] = byt[i];
}
crc = crc16_modbus(byt, byt.Length);//計算CRC
byte[] Crcbyte = System.BitConverter.GetBytes(crc);//得到CRC
sendbyte[sendbyte.Length - 2] = Crcbyte[0];
sendbyte[sendbyte.Length - 1] = Crcbyte[1];
try
{
networkstrem.Write(sendbyte, 0, sendbyte.Length);
}
catch (Exception)
{
//異常處理函數
ConnectFlage = false;
timer1.Stop();//停止定時器
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }//銷毀任務
catch { }
try { networkstrem.Dispose(); }//釋放資源
catch { }
try { myTcpClient.Close(); }//關閉TCP
catch { }
MessageBox.Show("請檢查連接配接", "提示!");
}
}
定時器呢在這裡面啟動
/// <打開按鈕回調函數>
///
/// </summary>
private void buttonConnectMethod(string str)
{
if (str == "斷開")
{
timer1.Enabled = true;
timer1.Start();//啟動定時器
}
else
{
timer1.Enabled = false;
timer1.Stop();//停止定時器
}
buttonConnect.Text = str;
}
現在的代碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace TCPClient
{
public partial class Form1 : Form
{
private Thread ConnectThread;//連接配接線程
private IPAddress ipAddress;//ip位址
int Port = 0;//端口号
Boolean ConnectFlage = false;//連接配接标志
private TcpClient myTcpClient = null;// TcpClient
private delegate void ButtonConnectDelegate(string str);//定義連接配接按鈕回調
ButtonConnectDelegate buttonConnectDelegate;
private delegate void ShowReMsgTcpDelegate(byte[] by);//定義顯示TCP接收消息回調
private ShowReMsgTcpDelegate showReMsgTcpDelegate;
private NetworkStream networkstrem = null;//網絡資料流
private Thread ReceiveThread;//接收消息線程
private Thread ShowDataThread;//顯示任務,點選Hex顯示時
private delegate void ShowSeMsgTcpDelegate(byte[] by);//定義顯示TCP發送消息回調
private ShowSeMsgTcpDelegate showSeMsgTcpDelegate;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Stop();//啟動定時器
buttonConnectDelegate = new ButtonConnectDelegate(buttonConnectMethod);//執行個體化
showReMsgTcpDelegate = new ShowReMsgTcpDelegate(ShowReMsgTcpMethod);//執行個體化顯示回調
showSeMsgTcpDelegate = new ShowSeMsgTcpDelegate(showSeMsgTcpMethod);//執行個體化發送回調
getIPAddress();
}
/// <擷取本機 IP 位址>
///
/// </summary>
/// <returns></returns>
private void getIPAddress()
{
IPAddress[] hostipspool = Dns.GetHostAddresses("");
comboBoxIPAdress.Items.Clear();
foreach (IPAddress ipa in hostipspool)
{
if (ipa.AddressFamily == AddressFamily.InterNetwork)
{
comboBoxIPAdress.Items.Add(ipa.ToString());
comboBoxIPAdress.SelectedIndex = comboBoxIPAdress.Items.Count > 0 ? 0 : -1;
}
}
}
/// <點選IP下拉框>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void comboBoxIPAdress_DropDown(object sender, EventArgs e)
{
getIPAddress();
}
/// <連接配接點選事件>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonConnect_Click(object sender, EventArgs e)
{
if (ConnectFlage == false)
{
if (string.IsNullOrEmpty(comboBoxIPAdress.Text) == false)
{
ipAddress = IPAddress.Parse(comboBoxIPAdress.Text);//擷取IP位址
Port = Convert.ToInt32(textBoxPort.Text); //擷取端口号
ConnectThread = new Thread(ConnectMethod);
ConnectThread.Start();
}
else
{
MessageBox.Show("請檢查IP位址!", "提示");
}
}
else
{
//斷開處理函數
ConnectFlage = false;
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }
catch { }
try { networkstrem.Dispose(); }
catch { }
try { myTcpClient.Close(); }
catch { }
}
}
/// <連接配接線程方法>
///
/// </summary>
private void ConnectMethod()
{
myTcpClient = new TcpClient(); //執行個體化myTcpClient
try
{
myTcpClient.Connect(ipAddress, Port);//連接配接伺服器
networkstrem = myTcpClient.GetStream();//擷取資料流
ReceiveThread = new Thread(ReceiveDataMethod);//啟動接收資料任務
ReceiveThread.Start();
buttonConnect.Invoke(buttonConnectDelegate, "斷開");
ConnectFlage = true;
}
catch (Exception)
{
//異常處理函數
ConnectFlage = false;
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }//銷毀任務
catch { }
try { networkstrem.Dispose(); }//釋放資源
catch { }
try { myTcpClient.Close(); }//關閉TCP
catch { }
}
}
/// <打開按鈕回調函數>
///
/// </summary>
private void buttonConnectMethod(string str)
{
if (str == "斷開")
{
timer1.Enabled = true;
timer1.Start();//啟動定時器
}
else
{
timer1.Enabled = false;
timer1.Stop();//停止定時器
}
buttonConnect.Text = str;
}
/// <顯示序列槽接收到的資訊--回調函數>
///
/// </summary>
private void ShowReMsgTcpMethod(byte[] by)
{
string getMsg = string.Empty;
if (checkBoxHexShow.Checked)//16進制顯示
{
getMsg = byteToHexStr(by); //用到函數byteToHexStr--位元組數組轉16進制字元串
}
else
{
getMsg = new ASCIIEncoding().GetString(by);
}
textBoxDataRes.AppendText(getMsg);
}
private void showSeMsgTcpMethod(byte[] by)
{
string getMsg = string.Empty;
if (checkBoxHexSend.Checked)//16進制顯示
{
getMsg = byteToHexStr(by); //用到函數byteToHexStr--位元組數組轉16進制字元串
}
else
{
getMsg = new ASCIIEncoding().GetString(by);
}
textBoxSend.AppendText(getMsg);
}
/// <位元組數組轉16進制字元串>
///
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string byteToHexStr(byte[] bytes)
{
string returnStr = "";
try
{
if (bytes != null)
{
for (int i = 0; i < bytes.Length; i++)
{
returnStr += bytes[i].ToString("X2");
}
}
return returnStr;
}
catch (Exception)
{
return returnStr;
}
}
/// <接收消息線程>
///
/// </summary>
private void ReceiveDataMethod()
{
int RecvCnt = 0;
byte[] recvBytes = new byte[1024];
while (true)
{
try
{
if ((myTcpClient.Client.Poll(20, SelectMode.SelectRead)) && (myTcpClient.Client.Available == 0))
{
myTcpClient.Close();//引發異常
}
RecvCnt = networkstrem.Read(recvBytes, 0, recvBytes.Length);
byte[] receive = new byte[RecvCnt];//設定緩沖區 RecvCnt 個位元組
for (int i = 0; i < receive.Length; i++)
{
receive[i] = recvBytes[i];
}
textBoxDataRes.Invoke(showReMsgTcpDelegate, receive);
}
catch (Exception)
{
//異常處理函數
ConnectFlage = false;
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }//銷毀任務
catch { }
try { networkstrem.Dispose(); }//釋放資源
catch { }
try { myTcpClient.Close(); }//關閉TCP
catch { }
}
}
}
/// <發送按鈕點選事件>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonSend_Click(object sender, EventArgs e)
{
if (!checkBoxHexSend.Checked)//字元發送
{
byte[] sendbyte = Encoding.Default.GetBytes(textBoxSend.Text);
try { networkstrem.Write(sendbyte, 0, sendbyte.Length); }
catch (Exception) { MessageBox.Show("請檢查連接配接", "提示!"); }
}
else//16形式進制發送
{
byte[] sendbyte = strToToHexByte(textBoxSend.Text);//字元串轉16進制格式,不夠自動前面補零
try { networkstrem.Write(sendbyte, 0, sendbyte.Length); }
catch (Exception) { MessageBox.Show("請檢查連接配接", "提示!"); }
}
}
/// <字元串轉16進制格式,不夠自動前面補零>
///
/// </summary>
/// <param name="hexString"></param>
/// <returns></returns>
private static byte[] strToToHexByte(String hexString)
{
int i;
bool Flag = false;
hexString = hexString.Replace(" ", "");//清除空格
if ((hexString.Length % 2) != 0)
{
Flag = true;
}
if (Flag == true)
{
byte[] returnBytes = new byte[(hexString.Length + 1) / 2];
try
{
for (i = 0; i < (hexString.Length - 1) / 2; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, '0'), 16);
}
catch
{
for (i = 0; i < returnBytes.Length; i++)
{
returnBytes[i] = 0;
}
MessageBox.Show("超過16進制範圍A-F,已初始化為0", "提示");
}
return returnBytes;
}
else
{
byte[] returnBytes = new byte[(hexString.Length) / 2];
try
{
for (i = 0; i < returnBytes.Length; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
}
catch
{
for (i = 0; i < returnBytes.Length; i++)
{
returnBytes[i] = 0;
}
MessageBox.Show("超過16進制範圍A-F,已初始化為0", "提示");
}
return returnBytes;
}
}
/// <字元串轉換成位元組數組>
///
/// </summary>
/// <param name="stringToConvert"></param>
/// <returns></returns>
public static byte[] StringToByte(string stringToConvert)
{
return (new ASCIIEncoding()).GetBytes(stringToConvert);
}
/// <發送Hex和字元串選擇>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void checkBoxHexSend_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxHexSend.Checked)
{
try
{
byte[] by = StringToByte(textBoxSend.Text);
textBoxSend.Clear();
textBoxSend.BeginInvoke(showSeMsgTcpDelegate, by);
}
catch (Exception)
{
//MessageBox.Show(ex.ToString());
}
}
else
{
try
{
byte[] by = strToToHexByte(textBoxSend.Text);
textBoxSend.Clear();
textBoxSend.BeginInvoke(showSeMsgTcpDelegate, by);
}
catch (Exception)
{
//MessageBox.Show(ex.ToString());
}
}
}
/// <接收Hex和字元串選擇>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void checkBoxHexShow_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxHexShow.Checked)
{
try
{
byte[] by = StringToByte(textBoxDataRes.Text);
textBoxDataRes.BeginInvoke(showReMsgTcpDelegate, by);
}
catch (Exception)
{
//MessageBox.Show(ex.ToString());
}
}
else
{
try
{
byte[] by = strToToHexByte(textBoxDataRes.Text);
textBoxDataRes.Clear();
textBoxDataRes.BeginInvoke(showReMsgTcpDelegate, by);
}
catch (Exception)
{
//MessageBox.Show(ex.ToString());
}
}
}
/// <清除接收>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonClearRecv_Click(object sender, EventArgs e)
{
textBoxDataRes.Clear();
}
/// <清除發送>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonClearSend_Click(object sender, EventArgs e)
{
textBoxSend.Clear();
}
/// <設定DA輸出>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trackBarDAout_Scroll(object sender, EventArgs e)
{
textBoxDAout.Text = trackBarDAout.Value.ToString();
}
/// <CRC 效驗 >
///
/// </summary>
/// <param name="modbusdata"></param>
/// <param name="length"></param>
/// <returns></returns>
private int crc16_modbus(byte[] modbusdata, int length)
{
int i, j;
int crc = 0xffff;
try
{
for (i = 0; i < length; i++)
{
crc ^= modbusdata[i];
for (j = 0; j < 8; j++)
{
if ((crc & 0x01) == 1)
{
crc = (crc >> 1) ^ 0xa001;
}
else
{
crc >>= 1;
}
}
}
}
catch (Exception)
{
throw;
}
return crc;
}
/*
modbusdata 要校驗的資料
length 資料長度
傳回值 1 正确 0 錯誤
*/
private int crc16_flage(byte[] modbusdata, int length)
{
int Receive_CRC = 0, calculation = 0;//接收到的CRC,計算的CRC
Receive_CRC = crc16_modbus(modbusdata, length);
calculation = modbusdata[length + 1];
calculation <<= 8;
calculation += modbusdata[length];
if (calculation != Receive_CRC)
{
return 0;
}
return 1;
}
/// <定時發送事件>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
byte[] sendbyte = new byte[3];
sendbyte[0] = 0xaa;
sendbyte[1] = 0x55;
sendbyte[2] = 0x01;
SerialSend(sendbyte);
}
/// <網絡發送資料函數>
///
/// </summary>
/// <param name="sendbyte"></param>
private void SerialSend(byte[] byt)
{
int crc = 0;
byte[] sendbyte = new byte[byt.Length + 2];
for (int i = 0; i < byt.Length; i++)
{
sendbyte[i] = byt[i];
}
crc = crc16_modbus(byt, byt.Length);//計算CRC
byte[] Crcbyte = System.BitConverter.GetBytes(crc);//得到CRC
sendbyte[sendbyte.Length - 2] = Crcbyte[0];
sendbyte[sendbyte.Length - 1] = Crcbyte[1];
try
{
networkstrem.Write(sendbyte, 0, sendbyte.Length);
}
catch (Exception)
{
//異常處理函數
ConnectFlage = false;
timer1.Stop();//停止定時器
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }//銷毀任務
catch { }
try { networkstrem.Dispose(); }//釋放資源
catch { }
try { myTcpClient.Close(); }//關閉TCP
catch { }
MessageBox.Show("請檢查連接配接", "提示!");
}
}
}
}
View Code
測試一下
然後呢,設定一下這個好弄得
協定的規定
//上位機發送
0xaa,0x55,0x02,設定的輸出電壓(單位元組),CRC//設定輸出的電壓
/// <設定DA輸出>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trackBarDAout_Scroll(object sender, EventArgs e)
{
byte[] sendbyte = new byte[4];
sendbyte[0] = 0xaa;
sendbyte[1] = 0x55;
sendbyte[2] = 0x02;
sendbyte[3] = Convert.ToByte(trackBarDAout.Value);
SerialSend(sendbyte);
textBoxDAout.Text = trackBarDAout.Value.ToString();
}
隻要滑動這個滑動條就把目前滑動條的資料發給Wifi子產品
現在寫接收資料的函數,然後顯示在相應的位置
/// <顯示序列槽接收到的資訊--回調函數>
///
/// </summary>
private void ShowReMsgTcpMethod(byte[] by)
{
string getMsg = string.Empty;
if (checkBoxHexShow.Checked)//16進制顯示
{
getMsg = byteToHexStr(by); //用到函數byteToHexStr--位元組數組轉16進制字元串
}
else
{
getMsg = new ASCIIEncoding().GetString(by);
}
if (by[0] == 0xaa && by[1] == 0x55)
{
if (crc16_flage(by, by.Length - 2) == 1)//CRC校驗正确
{
if (by[2] == 0x01)
{
float f1 = BitConverter.ToSingle(by, 3);
float f2 = BitConverter.ToSingle(by, 7);
float f3 = BitConverter.ToSingle(by, 11);
float f4 = BitConverter.ToSingle(by, 15);
textBoxAD0.Text = f1.ToString();
textBoxAD1.Text = f2.ToString();
textBoxAD2.Text = f3.ToString();
textBoxAD3.Text = f4.ToString();
}
}
}
textBoxDataRes.AppendText(getMsg);
}
就加上這幾句
if (by[0] == 0xaa && by[1] == 0x55)
{
if (crc16_flage(by, by.Length - 2) == 1)//CRC校驗正确
{
if (by[2] == 0x01)
{
float f1 = BitConverter.ToSingle(by, 3);
float f2 = BitConverter.ToSingle(by, 7);
float f3 = BitConverter.ToSingle(by, 11);
float f4 = BitConverter.ToSingle(by, 15);
textBoxAD0.Text = f1.ToString();
textBoxAD1.Text = f2.ToString();
textBoxAD2.Text = f3.ToString();
textBoxAD3.Text = f4.ToString();
}
}
}
關于FLoat的轉換,先給大家一篇文章
連結:http://pan.baidu.com/s/1ck2m7O%20密碼:rmsg
單片機程式中會做轉換介紹,其實就是用共用體,,上位機呢就是用那個方法解析
現在測試一下,把資料220.5發給上位機
好了上位機測試沒有問題了
我後來改了一些地方
協定變成了一條
0xaa,0x55,0x01,(設定的電壓值),CRC16校驗
/// <定時發送事件>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
byte[] sendbyte = new byte[4];
sendbyte[0] = 0xaa;
sendbyte[1] = 0x55;
sendbyte[2] = 0x01;
sendbyte[3] = trackBarValue[0];//滑動條的值
SerialSend(sendbyte);
}
滑動控件的事件
/// <設定DA輸出>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trackBarDAout_Scroll(object sender, EventArgs e)
{
trackBarValue[0] = Convert.ToByte(trackBarDAout.Value);
textBoxDAout.Text = trackBarDAout.Value.ToString();
}
這裡面加了個try,,是因為不能保證資料傳輸的時候沒有錯誤的時候
/// <顯示序列槽接收到的資訊--回調函數>
///
/// </summary>
private void ShowReMsgTcpMethod(byte[] by)
{
string getMsg = string.Empty;
try
{
if (checkBoxHexShow.Checked)//16進制顯示
{
getMsg = byteToHexStr(by); //用到函數byteToHexStr--位元組數組轉16進制字元串
}
else
{
getMsg = new ASCIIEncoding().GetString(by);
}
if (by[0] == 0xaa && by[1] == 0x55)
{
if (crc16_flage(by, by.Length - 2) == 1)//CRC校驗正确
{
if (by[2] == 0x01)
{
float f1 = BitConverter.ToSingle(by, 3);
float f2 = BitConverter.ToSingle(by, 7);
float f3 = BitConverter.ToSingle(by, 11);
float f4 = BitConverter.ToSingle(by, 15);
textBoxAD0.Text = f1.ToString();
textBoxAD1.Text = f2.ToString();
textBoxAD2.Text = f3.ToString();
textBoxAD3.Text = f4.ToString();
}
}
}
}
catch (Exception)
{
}
textBoxDataRes.AppendText(getMsg);
}
現在做單片機的程式
程式呢說幾個地方
typedef union Resolverf//51是大端模式,32預設小端模式
{
float Data;
unsigned char Data_Table[4];
}ResolverfData;
typedef union ResolverLong
{
long Data;
unsigned char Data_Table[4];
}ResolverLongData;
最後做完了發現了一個問題,這個子產品刷入Lua語言後,把資料通過WIFI發給子產品,子產品内部預設的是遇到'\0'後認為是一條資料,,是以呢
當發送的byte數組裡面的值是0的時候資料就會出現問題,直接把0以前的資料發過去了.
是以自己做了下設定
/// <網絡發送資料函數>
///
/// </summary>
/// <param name="sendbyte"></param>
private void SerialSend(byte[] byt)
{
int crc = 0;
byte[] sendbyte = new byte[byt.Length + 4];//加一個檢測資料的,CRC,最後結尾判斷CRC
byte[] Change0 = new byte[1];//檢測資料用
Change0[0] = 0xff;//初始化為FF,哪位資料為0就把相應的資料位變為1,檢測的為變為0
for (int i = 0; i < byt.Length; i++)
{
sendbyte[i] = byt[i];
if (sendbyte[i] == 0x00)
{
sendbyte[i] = 0x01;
switch(i)
{
case 0: Change0[0] &= 0xfe; break;
case 1: Change0[0] &= 0xfd; break;
case 2: Change0[0] &= 0xfb; break;
case 3: Change0[0] &= 0xf7; break;
case 4: Change0[0] &= 0xef; break;
case 5: Change0[0] &= 0xdf; break;
case 6: Change0[0] &= 0xbf; break;
}
}
}
sendbyte[sendbyte.Length-4] = Change0[0];
crc = crc16_modbus(sendbyte, sendbyte.Length-3);//計算CRC
byte[] Crcbyte = System.BitConverter.GetBytes(crc);//得到CRC
sendbyte[sendbyte.Length - 3] = Crcbyte[0];
sendbyte[sendbyte.Length - 2] = Crcbyte[1];
sendbyte[sendbyte.Length - 1] = 0xff;
if (sendbyte[sendbyte.Length - 3] == 0x00)
{
sendbyte[sendbyte.Length - 3] = 0x01;
sendbyte[sendbyte.Length - 1] = 0xF0;
}
if (sendbyte[sendbyte.Length - 2] == 0x00)
{
sendbyte[sendbyte.Length - 2] = 0x01;
sendbyte[sendbyte.Length - 1] = 0x0F;
}
try
{
if (networkstrem.CanWrite)
{
networkstrem.Write(sendbyte, 0, sendbyte.Length);
}
}
catch (Exception)
{
//異常處理函數
ConnectFlage = false;
timer1.Stop();//停止定時器
buttonConnect.Invoke(buttonConnectDelegate, "連接配接");
try { ReceiveThread.Abort(); }//銷毀任務
catch { }
try { networkstrem.Dispose(); }//釋放資源
catch { }
try { myTcpClient.Close(); }//關閉TCP
catch { }
MessageBox.Show("請檢查連接配接", "提示!");
}
}
想法就是在資料的後面用一個位元組來辨別前面的資料哪些是0,對了如果資料是0,就把資料改為1,
0xaa, 0x55, 0x01, DA值, 辨別前面哪些資料是0, CRC低位, CRC高位, 辨別CRC有沒有0
假如說 DA值是0,那麼把資料變成0x01,然後辨別下
0xaa,0x55,0x01,0x01,0xf7,CRC低位,CRC高位,0xFF(假設校驗沒有0)
如果CRC低位也是0那麼
0xaa,0x55,0x01,0x01,0xf7,CRC低位,CRC高位,0xF0
如果CRC高位也是0那麼
0xaa,0x55,0x01,0x01,0xf7,CRC低位,CRC高位,0x0F
其實可以用字元串格式發送,就不用麻煩了
單片機解析
/**
* @brief 資料發送方法
* @param
* @param None
* @param None
* @retval None
* @example
**/
void DataSendMethod()
{
if(UsartFlage == 1)//接收到資料
{
if(UsartReceive[7] != 0xff)//CRC校驗有0資料
{
if(UsartReceive[7] == 0xf0)//看一下CRC低位是0還是高位是0
{UsartReceive[5] = 0;}
if(UsartReceive[7] == 0x0f)
{UsartReceive[6] = 0;}
}
if(crc16_flage(UsartReceive, UsartReadCntCopy-5))//校驗資料
{
if(UsartReceive[4]!=0xFF)//有0資料
{
switch(UsartReceive[4])//找到哪一位是0資料,修改過來
{
case 0xFE: UsartReceive[0] = 0; break;//0位
case 0xFD: UsartReceive[1] = 0; break;//1位
case 0xFB: UsartReceive[2] = 0; break;//2位
case 0xF7: UsartReceive[3] = 0; break;//3位
case 0xEF: UsartReceive[4] = 0; break;//4位
case 0xDF: UsartReceive[5] = 0; break;//5位
case 0xBF: UsartReceive[6] = 0; break;//6位
}
}
if(UsartReceive[0] == 0xaa && UsartReceive[1] == 0x55)
{
UsartFlage = 0;
switch(UsartReceive[2])
{
case 0x01: SetDAValue(); SendADValue(); break;//設定輸出的電壓,發送采集的電壓
default: break;
}
}
}
else
{
UsartFlage = 0;
}
}
}
一,關于大小端
關于大小端---資料的低位存在低位址,高位存在高位址就是小端模式
關于大小端---資料的低位存在高位址,高位存在低位址就是大端模式
傳輸float資料直接用共用體解決,可以看看上面的青智儀表的文章,,,這個用的挺多的,,幾乎所有裝置的浮點型資料都是這樣存儲的,,,,,,
二,51單片機使用printf,,,我程式裡能使用printf,,不過說一點注意的地方
如果51想用printf發送資料,加上這個函數,其實也可以不加,,,不過必須在初始化的時候TI必須一直為 1 但是這樣的話就會有問題,程式一直進入序列槽中斷......會造成主程式卡卡卡卡.......
/* 用自帶的printf 發送資料 */
char putchar(char c)
{
UartSend(c);
return c;
}
别忘了包含
#include "stdio.h"
#include <stdarg.h>
可以看一下這篇文章
http://blog.csdn.net/googlemi/article/details/8996605
發送資料
printf("%d",(unsigned int)Value);注意Value如果是char 或者 unsigned char 這樣的時候要加強制轉換
可以看一下這篇
http://taihang604.blog.163.com/blog/static/20834216520125185579173/
三,關于51單片機裡面的CRC16校驗
記住:操作的資料,,定義的資料類型要定義成 unsigned char 或者 unsigned int 類型的,,否則會算錯的
/**
* @brief 計算CRC
* @param *modbusdata:資料指針
* @param length:資料長度
* @param
* @retval 計算的CRC值
* @example
**/
unsigned int crc16_modbus(unsigned char *modbusdata, char length)
{
char i, j;
unsigned int crc = 0xffff;
for (i = 0; i < length; i++)
{
crc ^= modbusdata[i];
for (j = 0; j < 8; j++)
{
if ((crc & 0x01) == 1)
{
crc = (crc >> 1) ^ 0xa001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
/**
* @brief 判斷資料的CRC校驗是否正确
* @param *modbusdata:資料指針
* @param length:資料長度
* @param
* @retval 1 正确 0 錯誤
* @example
**/
char crc16_flage(unsigned char *modbusdata, char length)
{
unsigned int Receive_CRC=0,calculation=0;//接收到的CRC,計算的CRC
int i = 0;
Receive_CRC = crc16_modbus(modbusdata, length);
calculation = modbusdata[length+1];
calculation <<= 8;
calculation += modbusdata[length];
if(calculation != Receive_CRC)
{
return 0;
}
return 1;
}
四,51單片機的發送資料我是用中斷發送的
隻需要把準備好的資料調用這個函數
/**
* @brief 序列槽發送函數中斷函數
* @param
* @param None
* @param None
* @retval None
* @example
**/
void UartSendTI(unsigned char *value,int DataLen)
{
UsartSendData = value;
UsartSendDataCnt = DataLen;
TI = 1;
}
/**
* @brief 序列槽發送函數中斷函數
* @param
* @param None
* @param None
* @retval None
* @example
**/
void UartSendTI(unsigned char *value,int DataLen)
{
UsartSendData = value;
UsartSendDataCnt = DataLen;
TI = 1;
}
void UARTInterrupt(void) interrupt 4
{
if(RI)
{
RI=0;
UsartReceive[UsartReadCnt]=SBUF;//接收序列槽資料
UsartReadCnt++;
if(UsartReadCnt>8)//防止溢出
{
UsartReadCnt = 0;
}
}
if(TI)
{
TI = 0;
if(UsartSendDataCnt>0)
{
SBUF = *UsartSendData++;
UsartSendDataCnt--;
}
else
{
TI = 0;
}
}
}
五,序列槽接收和以前一樣用的空閑檢測
void Timer0Interrupt(void) interrupt 1
{
TH0 = (65536 - 1000)/256;
TL0 = (65536 - 1000)%256;
//TimeCnt ++;
if (UsartReadCnt != 0)//如果接收到資料了
{
if (UsartIdleCnt == UsartReadCnt)//1ms時間資料沒了變化
{
UsartReadCntCopy = UsartReadCnt;
UsartReadCnt = 0;//清零資料個數
UsartIdleCnt = 0;//清零
UsartFlage = 1;
}
else
{
UsartIdleCnt = UsartReadCnt;
}
}
}
說一下IIC,,因為當初一個學弟問過我,簡要說一下
IIC協定規定發送資料的時候要先發一個起始信号,,,也就是告訴對方我開始和你通信了
函數原型: void Start_I2c();
功能: 啟動I2C總線,即發送I2C起始條件.
********************************************************************/
void Start_I2c()
{
SDA=1; /*發送起始條件的資料信号*/
_Nop();
SCL=1;
_Nop(); /*起始條件建立時間大于4.7us,延時*/
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0; /*發送起始信号*/
_Nop(); /* 起始條件鎖定時間大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0; /*鉗住I2C總線,準備發送或接收資料 */
_Nop();
_Nop();
}
然後呢,發送器件的位址,,因為可能挂接了好幾個IIC裝置,目的是找到我想與之通信的那個,
看一下發送資料,,,SCL=0;注意IIC規定隻有SCL=0;的時候SDA上的資料才允許改變.....
void SendByte(unsigned char c)
{
unsigned char BitCnt;
//SCL=0; 起始信号最後是SCL=0;是以不用寫了
for(BitCnt=0;BitCnt<8;BitCnt++) /*要傳送的資料長度為8位*/
{
if((c<<BitCnt)&0x80)SDA=1; /*判斷發送位*/
else SDA=0;
_Nop();
SCL=1; /*置時鐘線為高,通知被控器開始接收資料位*/
_Nop();
_Nop(); /*保證時鐘高電平周期大于4μs*/
_Nop();
_Nop();
_Nop();
SCL=0;
}
發送資料的時候先發的高位,,SCL為低電平的時候SDA準備資料,,,然後SCL為高電平,資料就傳過去了(就是告訴對方你接收SDA上的電平資料哈),
接着
8位資料傳完之後,主機(單片機)把SDA置為高電平,,,然後SCL置為高電平這個時候呢如果從機(PCF8591)确實接收完了8位資料,就會把SDA拉低
這就是從機的應答信号,說明資料傳輸成功,,,非應答就是不用管它
void SendByte(unsigned char c)
{
unsigned char BitCnt;
//SCL=0; 起始信号最後是SCL=0;是以不用寫了
for(BitCnt=0;BitCnt<8;BitCnt++) /*要傳送的資料長度為8位*/
{
if((c<<BitCnt)&0x80)SDA=1; /*判斷發送位*/
else SDA=0;
_Nop();
SCL=1; /*置時鐘線為高,通知被控器開始接收資料位*/
_Nop();
_Nop(); /*保證時鐘高電平周期大于4μs*/
_Nop();
_Nop();
_Nop();
SCL=0;
}
_Nop();
_Nop();
SDA=1; /*8位發送完後釋放資料線,準備接收應答位*/
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
if(SDA==1)ack=0;
else ack=1; /*判斷是否接收到應答信号*/
SCL=0;
_Nop();
_Nop();
}
接着就是發指令或者說資料了,,,,這個資料呢,就和晶片有關了,,看晶片資料才知道
源碼連結
連結:http://pan.baidu.com/s/1qX8gfA8%20密碼:8xiy
實物連結
https://item.taobao.com/item.htm?spm=686.1000925.0.0.4a155084sH7efy&id=557671967654
哎呀!為什麼部落格又不能直接複制粘貼圖檔了呢........這樣的話每寫一篇文章就會很耽誤時間了.............