单片机使用HTTP POST方式发送数据

HTTP请求是依赖TCP的,也就是在单片机上面,我们一样可以使用,无非就是先发送一个http请求头,再发送正文,比如我最近就使用了http post方式发送数据到服务器,服务器端使用的是WEB API,单片机使用的STM32 与SIM800C,SIM800C使用GPRS连接服务器,采用透传方式,TCP连接,在这里就不讲述TCP连接方式了,假设你已经会使用TCP方式连接服务器,并发送数了。

以下代码只会教会你怎么发起http请求,具体的发送接口底层这里不做说明,以及json打包等操作也不做说明,json打包就是不停的使用sprintf即可。

  1. #define POST_VER "1.00" //此处必须是浮点或整数
  2. #define POST_REAL_DATA_STRING "RealData" //实时数据上报定义
  3. #define POST_API_URL "/api/RTU_DataInterface" //接口地址
  4. //POST请求的http头信息,参数1:API地址,参数2:服务器IP,参数3:后续数据长度
  5. static const char *const HTTP_POST_HEAD =
  6. {
  7. "POST %s HTTP/1.1\r\n\
  8. Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n\
  9. X-Requested-With: XMLHttpRequest\r\n\
  10. Host: %s\r\n\
  11. Content-Length: %d\r\n\
  12. Expect: 100-continue\r\n\
  13. Connection: Keep-Alive\r\n" //最后面必须空一行
  14. }

//发送http post请求

  1. /*************************************************************************************************************************
  2. * 函数 : bool POST_SendHttpHead(POST_HANDLE *pHandle, u16 DataLen)
  3. * 功能 : 发送http POST请求头
  4. * 参数 : pHandle:句柄;AcqTime:观测时间;pData:实时数据指针;SerialNumber:流水号;AlarmStatus:报警定义
  5. * 返回 : 长度
  6. * 依赖 : 无
  7. * 作者 : cp1300@139.com
  8. * 时间 : 2018-02-04
  9. * 最后修改时间 : 2018-02-04
  10. * 说明 : 将报文打包写入到了pPackDataBuff中
  11. *************************************************************************************************************************/
  12. bool POST_SendHttpHead(POST_HANDLE *pHandle, u16 DataLen)
  13. {
  14. int len;
  15. char IpBuff[16];
  16. if(pHandle->pHttpHeadDataBuff == NULL) DEBUG("pHttpHeadDataBuff未初始化\r\n");
  17. HTTP_POST_GetHost(IpBuff, pHandle->SendDataTelAttr.CentralIndex); //获取服务器ip
  18. len = sprintf((char *)pHandle->pHttpHeadDataBuff, HTTP_POST_HEAD, POST_API_URL, IpBuff, DataLen); //http请求头打包
  19. uart_printf("http头(%dB):%s\r\n",len, (char*)pHandle->pHttpHeadDataBuff);
  20. return pHandle->pSendData(pHandle->pHttpHeadDataBuff, len);
  21. }
  1. //获取指定服务器的IP地址
  2. u8 HTTP_POST_GetHost(char *pIpBuff, u8 ServerIndex)
  3. {
  4. u16 ip[4];
  5. if(ServerIndex > 3)
  6. {
  7. return sprintf(pIpBuff, "localhost");
  8. }
  9. ip[0]=atoi(&g_SYS_Config.ServerIP[ServerIndex][0]);
  10. ip[1]=atoi(&g_SYS_Config.ServerIP[ServerIndex][4]);
  11. ip[2]=atoi(&g_SYS_Config.ServerIP[ServerIndex][8]);
  12. ip[3]=atoi(&g_SYS_Config.ServerIP[ServerIndex][12]);
  13. return sprintf(pIpBuff,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
  14. }


//这里面句柄什么的,发送接口什么的忽略掉,你只要知道打包方式,这个报文长度什么的非常重要。

  1. /*************************************************************************************************************************
  2. * 函数 : u16 POST_RealDataPackage(POST_HANDLE *pHandle, tm *pAcqTime,REAL_DATA *pData, u16 SerialNumber)
  3. * 功能 : 实时数据打包(正文打包)
  4. * 参数 : pHandle:句柄;AcqTime:观测时间;pData:实时数据指针;SerialNumber:流水号;AlarmStatus:报警定义
  5. * 返回 : 长度
  6. * 依赖 : 无
  7. * 作者 : cp1300@139.com
  8. * 时间 : 2018-02-04
  9. * 最后修改时间 : 2018-02-04
  10. * 说明 : 将报文打包写入到了pPackDataBuff中,注意:一定要在最后面增加一个换行符,这样每次发送数据时服务器才不会断开连接
  11. *************************************************************************************************************************/
  12. u16 POST_RealDataPackage(POST_HANDLE *pHandle,tm *pAcqTime,REAL_DATA *pData, u16 SerialNumber)
  13. {
  14. u16 len = 0;
  15. char *pTextBuff = (char *) pHandle->pPackDataBuff;
  16. //报文格式 flag=1&ver=1.00&type=RealData&SERIAL=1&ST=1510260128&UT=2018-02-04 15:24:32&data={"ST":"1510260128","TT":"2018-02-04 18:00:00","Z":1.234,"VT":12.56,"ZT":128}
  17. //http头与后面数据分开发送的时候,要先发送一个换行,但是这个换行不算总长度
  18. len += sprintf(&pTextBuff[len],"\r\n"); //前面增加一个换行-不要弄掉了,掉了会出现各种问题
  19. len += sprintf(&pTextBuff[len],"flag=%d&",1); //数据上传标识符1
  20. len += sprintf(&pTextBuff[len],"ver=%s&",POST_VER); //通信协议版本,用于后续升级的兼容
  21. len += sprintf(&pTextBuff[len],"type=%s&",POST_REAL_DATA_STRING); //报文类型:实时数据
  22. len += sprintf(&pTextBuff[len],"SERIAL=%d&",SerialNumber); //流水号
  23. len += sprintf(&pTextBuff[len],"ST=%02X%02X%02X%02X%02X&",pHandle->SendDataTelAttr.TelNumber[0],pHandle->SendDataTelAttr.TelNumber[1],
  24. pHandle->SendDataTelAttr.TelNumber[2],pHandle->SendDataTelAttr.TelNumber[3],pHandle->SendDataTelAttr.TelNumber[4]); //站点编号
  25. len += sprintf(&pTextBuff[len],"UT=%04d-%02d-%02d %02d:%02d:%02d&",timer.w_year, timer.w_month, timer.w_date, timer.hour, timer.min, timer.sec);//发报时间
  26. len += sprintf(&pTextBuff[len],"data={"); //报文正文
  27. //前面固定为ST 与 TT
  28. len += sprintf(&pTextBuff[len],"\"ST\":\"%02X%02X%02X%02X%02X\",",pHandle->SendDataTelAttr.TelNumber[0],pHandle->SendDataTelAttr.TelNumber[1],
  29. pHandle->SendDataTelAttr.TelNumber[2],pHandle->SendDataTelAttr.TelNumber[3],pHandle->SendDataTelAttr.TelNumber[4]); //站点编号
  30. len += sprintf(&pTextBuff[len],"\"TT\":\"%04d-%02d-%02d %02d:%02d:%02d\",",pAcqTime->w_year, pAcqTime->w_month, pAcqTime->w_date, pAcqTime->hour, pAcqTime->min, 0); //采集时间
  31. //打包所有要素数据
  32. len += JSON_RealDataPackage(&pTextBuff[len], pData, pAcqTime); //实时数据打包为JSON(正文打包)
  33. len += sprintf(&pTextBuff[len],"}");
  34. return len;
  35. }

//上面是正文数据打包,自己根据需要实现json就行。


//下面就是发送了,先发送请求头,里面包含了报文正文的长度,请求头与报文正文之间有个换行,但是不算总长度,这个非常重要,没有控制好就会导致发送完成后服务器断开了,或者是不能收到服务器响应等各种情况

  1. /*************************************************************************************************************************
  2. * 函数 : POST_COMM_ERROR POST_SendRealDataPackge(POST_HANDLE *pHandle, tm *pAcqTime,REAL_DATA *pData, u16 SerialNumber)
  3. * 功能 : 发送实时数据
  4. * 参数 : pHandle:协议栈句柄;pConnectData:POST连接结构指针;pAcqTime:发报时间;pData:实时数据指针;SerialNumber:流水号;
  5. * 返回 : RTU_ERROR
  6. * 依赖 : 无
  7. * 作者 : cp1300@139.com
  8. * 时间 : 2018-02-01
  9. * 最后修改时间 : 2018-02-01
  10. * 说明 :
  11. *************************************************************************************************************************/
  12. POST_COMM_ERROR POST_SendRealDataPackge(POST_HANDLE *pHandle, tm *pAcqTime,REAL_DATA *pData, u16 SerialNumber)
  13. {
  14. u16 ByteNum = 0;
  15. u8 retry ;
  16. int len;
  17. u16 ReceiveDelay;
  18. u8 *pRxData; //注意:接收数据的缓冲区与发送的一般是共用的,为了节省内存
  19. if(SerialNumber == 0) //需要自动流水号
  20. {
  21. SerialNumber = pHandle->SerialNumberAdd(pHandle->SendDataTelAttr.CentralIndex); //流水号增加
  22. }
  23. for(retry = 0;retry < pHandle->SendRetry; retry ++) //失败重试
  24. {
  25. ByteNum = POST_RealDataPackage(pHandle, pAcqTime, pData, SerialNumber); //数据正文打包
  26. if(ByteNum < 2) return POST_COMM_DATA_ERROR;
  27. POST_SendHttpHead(pHandle, ByteNum-2); //发送http POST请求头-数据长度-2,去掉前面的2个换行符
  28. uart_printf("正文(%dB):%s\r\n",ByteNum, (char*)pHandle->pPackDataBuff);
  29. OSTimeDlyHMSM(0,0,0,500); //发送头部信息后延时500毫秒
  30. pHandle->pSendData(pHandle->pPackDataBuff, ByteNum); //发送后续正文数据
  31. //等待接收数据
  32. len = pHandle->pReadData(&pRxData, 20, pHandle->RxTimeOutMs, &ReceiveDelay); //接收数据
  33. if(len < 0)
  34. {
  35. uart_printf("[POST ERROR]:服务器异常的断开了连接\r\n");
  36. return POST_COMM_LINGK_ERROR;
  37. }
  38. else if(len <= 3)
  39. {
  40. uart_printf("[POST ERROR]:等待服务器返回数据超时\r\n");
  41. continue;
  42. }
  43. else
  44. {
  45. pRxData[len] = 0; //增加结束符
  46. uart_printf("[POST]:服务器返回数据(%dms):\r\n%s\r\n", ReceiveDelay, pRxData);
  47. return POST_COMM_OK;
  48. }
  49. }
  50. uart_printf("[POST ERROR]:发送超时\r\n");
  51. return POST_COMM_TIME_OUT;
  52. }

//东西有点乱,只着重看请求头打包以及请求头发送与正文发送就行。

  1. 服务器上webapi核心,接收数据后进行判断,存储,响应操作
  2. // POST api/rtu_datainterface
  3. public HttpResponseMessage Post([FromBody]UploadDataStruct data)
  4. {
  5. //数据解析
  6. try
  7. {
  8. if (data.flag != 1 || data.type == null || data.SERIAL == 0 || data.ST == null || data.ST.Length != 10 || data.UT == null)
  9. {
  10. return null; //数据无效
  11. }
  12. if (data.type == "RealData") //上传实时数据
  13. {
  14. if (data.data != null)
  15. SystemLog.Write(data.ST + " \t" + data.data);
  16. //响应数据
  17. return new HttpResponseMessage
  18. {
  19. Content = new StringContent(
  20. StaticJson.DefaultResponseJsonStructPack(1, "1510260128", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")),
  21. System.Text.Encoding.UTF8, "application/json")
  22. };
  23. }
  24. else return null;
  25. }
  26. catch (Exception e)
  27. {
  28. return null;
  29. }
  30. }

//最后收到的数据

2018-03-18 07:57:34 	2018030301 	{"ST":"2018030301","TT":"2018-03-18 08:00:00","Z":3.098,"ZB":3.098,"SBL1":0.000,"SBL2":0.000,"VA":0.000,"VJ":0.000,"PD":0.0,"PJ":0.0,"PT":8.0,"PN01":0.0,"PN05":0.0,"PN10":0.0,"PN30":0.0,"P1":0.0,"DTEMP":15.4,"SIGNAL":40.0,"VT":12.19}
2018-03-18 08:07:37 	2018030301 	{"ST":"2018030301","TT":"2018-03-18 08:05:00","Z":3.098,"ZB":3.098,"SBL1":0.000,"SBL2":0.000,"VA":0.000,"VJ":0.000,"PJ":0.0,"PT":8.0,"PN01":0.0,"PN05":0.0,"DTEMP":15.5,"SIGNAL":62.0,"VT":12.21}
2018-03-18 08:07:39 	2018030301 	{"ST":"2018030301","TT":"2018-03-18 08:10:00","Z":3.099,"ZB":3.099,"SBL1":0.000,"SBL2":0.000,"VA":0.000,"VJ":0.000,"PJ":0.0,"PT":8.0,"PN01":0.0,"PN05":0.0,"PN10":0.0,"DTEMP":15.4,"SIGNAL":62.0,"VT":12.22}
2018-03-18 08:17:34 	2018030301 	{"ST":"2018030301","TT":"2018-03-18 08:15:00","Z":3.098,"ZB":3.098,"SBL1":0.000,"SBL2":0.000,"VA":0.000,"VJ":0.000,"PJ":0.0,"PT":8.0,"PN01":0.0,"PN05":0.0,"DTEMP":15.5,"SIGNAL":43.0,"VT":12.24}

posted @ 2023-07-21 08:45  SymPny  阅读(933)  评论(0编辑  收藏  举报