
自行车定位器
简介
使用STM32G030F6P6单片机和EC800MCNGB联网+定位模组制作的定位器,可以用于自行车的定位,定位间隔可以设置,定位信息可以同步到手机客户端查看。使用锂电池供电,可长时间待机。
简介:使用STM32G030F6P6单片机和EC800MCNGB联网+定位模组制作的定位器,可以用于自行车的定位,定位间隔可以设置,定位信息可以同步到手机客户端查看。使用锂电池供电,可长时间待机。开源协议
:GPL 3.0
(未经作者授权,禁止转载)描述
一、项目介绍
-
自行车定位器项目,主控使用STM32G030单片机,M0+内核,有小体积、较为适中的内存大小、低功耗等优点;联网模组的是移远的EC800MCNGB模组,LCC封装,ASR1606L平台,支持MQTT协议,并且带有GPS定位功能。因此针对自行车定位,使用这种搭配是完全可以的。
-
项目分设备终端和手机客户端,中间借助MQTT服务器时间数据的转发。设备端根据设定的间隔时间,进行到时唤醒,然后执行联网、定位、上传数据等操作。数据到服务器以后再进行转发,到手机客户端,届时就可以在手就上查看设备的定位信息。定位功能当单片机唤醒后自动定时定位,也可以使用磁铁靠近霍尔芯片立即定位,还有可以通过串口1指令强制设备进入低功耗模式(类似关机)。
-
起初想着是直接使用OPENCPU的开发方式,但是鉴于这种开发方式需要再去学习官方平台指定的开发工具,需要有一定的学习成本,因此还是采用这种最常见的主控使用串口发送AT指令操作通讯模组的开发方式。并且,鉴于项目做完后依旧可以当做一个很好的学习平台,最终选择了这种组合进行开发,更加灵活。
二、程序流程和程序设计
A.程序流程
定位器端
安卓客户端
B. 程序设计
定位设备是处于不间断电池供电状态,依靠外部低速时钟晶振和单片机的休眠模式配合,进行低功耗待机和定时唤醒等操作。以下是部分的程序代码:
1、模组串口AT收发和回复数据处理函数
由于AT指令的回收字符是非定长的,但是有一定的规律,在回复的字符串种会带有某个特定的字符串,比方说【OK】、【+CSQ】等,因此根据这些特性,使用了DMA+串口的空闲中断对接收字符串进行处理模式。
单片机和模组通信的串口为串口2,由于其中包含所有的接收字符串处理,因此函数体比较长,并且带有接收检测和重发机制。由于再开发过程中发现单片机的内存超了,因此有些扩展功能就没有实现。
uint8_t EC800MCN_Send_Cmd(char * cmd,char *replay,uint16_t waitime)
{
uint8_t times = 0;//定义重复次数
Usart2type.UsartRecFlag = 0;
char *index;
char str[10];
uint8_t rx_buf[500];
memset(rx_buf, 0, sizeof(rx_buf));
memset(str, 0, sizeof(str));
memset(Usart2type.Usart2RecBuffer,0x00,sizeof Usart2type.Usart2RecBuffer);
times = 3;
do
{
printf("CAT1>>%s\r\n",cmd);
HAL_UART_Transmit(&huart2, (uint8_t *)cmd, strlen(cmd), HAL_MAX_DELAY);
HAL_Delay(waitime);
if(Usart2type.UsartRecFlag == 1)
{
Usart2type.UsartRecFlag = 0;
if(strstr((char *)Usart2type.Usart2RecBuffer,"+CCLK:"))
{
sscanf((char *)Usart2type.Usart2RecBuffer, "%[^: \"]: \"%[^/]/%[^/]/%[^,],%[^:]:%[^:]:%[^+]+%[^\"]\"", buf1, buf2, buf3, buf4, buf5, buf6, buf7, buf8);
a = atoi(buf2);
b = atoi(buf3);
c = atoi(buf4);
d = atoi(buf5);
e = atoi(buf6);
f = atoi(buf7);
if( (a < 20) || (b > 12) || (c > 32) || (d > 24) || (e > 60) || (f > 60))
{
printf("CAT1:Timing error!\r\n");
return FALSE;
}
day_time[0] = (uint8_t)(a); //年
day_time[1] = (uint8_t)(b); //月
day_time[2] = (uint8_t)(c); //日
day_time[3] = (uint8_t)(d); //时
day_time[4] = (uint8_t)(e); //分
day_time[5] = (uint8_t)(f); //秒
Gen_Utc2Beijing(day_time); //将UTC时间转换为北京时间
printf("CAT1:校时成功 当前时间:");
Gen_Hex2Bcd_Array(day_time, MCU_RTC_Time.RTC_Arry, 6);
Gen_Printf_Time(MCU_RTC_Time.RTC_Arry, 1);
Set_Rtc_NowTime(day_time[3],day_time[4],day_time[5],day_time[2],day_time[0]);
EE_ReadBytes(time_read_buf,SLEEP_MINUTE_ADDR,2);
printf("%d minutes,%d seconds later will wakeup!\r\n",time_read_buf[0],time_read_buf[1]);
day_time[5] += time_read_buf[1];
day_time[4] += time_read_buf[0];
if(day_time[5] > 59)
{
day_time[5] = day_time[5] - 60;
day_time[4] += 1;
if(day_time[4] >59)
{
day_time[4] = day_time[4] - 60;
day_time[3] += 1;
if(day_time[3] > 23)
{
day_time[3] = day_time[3] - 24;
}
}
}
Set_Rtc_AlarmTime(day_time[3],day_time[4],day_time[5],day_time[2]);
printf("STM32进入standby休眠模式\r\n");
HAL_PWR_EnterSTANDBYMode(); //进入standby模式
}
memset(Usart2type.Usart2RecBuffer, 0x00, sizeof(Usart2type.Usart2RecBuffer)); //先清空缓冲区
return 1;
}
}
times --;
}while(times);
return 0;
}
接收处理,在串口回调函数中:
if(huart->Instance == USART2)
{
memcpy(Usart2type.Usart2RecBuffer,Usart2type.Usart2DMARecBuffer,Usart2type.UsartDMARecLen); //转存到待处理区域
printf("USART2 receive <<%s",Usart2type.Usart2DMARecBuffer);
if(strstr((char *)Usart2type.Usart2RecBuffer,"+QGPSLOC"))
{
gps_location_flag = 1;
extractCoordinatesAndConvert((char *)Usart2type.Usart2RecBuffer,&lat_ddmm,&lng_ddmm);
lng_ddddd = ddmmToDecimal(lng_ddmm);
lat_ddddd = ddmmToDecimal(lat_ddmm);
wgs84_to_gcj02(lat_ddddd, lng_ddddd, &gcjLat, &gcjLon);
snprintf(GPS_Info, sizeof(GPS_Info), "AT%.14f,%.14f", gcjLon, gcjLat);
// printf("GPS_Info is %s\n",GPS_Info);
}
memset(Usart2type.Usart2DMARecBuffer, 0x00, sizeof(Usart2type.Usart2DMARecBuffer)); //先清空DMA缓冲区
Usart2type.UsartRecFlag = 1;
}
2、定位数据获取
定位功能依旧是借助4G模组自带的功能进行实现,使用AT指令对模组进行配置以后就可以开启GNSS电源和进行GNSS定位。
EC800MCN_Send_Cmd(CAT1_CMD_OPENGPS,"OK",GPS_TIME0UT);
printf("AT+QGPSLOC=0\r\n");
HAL_UART_Transmit(&huart2, (uint8_t *)"AT+QGPSLOC=0\r\n", strlen("AT+QGPSLOC=0\r\n"), HAL_MAX_DELAY);
HAL_Delay(1000);
3、定位数据解析、转换
经度和纬度等信息是不能直接拿来使用的,首先要从模组获取,然后就是要转换成浮点型数据,再通过坐标转换算法,将wgs84坐标系数据转换成gcj02坐标系。坐标系转换的算法都在网上都有,可以找到C语言版本转换测试。合宙有一个在线的的转换平台:old.openluat.com/GPS-Offset.html
从模组获取到的数据为字符串,经过下面的处理方法截取需要的特定数据(相关代码由人工智能生成):
void extractCoordinatesAndConvert(const char *gpsInfo, double *latitude, double *longitude)
{
char *token = strtok((char *)gpsInfo, ",");
int index = 0;
while (token != NULL) {
if (++index == 2) { // N前的纬度
*latitude = atof(token);
token[strcspn(token, "NSEW")] = '\0'; // 移除N
} else if (index == 3) { // E前的经度
*longitude = atof(token);
token[strcspn(token, "NSEW")] = '\0'; // 移除E
break; // 已找到所需数据,退出循环
}
token = strtok(NULL, ",");
}
}
获取到具体的经纬度数据的时候,通过观察,原始数据是属于ddmm.mmmm 格式。下面是具体的转换算法:
int out_of_china(double lng, double lat)
{
return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);
}
double transformLat(double x, double y)
{
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(fabs(x));
ret += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * sin(y * pi) + 40.0 * sin(y / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * sin(y / 12.0 * pi) + 320.0 * sin(y * pi / 30.0)) * 2.0 / 3.0;
return ret;
}
double transformLon(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(fabs(x));
ret += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * sin(x * pi) + 40.0 * sin(x / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * sin(x / 12.0 * pi) + 300.0 * sin(x / 30.0 * pi)) * 2.0 / 3.0;
return ret;
}
double ddmmToDecimal(double ddmm)
{
int degrees = (int)ddmm / 100;
double minutes = ddmm - (degrees * 100);
return degrees + minutes / 60.0;
}
void wgs84_to_gcj02(double wgLat, double wgLon, double *pgLat, double *pgLon)
{
if (!out_of_china(wgLon, wgLat)) {
double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
double radLat = wgLat / 180.0 * pi;
double magic = sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = sqrt(magic);
dLat = (dLat * 180.0) / ((aa * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (aa / sqrtMagic * cos(radLat) * pi);
*pgLat = wgLat + dLat;
*pgLon = wgLon + dLon;
} else {
printf("超出范围\r\n");
*pgLat = wgLat;
*pgLon = wgLon;
}
}
4、时间校准、休眠、唤醒、单采等
由于需要借助外部的RTC进行时钟的设置,因此就是需要对开始定位时间进行对时,对时使用的是通信模组自有的平台,使用AT指令即可获取。程序设计获取时间为定位成功并且发送数据后的最后一个操作,对时结束后就进入到低功耗待机模式。此时单片机无法下载程序,全部外设暂停工作,仅保留外部低速时钟运行。
硬件上设计的有霍尔,霍尔内部接通单片机的外部唤醒引脚,使用磁铁靠近时,可以实现立即唤醒的操作,以立即执行定位和坐标上传。
sscanf((char *)Usart2type.Usart2RecBuffer, "%[^: \"]: \"%[^/]/%[^/]/%[^,],%[^:]:%[^:]:%[^+]+%[^\"]\"", buf1, buf2, buf3, buf4, buf5, buf6, buf7, buf8);
a = atoi(buf2);
b = atoi(buf3);
c = atoi(buf4);
d = atoi(buf5);
e = atoi(buf6);
f = atoi(buf7);
if( (a < 20) || (b > 12) || (c > 32) || (d > 24) || (e > 60) || (f > 60))
{
printf("CAT1:Timing error!\r\n");
return FALSE;
}
day_time[0] = (uint8_t)(a); //年
day_time[1] = (uint8_t)(b); //月
day_time[2] = (uint8_t)(c); //日
day_time[3] = (uint8_t)(d); //时
day_time[4] = (uint8_t)(e); //分
day_time[5] = (uint8_t)(f); //秒
Gen_Utc2Beijing(day_time); //将UTC时间转换为北京时间
printf("CAT1:校时成功 当前时间:");
Gen_Hex2Bcd_Array(day_time, MCU_RTC_Time.RTC_Arry, 6);
Gen_Printf_Time(MCU_RTC_Time.RTC_Arry, 1);
Set_Rtc_NowTime(day_time[3],day_time[4],day_time[5],day_time[2],day_time[0]);
day_time[4] += 1;
if(day_time[4] > 59)
{
day_time[4] = day_time[4] - 60;
day_time[3] += 1;
}
Set_Rtc_AlarmTime(day_time[3],day_time[4],day_time[5],day_time[2]);
printf("进入到休眠模式\r\n");
HAL_PWR_EnterSTANDBYMode(); //进入standby模式
休眠:使用的是单片机的standby最低功耗模式,该模式下只能被外部唤醒引脚、RTC定时闹钟(等)唤醒,本项目也使用了上述的两种唤醒方式:
__HAL_RCC_PWR_CLK_ENABLE();//打开standby的时钟
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); //使能WKUP1唤醒引脚 其余的不用配置
printf("使能唤醒引脚\r\n");
void Set_Rtc_NowTime(uint8_t hour,uint8_t minute,uint8_t second,uint8_t day,uint8_t year)
{
NewTime.Hours = hour;
NewTime.Minutes = minute;
NewTime.Seconds = second;
NewTime.SubSeconds = 0;
NewTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
NewTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &NewTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
NewDate.WeekDay = RTC_WEEKDAY_MONDAY;
NewDate.Month = RTC_MONTH_JUNE;
NewDate.Date = day;
NewDate.Year = year;
if (HAL_RTC_SetDate(&hrtc, &NewDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
}
void Set_Rtc_AlarmTime(uint8_t hour,uint8_t minute,uint8_t second,uint8_t day)
{
NewAlarm.AlarmTime.Hours = hour;
NewAlarm.AlarmTime.Minutes = minute;
NewAlarm.AlarmTime.Seconds = second;
NewAlarm.AlarmTime.SubSeconds = 0;
NewAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
NewAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
NewAlarm.AlarmMask = RTC_ALARMMASK_NONE;
NewAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
NewAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
NewAlarm.AlarmDateWeekDay = day;
NewAlarm.Alarm = RTC_ALARM_A;
if (HAL_RTC_SetAlarm_IT(&hrtc, &NewAlarm, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
}
5、串口1的部分指令操作
给串口1设计了一部分的小菜单,可以简单进行复位、简单信息获取和直接强制进入休眠模式,此时再次唤醒只能使用磁铁唤醒,相当于使用外部唤醒引脚唤醒,使的设备进入低功耗,类似于关机。
输入指令 | SLEEP | RESET | Mx Sx | SET | CANCEL |
---|---|---|---|---|---|
功能 | 强制单片机进入休眠状态(相当于关机) | 复位单片机以重新执行程序 | 设置休眠的分钟和时间,需要搭配使用 | 设置为地址持续获取并上传 | 设置为仅定位一次并上传 |
memcpy(Usart1type.Usart1RecBuffer,Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLen); //转存到待处理区域
// printf("IDLE----->%s\r\n",Usart1type.Usart1DMARecBuffer);
//可以添加一些设置或者判断
if(strstr((char *)Usart1type.Usart1RecBuffer,"RESET"))
{
printf("device reset!\r\n");
NVIC_SystemReset();
}
else if(strstr((char *)Usart1type.Usart1RecBuffer,"SLEEP"))
{
printf("device into stanby mode!\r\n");
HAL_PWR_EnterSTANDBYMode(); //进入standby模式
}
else if(strstr((char *)Usart1type.Usart1RecBuffer,"HELP"))
{
printf("Please input the number about what event you want:\r\n");
printf("1.【RESET】--重启设备\r\n");
printf("2.【SLEEP】--强制进入休眠\r\n");
printf("3.【Mx】--设置休眠分钟,例如M12\r\n");
printf("4.【Sx】--设置休眠秒,例如S20\r\n");
printf("6.【SET_ALWAYS_GET_LOCATION】--设置为持续获取定位信息,发送ALWAYS\r\n");
printf("7.【CANCEL_ALWAYS_GET_LOCATION】--取消持续获取定位信息,发送CANCEL\r\n");
}
else if(strstr((char *)Usart1type.Usart1RecBuffer,"SET"))
{
//写入FLAG的值为0xAA
if(EE_WriteBytes(&readbuffer[0],ALWAYS_LOCATE_FLAG,1))
{
printf("设置为循环定位\r\n");
}
}
else if(strstr((char *)Usart1type.Usart1RecBuffer,"CANCEL"))
{
//写入FLAG的值为0xBB
if(EE_WriteBytes(&readbuffer[1],ALWAYS_LOCATE_FLAG,1))
{
printf("设置为一次定位\r\n");
}
}
else if(strstr((char *)Usart1type.Usart1RecBuffer,"M"))
{
char *numPart = (char *)Usart1type.Usart1RecBuffer + 1;
// 将字符串转换为整数
time_buf[0] = atoi(numPart);
}
else if(strstr((char *)Usart1type.Usart1RecBuffer,"S"))
{
char *numPart = (char *)Usart1type.Usart1RecBuffer + 1;
// 将字符串转换为整数
time_buf[1] = atoi(numPart);
// printf("time_buf[1] is %d\r\n",time_buf[1]);
EE_WriteBytes(time_buf,SLEEP_MINUTE_ADDR,2);
printf("数据写入分钟是%d,秒是%d\n",time_buf[0],time_buf[1]);
}
/*AT直接透传*/
// else if(strstr((char *)Usart1type.Usart1RecBuffer,"AT"))
// {
// HAL_UART_Transmit(&huart2, Usart1type.Usart1RecBuffer, strlen((char *)Usart1type.Usart1RecBuffer), HAL_MAX_DELAY);
// }
memset(Usart1type.Usart1DMARecBuffer, 0x00, sizeof(Usart1type.Usart1DMARecBuffer)); //先清空DMA缓冲区
Usart1type.UsartRecFlag = 1;
三、原理图设计和PCB设计
参考移远官方设计手册进行原理图和PCB的相关选型和设计。
1、主控部分
- 主控配合外部低速时钟组成最小系统
- BOOT引导(预留)
预留BOOT位置,当单片机被无意锁死的情况下可以使用串口解锁。
- 复位、串口1通信口和下载口
- 电源滤波和ADC
滤波是为了给ADC采集提供稳定的电压,ADC采集的是供电的锂电池的电压(保留)。
- 存储芯片
提供一个数据存储区域,为以后续的功能进行扩展的时候预留。
- 霍尔芯片
设计霍尔芯片的初衷是添加一个外部唤醒的功能,使用按键的话又需要开孔,就会不防水。因此采用这种无接触的方式,进行唤醒,以实现立即采集定位的功能。霍尔唤醒功能需要额外使用磁铁唤醒。
2、电源部分
- 主要电源芯片
电源芯片使用的是矽力杰的SY7069ADC芯片进行转换输出3.9v电压以供给单片机和4G模组的供电。当输入电压低于3.9V的时候可以将电压升高到3.9V以供模组和单片机使用,当电压高于3.9V的时候,也可以给给单片机和模组供电,由于模组支持宽电压输入,在3.4~4.3V的电源内均可。
滤波电路设计按照技术手册说明设计。
- 供电电路
电池使用的是锂电池,使用TP4056进行充电,可以更换电阻以调节充电电流大小。原理图使用的是组合电阻,可以更方便更换电阻阻值。
- 单片机供电芯片和GNSS天线供电
使用的是SOT23封装的 XC6206P332MR芯片,对于G030单片机功耗来说,这个芯片足够,并且其体积小巧。
- 联网模组的GNSS备用电池
根据【Quectel_EC800M-CN_硬件设计手册_V1.2】技术手册备电相关部分说明,可以外置一个备用电池以存储部分星历以加快定位速度。
3、联网模组
- 使用了mos控制
由于整体要求低功耗,然后现在的方案是单片机+联网模组的组合方式,当定位完成并上传数据到服务器以后,整体会进入到低功耗模式,此时将模组直接断电,以降低功耗。当单片机处于休眠状态时,其外部引脚处高阻态,那么根据电路设计,MOS会关断4G模组供电电源。
- 使用有源天线设计
根据【Quectel_EC800M-CN_硬件设计手册_V1.2】技术手册,设计使用有源天线,以增强接收GNSS信号的能力。供电电压为3.0V左右。陶瓷天线,长宽为35mmx35mm。
-
使用4G弹簧天线
-
电平转换
由于单片机的电平为TTL电平,是3.3V,4G模组的电平是1.8V,最好还是不要直接接在一起,因此就是需要使用电平转换芯片用于串口的电平转换。
4、PCB部分
- PCB正面部分介绍
1、其中,在PCB的中心是4G模组的位置,然后上面部分是GNSS备用电池,右上角是电源进入口。
2、弹簧天线在右边,左边是GNSS天线部分。
3、在左下角是单片机,然后下面中间是两个状态灯,一个是供电指示灯,另外一个是网络状态灯。
4、在左侧边缘有个霍尔芯片,配合磁铁以实现外部唤醒的功能。
5、最下面预留的有两个通信口,一个是串口1,一个是SWD下载口。右边预留的是USB下载口。
- PCB底面部分介绍
1、PCB背部左上角是GNSS详细信息输出触点,预留可以使用,以查看更加详细的定位信息输出。
2、下面是供电引脚触点。
3、右边是SIM卡座,使用的是插拔式的nano sim。右下角是一个AT24C02存储芯片。
- 充电PCB介绍
1、由于需要防水需求,因此就是使用了一个防水的TYPEC胶塞,引脚以供电和串口打印数据。
3、充电芯片使用的是TP4056,串口芯片使用的是CH340N。
3、双板之间使用OK线跳线连接。有充电指示灯,红色为正在充电,绿色为充电完成。设置的有CC识别电阻,可以使用USBC数据线直接进行充电。
4、可以使用USBC数据线,连接电脑进行串口调试和信息查看。
四、设备功耗、外壳设计和参考
- 功耗参考
电池容量大小为2000mah,如果按照当前的功耗计算,可以使用够两个星期。
唤醒到休眠过程中,平均功耗为90ma。定位功耗每次不尽一致,由于会受到环境影响。
定位成功后,单片机处于休眠状态的整机功耗。平均功耗在160ua。
- 外壳设计
外壳使用3D打印机打印,上壳为倒置以防止雨水侵入。3D打印壳子为方正壳子,长宽高为:54mmx54mmx54mm.
PCB的最表层贴图有点儿bug,因此就删除了贴图。
- 堆叠设计
使用了堆叠的方案,上层板子是处理部分,中间部分是电池,电池被固定在中间,最下面是凸出的滚花螺母(固定自行车相机支架)和防水typec胶塞部分。
上下层板子之间使用塑料螺柱固定。
五、参考和注意事项
- 安装APP
定位app仅支持安卓手机安装使用。
可以下载一个手机串口调试助手,以方便使用手机进行供电和打印信息查看。
- APP部分展示
定位成功后数据发送到MQTT服务器,安卓app需要处于打开状态,然后就会收到上报的地址数据。可以点击放大按钮查看具体定位。
在app上面的标题栏下面展示的有具体的定位经度和纬度。
- 使用注意
- 底壳带有1/4英制滚花螺母,可以直接和自行车旋转云台配合使用,夹在自行车上固定。
- 不要浸泡在水里,尽量不要受到暴雨冲刷
- 天线请使用有源天线以及要去到室外进空旷处行定位,上方有高大的建筑物或者树木覆盖会造成定位失败或者定位偏差过大。
附件
见附件。
设计图

BOM


评论