STM32开发GPRS传输的GPS定位器 C#编写服务器转发程序,客户端显示轨迹_c# gps系统

41 阅读4分钟

单片机解析GPS模块的位置信息,一般解析GPRMC这一条就可以,解析的方法很多,可以用找字头,数逗号的方式例如下面的信息。

GPRMC,092927.000,A,2235.9058,N,11400.0518,E,0.000,74.11,151216,,D\*49 GPRMC,092927.000,A,2235.9058,N,11400.0518,E,0.000,74.11,151216,,D\*49  GPVTG,74.11,T,,M,0.000,N,0.000,K,D*0B 
GPGGA,092927.000,2235.9058,N,11400.0518,E,2,9,1.03,53.1,M,2.4,M,0.0,0\*6B GPGGA,092927.000,2235.9058,N,11400.0518,E,2,9,1.03,53.1,M,-2.4,M,0.0,0\*6B  GPGSA,A,3,29,18,12,25,10,193,32,14,31,,,,1.34,1.03,0.85*31 
GPGSV,3,1,12,10,77,192,17,25,59,077,42,32,51,359,39,193,49,157,36\*48 GPGSV,3,1,12,10,77,192,17,25,59,077,42,32,51,359,39,193,49,157,36\*48  GPGSV,3,2,12,31,47,274,25,50,46,122,37,18,45,158,37,14,36,326,18*70 
$GPGSV,3,3,12,12,24,045,45,26,17,200,18,29,07,128,38,21,02,174,*79

char *gpsdata;
	int i,count;
	if(USART_GetITStatus(USART2,USART_IT_IDLE) == SET)     
	{                                                 
		USART2->SR;        
		USART2->DR;
		USART_ClearITPendingBit(USART2,USART_IT_IDLE); 
		DMA_Cmd(DMA1_Channel6,DISABLE);                      //??DMA        
		U2_Rx_Counter = 1024 - DMA_GetCurrDataCounter(DMA1_Channel6);  //??????????          		
		gpsdata = strstr(U2_RX_data,"$GNRMC");
		if(gpsdata)	
		{ 
			for(i=0;i<strlen(gpsdata);i++)
			{
				if(gpsdata[i]==',')
				{
					count++;
					if(count==2)
					{
							
							if(gpsdata[i+1]=='A')
							{
								gps_flag=1;
								
							}
							else
							{
								gps_n=0;
								gps_e=0;
								break;
							} 
					}	
					if(gps_flag==1)
					{
						if((count==3)&&(gpsdata[i+1]!=','))
						{
							gps_n=((gpsdata[i+1]-0x30)*100000+(gpsdata[i+2]-0x30)*10000+((gpsdata[i+3]-0x30)*100000+(gpsdata[i+4]-0x30)*10000+(gpsdata[i+6]-0x30)*1000+(gpsdata[i+7]-0x30)*100+(gpsdata[i+8]-0x30)*10+(gpsdata[i+9]-0x30))/60);
						}
						if((count==5)&&(gpsdata[i+1]!=','))
						{
							gps_e=((gpsdata[i+1]-0x30)*1000000+(gpsdata[i+2]-0x30)*100000+(gpsdata[i+3]-0x30)*10000+((gpsdata[i+4]-0x30)*100000+(gpsdata[i+5]-0x30)*10000+(gpsdata[i+7]-0x30)*1000+(gpsdata[i+8]-0x30)*100+(gpsdata[i+9]-0x30)*10+(gpsdata[i+10]-0x30))/60);			
						}		
						if(count>=13)
						{
							count=0;
							gps_flag=0;
							break;
						}
					}	
				}	
			}
		}
		memset(U2_RX_data,0, 1024);		
		DMA1_Channel6->CNDTR = 1024;                            //??????????           
		DMA_Cmd(DMA1_Channel6,ENABLE);                       //??DMA             
	}

单片机与服务器通过AT指令控制GPRS模块与服务器通信,AT指令是比较难的,开发过的会有感受,AT指令返回的状态比较多,并不是返回一种或两种结果。不但要按照正常流程一步一步发送AT指令,还要有错误返回处理。AT指令挺复杂的,特别适合用状态机处理这些流程。

switch (M26_info.state)             
	{
		case GPRS_state_Poweroff :
				if(AT_Delay_Timer>5)
				{
					GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_SET);
					GPIO_SetBits(GPIOA,GPIO_Pin_1);
					GPIO_ResetBits(GPIOA, GPIO_Pin_7);
					GPIO_ResetBits(GPIOA, GPIO_Pin_4);             // Sleep
				}
				if(AT_Delay_Timer>25)
				{
					GPIO_ResetBits(GPIOA,GPIO_Pin_1);
					GPIO_SetBits(GPIOA, GPIO_Pin_7);
				}				
				if(AT_Delay_Timer>45)
				{
					GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_RESET);
					M26_info.state=GPRS_state_AT;
					AT_Timeslimite=0;
					AT_Delay_Timer=0;
					AT_overtime=0;
					ZC_DATA_flog=1;
				}		
			break; 
		case GPRS_state_AT :
				M26_ATTR(5,30,50,"AT+IPR=115200&W\r\n","OK","","","","",GPRS_state_CREG,GPRS_state_AT);
			break; 
		case GPRS_state_CREG :
				M26_ATTR(5,30,100,"AT+CREG?\r\n","+CREG: 0,1","+CREG: 0,5","+CREG: 0,2","+CREG: 0,3","+CREG: 0,4",GPRS_state_CSQ,GPRS_state_CREG);
			break;
		case GPRS_state_CSQ :
				M26_ATTR(2,30,20,"AT+CSQ\r\n","+CSQ:","","","","",GPRS_state_QIDNSIP,GPRS_state_CSQ);
		case GPRS_state_QIDNSIP:
				M26_ATTR(2,30,5,"AT+QIFGCNT=0\r\n","OK","","","","",GPRS_state_QIDEACT,GPRS_state_Poweroff);
			break;
		case GPRS_state_QIDEACT:
				M26_ATTR(2,2,50,"AT+QICSGP=1,\"CMMTM\"\r\n","OK","","ERROR","","",GPRS_state_QIREGAPP,GPRS_state_Poweroff);
				break;
		case GPRS_state_QIREGAPP:
				M26_ATTR(2,2,50,"AT+QIREGAPP\r\n","OK","","ERROR","","",GPRS_state_QIACT,GPRS_state_Poweroff);
			break;
		case GPRS_state_QIACT:
				M26_ATTR(10,2,300,"AT+QIACT\r\n","OK","","ERROR","","",GPRS_state_QIOPEN,GPRS_state_Poweroff);
			break;
		case GPRS_state_QIOPEN:
				M26_ATTR(20,10,50,"AT+QIOPEN=\"TCP\",\"122.51.33.246\",\"8888\"\r\n","CONNECT OK","ALREADY CONNECT","ERROR","CONNECT FAIL","",GPRS_state_QISEND,GPRS_state_QIACT);
			break;
		case GPRS_state_QISTAT:
				M26_ATTR(10,10,500,"AT+QISTAT\r\n","STATE: CONNECT OK","","STATE: IP INITIAL","","",GPRS_state_QISEND,GPRS_state_QIOPEN);
			break;
		case GPRS_state_QISEND:
				GPRS_DATA_flog=1;
				M26_ATTR(50,20,100,"AT+QISEND=27\r\n",">",">","ERROR","ERROR","ERROR",GPRS_state_QISENDDATA,GPRS_state_QISENDDATA);
			break;
		case GPRS_state_QISENDDATA:
				M26_senddata(10,10,100,"12345678","SEND OK","","ERROR","ERROR","ERROR",GPRS_state_QISEND,GPRS_state_Poweroff);
			break;
		
		default:  ; 
	}

服务器后台

用C#编写服务器后台程序,他负责接收所有设备发来数据,把与客户端有关的设备数据转发给客户端。这样局域网里的客户端可以通过网关获取服务器的数据。服务器程序用到多线程技术,可以同时处理多个设备发来的数据。用到了Dictionary数据类型,实现设备ID与socket对应,关键代码如下

private void ReceiveClient(object obj)
        {
            Socket _ClientSocket = (Socket)obj;
            while (true)
            {
                try
                {
                    byte[] result = new byte[1024];
                    int receiveLength = _ClientSocket.Receive(result);             
                    string clientMessage = Encoding.UTF8.GetString(result, 0, receiveLength);
                    string Destination_Address;
                    string Data_Rcve;
                    if (receiveLength == 0)
                    {
                        ClientSocketDictionary.Remove(_ClientSocket);
                        _ClientSocket.Shutdown(SocketShutdown.Both);
                        _ClientSocket.Close();
                        SetText2box();
                        break;
                    }
                    else
                    {
                        if ((result[0] == '$') && (result[1] == '2'))//发送数据 目的地址
                        {
                            Destination_Address = clientMessage.Substring(2, 10);
                            Data_Rcve = clientMessage.Substring(12, clientMessage.Length-12);
                            temp = DateTime.Now.ToString()+ " Dest is : " + Destination_Address + "  Data is : " + Data_Rcve;
                            SetTextbox();
                            SendMessage(clientMessage);
                        }
                        if ((result[0] == '$') && (result[1] == '1'))//注册ID  源地址
                        {
                            if (!ClientSocketDictionary.ContainsKey(_ClientSocket))
                            {
                                ClientSocketDictionary.Add(_ClientSocket, clientMessage.Substring(2, 10));
                                SetText2box();
                            }
                            else
                            {
                                ClientSocketDictionary.Remove(_ClientSocket);
                                ClientSocketDictionary.Add(_ClientSocket, clientMessage.Substring(2, 10));
                                SetText2box();
                            }                                        
                        }
                    }
                                                       
                }
                catch (Exception e)
                {
                    ClientSocketDictionary.Remove(_ClientSocket);
                    _ClientSocket.Shutdown(SocketShutdown.Both);
                    _ClientSocket.Close();
                    SetText2box();
                    SetText3box(e);

                    break;
                }
            }
        }
public void SendMessage(string msg)
        {
            if (msg == string.Empty || this.ClientSocketDictionary.Count <= 0) return;
            string Destination_Address = msg.Substring(2, 10);
            string Data_Rcve = msg.Substring(12, msg.Length-12);
            msg = "$3" + Destination_Address + Data_Rcve;

            
            try
            {
                foreach (KeyValuePair<Socket, string> kvp in ClientSocketDictionary)
                {
                   if(kvp.Value.Substring(0,10)== Destination_Address)
                   {
                       kvp.Key.Send(Encoding.UTF8.GetBytes(msg));
                   }              
                }
            }
            catch (Exception e)
            {
                SetText3box(e);
            }
        }      

客户端

C#编写客户端程序,调用百度地图API,实现地图打标,绘制轨迹。客户端工作流程是这样的,首先向服务器发送注册自身ID,然后向服务器获取相关设备ID的数据,解析数据,调用百度地图API实现打标定位等。关键代码如下

 private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                clientScoket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                clientScoket.Connect(new IPEndPoint(IPAddress.Parse(textBox1.Text.ToString()), 8888));
                t = new Thread(ReceiveMessage);//开启线程执行循环接收消息
                t.IsBackground=true;


![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/fdfd05d2579645c7884ce2577fe989d3~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771256688&x-signature=owoBqCyTn3m6XVIYmFaPYmmbIKk%3D)
![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/e910580daae7424b841a9e7bf0bb165a~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771256688&x-signature=QXVpxJgyRFGfJu7TRmcdvcOkXsw%3D)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://gitee.com/vip204888)**