Arduino 物聯網篇 | Arduno + ESP8266模組運用中移OneNet物聯網平臺實現遠程監控

一、初識OneNet

OneNET定位爲PaaS服務,即在物聯網應用和真實設備之間搭建高效、穩定、安全的應用平臺:面向設備,適配多種網絡環境和常見傳輸協議,提供各類硬件終端的快速接入方案和設備管理服務;面向應用層,提供豐富的API和數據分發能力以滿足各類行業應用系統的開發需求,使物聯網企業可以更加專注於自身應用的開發,而不用將工作重心放在設備接入層的環境搭建上,從而縮短物聯網系統的形成周期,降低企業研發、運營和運維成本。

1. 物聯網與OneNet

物聯網是把生活中各種物品通過各種元器件,傳感器等連接在一起的網絡。這個網絡具備觀察性和可控性,我們可以通過這個網絡知道其中各個物品的狀態,同時可以控制他們。

物聯網可以分爲三層,感知層、網絡層、應用層。其中感知層可以通過傳感器,執行器等元器件與環境進行互動。網絡層指起到數據存儲於管理功能的那一部分,其可以把各種數據按照特定標準進行存儲,便於應用層調取。應用層是用戶與網絡層的接口,一方面把數據以特定形式展現給用戶,一方面存儲用戶的指令,以備感知層調用。

OneNET平臺具備網絡層和應用層的功能,它可以把Arduino傳感器採集的數據存儲在自己的服務器上,同時可以在設計應用的時候調用這些數據。

OneNET平臺是中國移動開發的物聯網平臺,穩定性非常好,上手容易。
使用方法:

1)進入OneNET官網(“https://open.iot.10086.cn/”)註冊賬號

2)註冊完成後在首頁右上角點擊「開發者中心」進入產品開發界面

在這裏插入圖片描述
小提示:零基礎入,建議先別去網絡上找找亂七八糟的文章學習,直接參考官方文檔一步一步走就沒那麼多問題了。遇到實際問題後首先根據官方文檔以及網上資料去解決。官方文檔:【首頁】-【服務與支持】- 【開發文檔】- 【快速入門】

2. OneNet快速入門

2.1 創建產品

進入產品開發界面後,選擇左側菜單欄中【全部產品】-【基礎服務】。基礎服務中有:

  • NB-IoT物聯網套件
  • MQTT物聯網套件(新版)
  • 多協議接入
  • 協議適配

在這裏插入圖片描述

【多協議接入】-【HTTP】-【添加產品】

在這裏插入圖片描述
添加產品時,填寫產品相關信息
在這裏插入圖片描述

2.2 創建設備

創建產品後,點擊左上角 【OneNet】 回到首頁,單擊進入產品 【HTTP

在這裏插入圖片描述

OneNet上在項目下還有設備的概念,一個設備就相當於一塊Arduino板。單擊【設備列表】-【添加備】,在彈出的設備信息裏可以隨便填,對後面沒有影響,唯一需要注意的是數據保密性這裏,建議選擇私有,如果選擇公開,那後面你把這個項目發佈的時候別人就能看的你的數據了。填完後單擊添加即可完成創建。

在這裏插入圖片描述

選擇【設備列表】-【添加設備

在這裏插入圖片描述
添加新設備】 - 設置設備名稱、設備編號後,點擊確認即可。
在這裏插入圖片描述
2.3 新建數據流

Arduino發送給OneNET平臺的數據可能有很多種,比如溫度,溼度,光照強度等等。我們需要在OneNET上建立「變量」來存儲這些數據,在OneNET上我們把這種變量叫做“ 數據流 ”。單擊【數據流模板】-【添加數據流模板】,填入數據流名稱即可得到一個存儲數據的數據流。

在這裏插入圖片描述

2.4 創建應用

有了數據之後,我們需要把數據以一種特定形式呈現出來,比如用一個溫度計顯示實時溫度,以一個折線圖顯示一段時間的溫度趨勢。肩負展示數據功能的是“應用”,就像我們平時在手機的“應用商店”裏下載的那種應用一樣,上面會有圖標,圖片等等。我們依次單擊選擇【應用管理】-【添加應用
在這裏插入圖片描述
設置應用相關信息:名稱、logo等。
在這裏插入圖片描述

添加應用後,進入編輯應用界面如下:

在這裏插入圖片描述
設置應用界面組件以及組件的屬性:

在這裏插入圖片描述
查看應用有兩種方法:

  • 第一種單擊【應用管理】-【應用名稱】,即可在底部看到應用頁面
  • 第二種是下載OneNET的移動端(下載地址:https://open.iot.10086.cn/doc/art656.html ),通過手機或平板查看。

參考資料


二、Arduino + ESP8266 接入 OneNet

1.準備工作

1.1 硬件

  • ESP8266-01S * 1
  • Arduno Uno * 1
  • DHT11 * 1
ESP8266-01S Arduno Uno
VCC&EN 3.3V
GND GND
RX TX (D1)
TX RX(D0)
DHT11 Arduno Uno
VCC 3.3V /5V
GND GND
OUT D3

在這裏插入圖片描述

1.2 軟件

Arduino 通過 ESP8266-01S 發送數據給 OneNet 平臺的數據。ESP8266-01S 屬於 安信可公司的產品,我們需要下載官網的安信可串口調試助手 查看返回的數據。

  • 安信可串口調試助手
  • Arduino IDE

1.3 平臺

OneNet服務器裏有很多項目很多設備,具體把數據發到拿呢?
實際上OneNet平臺對於發送過來的Json數據是有一定格式要求的,其中除了要傳輸的數據,還包含了設備ID密匙,這樣就可以保證數據發送到正確的設備。

在正式開始前,我們需要記錄設備的ID和密匙。設備ID可通過單擊【設備列表】可在設備前面看到設備ID、密匙,在APIKey這一列的就是密匙。

  • 設備ID 597420765
  • 密匙 APIKey vutzlt=cl9B99vnsozQcKSRSkNs= (進入【設備列表】- 【設備詳情】路徑添加APIKey)

另外,還需要自家的WiFi賬號與密碼接入網絡。

  • WiFi名稱
  • WiFi密碼

在這裏插入圖片描述
補充:按照前文的方法對 OneNet平臺設置:創建產品(多協議接入 - edp)、創建設備、新建數據流(數據流的名稱要與程序中的一致)、添加應用(注意開關控件的屬性設置"switch:{V}")
在這裏插入圖片描述
在這裏插入圖片描述

2.程序設計

2.1 ESP8266-01S AT指令手動聯網

/***************************************************
 * 
 * 名稱:ESP8266 01S AT指令手動聯網
 * 作者:Naiva
 * 日期:2020/05/17
 * 接線:
 * Arduino nano       ESP8266 01S
 * D2(RX)   ———   TX
 * D3(TX)   ———   RX
 * VCC(3.3) ———   VCC(&EN)
 * GND      ———   GND
 * 
 ****************************************************/


#include<SoftwareSerial.h>
SoftwareSerial mySerial(2,3);//RX ,TX   wifiSerial


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  while (!Serial)
  {
    ;
    }
   Serial.println("ok");
   
   mySerial.begin(115200);
   mySerial.println("ready");
  

}

void loop() {
  // put your main code here, to run repeatedly:
  if(mySerial.available())
  Serial.write(mySerial.read());
  
  if(Serial.available())
  mySerial.write(Serial.read());

  


  

}

2.2 自動聯網及接入設備

把AT指令寫進程序讓程序自動幫我們發送,並且鏈接到雲平臺,還要再實現設備和腳本的關聯。

 while (!doCmdOk("AT+CWMODE=3", "OK"));            //工作模式
      
      
 while (!doCmdOk("AT+CWJAP=\"CMCC-R6Qs\",\"qmt2fx3q\"", "OK"));//WiFi賬號與WiFi密碼
        
while (!doCmdOk("AT+CIPSTART=\"TCP\",\"jjfaedp.hedevice.com\",876", "OK"));//鏈接到雲平臺,三個參數分別是接入協議,服務器地址和端口號
while (!doCmdOk("AT+CIPMODE=1", "OK"));           //透傳模式
while (!doCmdOk("AT+CIPSEND", ">"));              //開始發送

程序講解參考一下視頻連接,代碼見文末附錄。
https://www.bilibili.com/video/BV1CE411h7hP?p=4

3.效果展示

安信可串口調試助手
在這裏插入圖片描述
實驗過程中,一開始用的Arduino Nano 開發板,但後來可能因爲開發板質量或接線的把板子燒壞了,電腦識別不了設備,於是我換了一塊官網的Arduino Uno 正版開發板!
在這裏插入圖片描述

OneNet 移動終端「設備雲」效果界面展示,實現溫溼度顯示、開關控件控制pin13 板載 LED 的亮滅。
在這裏插入圖片描述


參考資料


附代碼

ESP8266_EDP.ino

/***********************************************************
File name:  ESP8266_EDP.ino
Description:  ESP8266 realizes remote control via ONENet
Author: Naiva
Date: 2020/05/17 
***********************************************************/

#include "edp.c"
#include "DHT11.h"


DHT11 dht11;
#define DHT11PIN 3 //pin3

#define KEY  "24eV33JUKr8dj=XGpckVygdFyJQ="    //APIkey 
#define ID   "598229651"                       //設備ID

//#define PUSH_ID "680788"
#define PUSH_ID NULL
 String comdata = "";
// 串口
#define _baudrate   115200

#define WIFI_UART   Serial
int DHT11 = 0;
 
const int stbPin = 7;  //the segment display module STB pin connected to digital pin 7
const int clkPin = 9;  //the segment display module CLK pin connected to digital pin 9
const int dioPin = 8;  //the segment display module DIO pin connected to digital pin 8

 uint8_t digits[] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f };

     
edp_pkt *pkt;
 
/*
* doCmdOk
* 發送命令至模塊,從回覆中獲取期待的關鍵字
* keyword: 所期待的關鍵字
* 成功找到關鍵字返回true,否則返回false
*/
bool doCmdOk(String data, char *keyword)
{
    bool result = false;
    
    if (data != "")   //對於tcp連接命令,直接等待第二次回覆
    {
    WIFI_UART.println(data);  //發送AT指令
    
    }
    if (data == "AT")   //檢查模塊存在
      delay(2000);
      else
      while (!WIFI_UART.available());  // 等待模塊回覆
       
      delay(200);
    if (WIFI_UART.find(keyword))   //返回值判斷
    {
    
         result = true;
    }
    else
    {
          result = false;
    }
    while (WIFI_UART.available()) WIFI_UART.read();   //清空串口接收緩存
    delay(500); //指令時間間隔
    return result;
}
 
 void sendCommand(uint8_t value) 
{ 
   digitalWrite(stbPin, LOW);                   //pin low.  To begin receiving data
   shiftOut(dioPin, clkPin, LSBFIRST, value);   //send data(value) to the segment display module
   digitalWrite(stbPin, HIGH);                  //pin high.  Stop receiving data
}  
void setup()
{
      char buf[100] = {0};
      int tmp;
       
      pinMode(13, OUTPUT);   //WIFI模塊指示燈
      pinMode(8, OUTPUT);    //用於連接EDP控制的發光二極管
       
      WIFI_UART.begin( _baudrate );
       pinMode(stbPin, OUTPUT); //initialize the stbPin as an output   數碼管
         pinMode(clkPin, OUTPUT); //initialize the clkPin as an output
         pinMode(dioPin, OUTPUT); //initialize the dioPin as an output
         sendCommand(0x8f);       //activate 
      WIFI_UART.setTimeout(3000);    //設置find超時時間
      delay(3000);
      Serial.setTimeout(100);
       
      delay(2000);
      while (!doCmdOk("AT", "OK"));
      digitalWrite(13, HIGH);   // 使Led亮
      while (!doCmdOk("AT+CWMODE=3", "OK"));            //工作模式
      
      
      while (!doCmdOk("AT+CWJAP=\"CMCC-R6Qs\",\"qmt2fx3q\"", "OK"));//WiFi賬號與WiFi密碼
        
      while (!doCmdOk("AT+CIPSTART=\"TCP\",\"jjfaedp.hedevice.com\",876", "OK"));
      while (!doCmdOk("AT+CIPMODE=1", "OK"));           //透傳模式
      while (!doCmdOk("AT+CIPSEND", ">"));              //開始發送
}
     int dht_flag = 1;
void loop()
{
    static int edp_connect = 0;
    bool trigger = false;
    edp_pkt rcv_pkt;
    unsigned char pkt_type;
    int i = 0, tmp;
    char num[10];
    
    int wd,sd;
    char wd1[20],sd1[20];
    
/* EDP 連接 */
    if (!edp_connect)
    {
        while (WIFI_UART.available()) WIFI_UART.read(); //清空串口接收緩存
        packetSend(packetConnect(ID, KEY));             //發送EPD連接包
        while (!WIFI_UART.available());                 //等待EDP連接應答
    if ((tmp = WIFI_UART.readBytes(rcv_pkt.data, sizeof(rcv_pkt.data))) > 0 )
    {
        rcvDebug(rcv_pkt.data, tmp);
     
    if (rcv_pkt.data[0] == 0x20 && rcv_pkt.data[2] == 0x00 && rcv_pkt.data[3] == 0x00)
    {
        edp_connect = 1;
    
        digitalWrite(13, LOW);   // 使Led滅
    }
    else
         ;
    }
         packetClear(&rcv_pkt);
    }
    
    
   if(dht_flag == 1)
   {
      dht_flag = 0;
      dht11.read(DHT11PIN);//溫溼度
      wd = dht11.temperature;
      sd = dht11.humidity;
      sprintf(wd1,"%d",wd); //int型轉換char型
      sprintf(sd1,"%d",sd); //int型轉換char型
      DHT11 = 0;
      delay(500);
      
      packetSend(packetDataSaveTrans(NULL, "WD", wd1)); //將新數據值上傳至數據流 WD是上傳的地方 wd1是上傳的數據
      delay(500);
      packetSend(packetDataSaveTrans(NULL, "SD", sd1)); //將新數據值上傳至數據流
      delay(500);
    }
    
  DHT11++;



 if(DHT11 > 150&&edp_connect)
 {
   
     dht11.read(DHT11PIN);
    wd = dht11.temperature;
    sd = dht11.humidity;
    sprintf(wd1,"%d",wd); //int型轉換char型
    sprintf(sd1,"%d",sd); //int型轉換char型
    DHT11 = 0;
    delay(500);
    packetSend(packetDataSaveTrans(NULL, "WD", wd1)); //將新數據值上傳至數據流
    delay(500);
    packetSend(packetDataSaveTrans(NULL, "SD", sd1)); //將新數據值上傳至數據流
    delay(500);
  }

  
while (WIFI_UART.available())
{

    readEdpPkt(&rcv_pkt);
  if (isEdpPkt(&rcv_pkt))
  {
    pkt_type = rcv_pkt.data[0];
  switch (pkt_type)
    {
      case CMDREQ:
      char edp_command[50];
      char edp_cmd_id[40];
      long id_len, cmd_len, rm_len;
      char datastr[20];
      char val[10];
      memset(edp_command, 0, sizeof(edp_command));
      memset(edp_cmd_id, 0, sizeof(edp_cmd_id));
      edpCommandReqParse(&rcv_pkt, edp_cmd_id, edp_command, &rm_len, &id_len, &cmd_len);  

//數據處理與應用中EDP命令內容對應
//本例中格式爲  datastream:[1/0] 
    sscanf(edp_command, "%[^:]:%s", datastr, val);
    
    if (atoi(val) == 1)
       // 使Led亮
       digitalWrite(13, HIGH);
       //digitalWrite(8, HIGH);
    else
       digitalWrite(13, LOW);   // 使Led滅
      //digitalWrite(8, HIGH);

if(atoi(val) > 1)
{
   sendCommand(0x40);                                       //setting the Write Data Command,using automatic address genuine.
   digitalWrite(stbPin, LOW);                               //pin low.  To begin receiving data
   shiftOut(dioPin, clkPin, LSBFIRST, 0xc0);                //Set the start address 0C0H
  if(atoi(val) >= 100 && atoi(val) <=1000)
   {   
      shiftOut(dioPin, clkPin, LSBFIRST, digits[0]);//thousand data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data  
     shiftOut(dioPin, clkPin, LSBFIRST, digits[atoi(val)/100%10]); //hundred data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data 
     shiftOut(dioPin, clkPin, LSBFIRST, digits[atoi(val)/10%10]);  //ten data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data
     shiftOut(dioPin, clkPin, LSBFIRST, digits[atoi(val)%10]);     //bit data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data 
   }
 
   else  if(atoi(val) >= 10 && atoi(val) <=100)
   {  
      shiftOut(dioPin, clkPin, LSBFIRST, digits[0]);//thousand data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data  
     shiftOut(dioPin, clkPin, LSBFIRST, digits[0]); //hundred data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data 
     shiftOut(dioPin, clkPin, LSBFIRST, digits[atoi(val)/10%10]);  //ten data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data
     shiftOut(dioPin, clkPin, LSBFIRST, digits[atoi(val)%10]);     //bit data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data 
   }
  
  else  if(atoi(val) > 0 && atoi(val) <=10)
   {
      shiftOut(dioPin, clkPin, LSBFIRST, digits[0]);//thousand data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data  
     shiftOut(dioPin, clkPin, LSBFIRST, digits[0]); //hundred data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data 
     shiftOut(dioPin, clkPin, LSBFIRST, digits[0]);  //ten data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data
     shiftOut(dioPin, clkPin, LSBFIRST, digits[atoi(val)%10]);     //bit data
     shiftOut(dioPin, clkPin, LSBFIRST, 0x00);                //filling high 8-bit data  
   }
   digitalWrite(stbPin, HIGH);
   delay(500);  
} 
                               //pin high.  Stop receiving data
packetSend(packetDataSaveTrans(NULL, datastr, val)); //將新數據值上傳至數據流
    

break;
default:
;
break;
}
}
//delay(4);
}
  if (rcv_pkt.len > 0)
  packetClear(&rcv_pkt);
  delay(150);
}
 
/*
* readEdpPkt
* 從串口緩存中讀數據到接收緩存
*/
bool readEdpPkt(edp_pkt *p)
{
    int tmp;
    if ((tmp = WIFI_UART.readBytes(p->data + p->len, sizeof(p->data))) > 0 )
    {
      rcvDebug(p->data + p->len, tmp);
      p->len += tmp;
    }
    return true;
}
 
/*
* packetSend
* 將待發數據發送至串口,並釋放到動態分配的內存
*/
void packetSend(edp_pkt* pkt)
{
  if (pkt != NULL)
  {
      WIFI_UART.write(pkt->data, pkt->len);    //串口發送
      WIFI_UART.flush();
      free(pkt);              //回收內存
  }
}
 
void rcvDebug(unsigned char *rcv, int len)
{
    int i;
}

EHT11.cpp

 /*
 File name:  EHT11.cpp
*/

#include "DHT11.h"

// Return values:
// DHTLIB_OK
// DHTLIB_ERROR_CHECKSUM
// DHTLIB_ERROR_TIMEOUT
int DHT11::read(int pin)
{
	// BUFFER TO RECEIVE
	uint8_t bits[5];
	uint8_t cnt = 7;
	uint8_t idx = 0;

	// EMPTY BUFFER
	for (int i=0; i< 5; i++) bits[i] = 0;

	// REQUEST SAMPLE
	pinMode(pin, OUTPUT);
	digitalWrite(pin, LOW);
	delay(18);
	digitalWrite(pin, HIGH);
	delayMicroseconds(40);
	pinMode(pin, INPUT);

	// ACKNOWLEDGE or TIMEOUT
	unsigned int loopCnt = 10000;
	while(digitalRead(pin) == LOW)
		if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

	loopCnt = 10000;
	while(digitalRead(pin) == HIGH)
		if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

	// READ OUTPUT - 40 BITS => 5 BYTES or TIMEOUT
	for (int i=0; i<40; i++)
	{
		loopCnt = 10000;
		while(digitalRead(pin) == LOW)
			if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

		unsigned long t = micros();

		loopCnt = 10000;
		while(digitalRead(pin) == HIGH)
			if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

		if ((micros() - t) > 40) bits[idx] |= (1 << cnt);
		if (cnt == 0)   // next byte?
		{
			cnt = 7;    // restart at MSB
			idx++;      // next byte!
		}
		else cnt--;
	}

	// WRITE TO RIGHT VARS
        // as bits[1] and bits[3] are allways zero they are omitted in formulas.
	humidity    = bits[0]; 
	temperature = bits[2]; 

	uint8_t sum = bits[0] + bits[2];  

	if (bits[4] != sum) return DHTLIB_ERROR_CHECKSUM;
	return DHTLIB_OK;
}

EHT11.h

 /*
 /*
 File name:  EHT11.h
*/
*/

#ifndef DHT11_H
#define DHT11_H

#if defined(ARDUINO) && (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

#define DHT11LIB_VERSION "V1.0"

#define DHTLIB_OK				0
#define DHTLIB_ERROR_CHECKSUM	-1
#define DHTLIB_ERROR_TIMEOUT	-2

class DHT11
{
public:
    int read(int pin);
	int humidity;
	int temperature;
};
#endif

edp.c


 /*
 File name:  edp.c
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define CONNREQ             0x10
#define CONNRESP            0x20
#define PUSHDATA            0x30
#define SAVEDATA            0x80
#define SAVEACK             0x90
#define CMDREQ              0xA0
#define CMDRESP             0xB0
#define PINGREQ             0xC0
#define PINGRESP            0xD0
#define ENCRYPTREQ          0xE0
#define ENCRYPTRESP         0xF0

#define MAX_LEN				200
#define PROTOCOL_NAME       "EDP"
#define PROTOCOL_VERSION    1

typedef unsigned char   uint8;
typedef char            int8;
typedef unsigned int    uint16;
typedef int             int16;
typedef unsigned long   uint32;
typedef long            int32;

typedef struct
{
  uint8 data[MAX_LEN];
  int16 len;
  int16 read_p;
} edp_pkt;


/*
 * packetCreate
 * 創建一個EDP包緩存空間
 */
edp_pkt *packetCreate(void)
{
  edp_pkt *p;

  if((p = (edp_pkt *)malloc(sizeof(edp_pkt))) != NULL)
    memset(p, 0, sizeof(edp_pkt));
  return p;
}

/*
 * writeRemainlen
 * 向EDP包中寫入剩餘長度字段
 * len_val: 剩餘長度的值
 */
int8 writeRemainlen(edp_pkt* pkt, int16 len_val)
{
  int8 remaining_count = 0;
  int8 tmp = 0;

  do {
    tmp = len_val % 128;
    len_val = len_val / 128;
    /* If there are more digits to encode, set the top bit of this digit */
    if (len_val > 0) {
      tmp = tmp | 0x80;
    }
    pkt->data[pkt->len++] = tmp;
    remaining_count++;
  } while (len_val > 0 && remaining_count < 5);

  return remaining_count;
}

/*
 * writeByte
 * 向EDP包中寫入一個字節
 */
int16 writeByte(edp_pkt* pkt, int8 byte)
{
  pkt->data[pkt->len++] = byte;
  return 0;
}

/*
 * writeBytes
 * 向EDP包中寫入多個字節
 */
int16 writeBytes(edp_pkt* pkt, const void* bytes, int16 count)
{
  memcpy(pkt->data + pkt->len, bytes, count);
  pkt->len += count;
  return 0;
}


/*
 * writeStr
 * 向EDP包中寫入字符串字段
 * 首先寫入兩個字節的長度,隨後緊跟字符串內容
 */
int16 writeStr(edp_pkt* pkt, const int8* str)
{
  short len = strlen(str);

  writeByte(pkt, len >> 8);
  writeByte(pkt, len & 0x00ff);

  memcpy(pkt->data + pkt->len, str, len);
  pkt->len += len;
  return 0;
}


/*---------------------------------------------------------------------------*/
/*
 * readUint8
 * 從EDP包中讀出一個字節
 */
uint8 readUint8(edp_pkt* pkt)
{
  return pkt->data[pkt->read_p++];
}

/*
 * readUint16
 * 從EDP包中讀出16bit的字段
 */
uint16 readUint16(edp_pkt* pkt)
{
  uint16 tmp;
  uint8 msb, lsb;
  
  msb = readUint8(pkt);
  lsb = readUint8(pkt);

  tmp = (msb<<8) | lsb;
  return tmp;
}

/*
 * readUint32
 * 從EDP包中讀出4個字節的字段
 */
uint32 readUint32(edp_pkt* pkt)
{
  uint32 tmp = 0;
  int i = 4;

  while (--i >= 0) 
  {
    tmp <<= 8;
    tmp |= readUint8(pkt);
  }
  return tmp;
}

/*
 * readStr
 * 根據長度,從EDP包中讀出字符串數據
 * len : 字符串的長度
 */
void readStr(edp_pkt* pkt, char* str, uint16 len)
{
  memcpy(str, pkt->data + pkt->read_p, len);
  pkt->read_p += len;
}

/*
 * readRemainlen
 * 從EDP包中讀出剩餘長度
 */
int32 readRemainlen(edp_pkt* pkt)
{
  uint32 multiplier = 1;
  uint32 len_len = 0;
  uint8 onebyte = 0;
  int32 len_val = 0;
  do 
  {
    onebyte = readUint8(pkt);

    len_val += (onebyte & 0x7f) * multiplier;
    multiplier *= 0x80;

    len_len++;
    if (len_len > 4) 
    {
      return -1; /*len of len more than 4;*/
    }
  } while((onebyte & 0x80) != 0);
  return len_val;
}

/*
 * packetConnect:組EDP連接包
 * 首先創建EDP緩存空間,按照EDP協議組EDP連接包
 * 分配的內存需要在發送之後free掉
 * devid: 設備id
 * key:APIKey
 */
edp_pkt *packetConnect(const int8* devid, const int8* key)
{
  int32 remainlen;
  edp_pkt* pkt;
  
  if((pkt = packetCreate()) == NULL)
    return NULL;
  
  /* msg type */
  writeByte(pkt, CONNREQ);
  /* remain len */
  remainlen = (2 + 3) + 1 + 1 + 2 + (2 + strlen(devid)) + (2 + strlen(key));
  writeRemainlen(pkt, remainlen);
  /* protocol desc */
  writeStr(pkt, PROTOCOL_NAME);
  /* protocol version */
  writeByte(pkt, PROTOCOL_VERSION);
  /* connect flag */
  writeByte(pkt, 0x40);
  /* keep time */
  writeByte(pkt, 0);
  writeByte(pkt, 0x80);

  /* DEVID */
  writeStr(pkt, devid);
  /* auth key */
  writeStr(pkt, key);
  
  return pkt;
}


/*
 * packetDataSaveTrans:組EDP數據存儲轉發包
 * 首先創建EDP緩存空間,按照EDP協議組EDP數據存儲轉發包
 * 分配的內存需要在發送之後free掉
 * devid: 設備id
 * streamId:數據流ID,即數據流名
 * val: 字符串形式的數據值
 */
edp_pkt *packetDataSaveTrans(const int8* destId, const int8* streamId, const int8 *val)
{
  int32 remainlen;
  int8 tmp[200];
  int16 str_len;
  edp_pkt *pkt;
  
  if((pkt = packetCreate()) == NULL)
    return pkt;

  /* 生成數據類型格式5的數據類型 */
  sprintf(tmp, ",;%s,%s", streamId, val);
  str_len = strlen(tmp);

  /* msg type */
  writeByte(pkt, SAVEDATA);

  if (destId != NULL)
  {
    /* remain len */
    remainlen = 1 + (2 + strlen(destId)) + 1 + (2 + str_len);
    writeRemainlen(pkt, remainlen);
    /* translate address flag */
    writeByte(pkt, 0x80);
    /* dst devid */
    writeStr(pkt, destId);
  }
  else
  {
    /* remain len */
    remainlen = 1 + 1 + (2 + str_len);
    writeRemainlen(pkt, remainlen);
    /* translate address flag */
    writeByte(pkt, 0x00);
  }

  /* json flag */
  writeByte(pkt, 5);
  /* json */
  writeStr(pkt, tmp);
  
  return pkt;
}


void packetClear(edp_pkt* pkt)
{
  memset(pkt, 0, sizeof(edp_pkt));
}


/*
 * isEdpPkt
 * 按照EDP數據格式,判斷是否是完整數據包
 */
int16 isEdpPkt(edp_pkt* pkt)
{
  uint32 data_len = 0;
  uint32 multiplier = 1;
  uint32 len_val = 0;
  uint32 len_len = 1;
  uint32 pkt_total_len = 0;
  uint8* pdigit;

  pdigit = pkt->data;
  data_len = pkt->len;

  if (data_len <= 1)
  {
    return 0;   /* continue receive */
  }

  do {
    if (len_len > 4)
    {
      return -1;  /* protocol error; */
    }
    if (len_len > data_len - 1)
    {
      return 0;   /* continue receive */
    }
    len_len++;
    pdigit++;
    len_val += ((*pdigit) & 0x7f) * multiplier;
    multiplier *= 0x80;
  } while (((*pdigit) & 0x80) != 0);

  pkt_total_len = len_len + len_val;

  /* receive payload */
  if (pkt_total_len == data_len)
  {
    return 1;   /* all data for this pkt is read */
  }
  else
  {
    return 0;   /* continue receive */
  }
}


/*
 * edpCommandReqParse
 * 按照EDP命令請求協議,解析數據
 */
int edpCommandReqParse(edp_pkt* pkt, char *id, char *cmd, int32 *rmlen, int32 *id_len, int32 *cmd_len)
{
  readUint8(pkt);     /* 包類型 */
  *rmlen = readRemainlen(pkt);    /* 剩餘長度 */
  *id_len = readUint16(pkt);      /* ID長度 */
  readStr(pkt, id, *id_len);      /* 命令ID */
  *cmd_len = readUint32(pkt);     /* 命令長度 */
  readStr(pkt, cmd, *cmd_len);    /* 命令內容 */
}


/*
 * edpPushDataParse
 * 按照EDP透傳數據格式,解析數據
 */
int edpPushDataParse(edp_pkt* pkt, char *srcId, char *data)
{
  uint32 remain_len;
  uint16 id_len;
  
  readUint8(pkt);     /* 包類型 */
  remain_len = readRemainlen(pkt);    /* 剩餘長度 */
  id_len = readUint16(pkt);           /* 源ID長度 */
  readStr(pkt, srcId, id_len);    /* 源ID */
  readStr(pkt, data, remain_len - 2 - id_len);    /* 數據內容 */
}

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