本作品转载自cyqsd’s blog采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。

以前我也想着捣鼓过树莓派搭建,那当然也是可以的。我觉得这应该是建立APRS站点成本最低的方式了,理论上已经无法再低了。国内玩这个的人我觉得是很少的,就算是有兴趣捣鼓,也是买的完完整整的成品,价格稍贵,自然人就少了。

至于并不是无线电爱好站看到本篇文章,但是颇有兴趣的,可以看下啥是APRS。

自动位置回报系统(Automatic Packet Report System,简称APRS) 为业余无线电中的一个项目,结合业余无线电和全球卫星定位系统(GPS)以AFSK AX.25通讯模式达到即时位置传送的目的。世界各地的APRS接收电台可连结上网际网路来上传该电台所接收到的APRS封包资讯至APRS伺服器,这些全世界各地的APRS伺服器将资料汇整供使用者读取。

准备

首先你得有个呼号,其次用呼号得到一个通行码,我不太认可说这个叫密码,因为。。。。。就不说了。

20200221144054464_25648.webp

生成的工具是开源,免费的,所以有非常多的衍生版本。我似乎很久以前还看过收费生成的。。。。

http://apps.magicbug.co.uk/passcode/index.php
http://www.hocool.com/radio/aprspassword

连接至china.aprs2.net

https://github.com/bg6cq/aprs

怎么连接到china.aprs2.net,我主要是看了这份源码:bg6jji/ESP8266_APRSWeather。(我找了一圈也没见着个请求API的手册)
友台BG6JJI的ESP8266基于Lua写的,但是用Lua不太方便,不能使用Arduino多得夸张的传感器例程,于是就改写到了Arduino上,这下就很方便了。

我只接上去了一个DHT11来读取温湿度,大气压,雨量传感器这些我都没装,如果你想加入进去,可以看ESP8266的传感器使用合集,里面列举了很多常见传感器连接ESP8266的例程。

代码很简单,就是简单的TCP连接。我不喜欢UDP连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <ESP8266WiFi.h>
#include "DHTesp.h"

#ifdef ESP32
#pragma message(THIS EXAMPLE IS FOR ESP8266 ONLY!)
#error Select ESP8266 board.
#endif

DHTesp dht;
WiFiClient client;
bool auth = false;
bool connect_wifi = false;
const char *host = "china.aprs2.net";
const int port = 14580;
const char *logininfo = "user BG8JUN pass XXXXX vers ESP8266Weather 0.0.1 filter m/500\r\n";
char senddata[150] = {0};

bool autoConfig()
{
WiFi.begin();
for (int i = 0; i < 20; i++)
{
int wstatus = WiFi.status();
if (wstatus == WL_CONNECTED)
{
Serial.println("AutoConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
WiFi.printDiag(Serial);
connect_wifi = true;
return true;
}
else
{
connect_wifi = false;
Serial.print("AutoConfig Waiting......");
Serial.println(wstatus);
delay(1000);
}
}
Serial.println("AutoConfig Faild!" );
return false;
}

void smartConfig()
{
WiFi.mode(WIFI_STA);
Serial.println("\r\nWait for Smartconfig");
WiFi.beginSmartConfig();
while (1)
{
Serial.print(".");
if (WiFi.smartConfigDone())
{
Serial.println("SmartConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
WiFi.setAutoConnect(true); // 设置自动连接
break;
}
delay(1000); // 这个地方一定要加延时,否则极易崩溃重启
}
}

void setup()
{
Serial.begin(115200);
Serial.println("");
// if (!autoConfig())
// {
// Serial.println("Start module");
// smartConfig();
// }
dht.setup(5, DHTesp::DHT11); // Connect DHT sensor to GPIO 5
WiFi.mode(WIFI_STA);
WiFi.begin("WIFI名称", "WIFI密码");
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("Connected");
Serial.print("IP Address:");
Serial.println(WiFi.localIP());
connect_wifi = true;
}

void loop()
{
delay(10000);

if (connect_wifi)
{
while (!client.connected())
{
if (!client.connect(host, port))
{
Serial.println("wait connection.to server ......");
}
}

while (client.connected())//当处于链接状态 时
{
if (client.available())//如果缓冲区字符串大于0
{
String line = client.readStringUntil('\r\n');//获取字符串
Serial.println(line);//把字符串传给串口

if (line.indexOf("javAPRSSrvr") != -1) { // !=-1含有 ==-1不含有
Serial.println("javAPRSSrvr");
} else if (line.indexOf("aprsc") != -1) {
Serial.println("aprsc");
client.print(logininfo);//向服务器反馈登录信息

} else if (line.indexOf("verified") != -1) {
//验证成功
Serial.println("verified");

auth = true;
} else {
Serial.println("none");
}

//
if (auth == true) {
//
int humidity = dht.getHumidity();
int temperature = dht.getTemperature();
int temperaturef = dht.toFahrenheit(temperature);
//
// -- c000s000g000t086r000p000h53b10020
// -- 每秒输出35个字节,包括数据末尾的换行符(OD,OA)
//
// -- 数据解析:
// -- c000:风向角度,单位:度。
// -- s000:前1分钟风速,单位:英里每小时
// -- g000:前5分钟最高风速,单位:英里每小时
// -- t086:温度(华氏)
// -- r000:前一小时雨量(0.01英寸)
// -- p000:前24小时内的降雨量(0.01英寸)
// -- h53:湿度(00%= 100%)
// -- b10020:气压(0.1 hpa)

snprintf(senddata, sizeof(senddata), "BG8JUN-0>AP51DW,qAS,:=2932.13N/10636.25E_000/000g000t0%dr000p000h%db09691 cyqsd's APRS use ESP32 144.800Mhz~ 3.3V\r\n", temperaturef, humidity);
client.print(senddata);//向服务器反馈信息

// Serial.print("APRS POST: ");
Serial.println(senddata);
// Serial.println("");
}
}
}
}
}

代码中很多我都注释了,因为我只是测试。可以根据自己需要修改。还有就是温度是℉,不是摄氏度,要转换。
需要配网连接Wifi的打开下面的注释,并注释掉WiFi.begin("WIFI名称", "WIFI密码");

1
2
3
4
5
//  if (!autoConfig())
// {
// Serial.println("Start module");
// smartConfig();
// }

串口怎么输出就随你了。如果对电量有要求,可以打开ESP8266的深度休眠,来省电,这些前几篇文章均有提及,这里就不在赘述了。

20200221143954374_12953.webp

服务器收到后是下面的样子,会对其解析。

20200221143811122_25670.webp

3. 连接至aprs.fi

aprs.fi也是一个非常好的网站,界面相对于前面的china.aprs2.net稍微现代化了很多,但是可能需要一些魔法才能看得到。如果你只是在上面看看信息,那就无需注册,如果需要上传就需要API码。注册之后就可以得到下面的信息:

20200222012849109_3760.webp

然后查看手册:
aprs.fi Application Programming Interface · Google Maps APRS
都写得清清楚楚的,一目了然。
代码很简单,就是简单的HTTP连接。官方有例程,我就不用贴代码了。下面截取的API手册的片段,使用JSON来传输,可以参考ESP8266使用MQTT连接阿里云物联网平台(官方NONOS_SDK和Arduino_IDE )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
https://api.aprs.fi/api/get?name=OH7RDA&what=loc&apikey=APIKEY&format=json

{
"command":"get",
"result":"ok",
"what":"loc",
"found":1,
"entries": [
{
"name":"OH7RDA",
"type":"l",
"time":"1267445689",
"lasttime":"1270580127",
"lat":"63.06717",
"lng":"27.66050",
"symbol":"\/#",
"srccall":"OH7RDA",
"dstcall":"APND12",
"phg":"44603",
"comment":"\/R,W,Wn,Tn Siilinjarvi",
"path":"WIDE2-2,qAR,OH7AA"
}
]
}

20200222012806846_14122.webp

其他

又水了一篇文,挺好。后面的小节是对前文的补充。

关于显示在地图上面的图标可以参考这个网页:APRS图标
关于呼号后面的数字后缀是啥意思,这里有中文的翻译:APRS协议规范的SSID

除了我上面写的两个站点,还有下面两个国内的。他们的数据是同步的,所以实现其中一个就可以了。
hamclub
hellocq

20200222023427496_28495.webp

20200222023427496_28495.webp

如果你只是想更加简单的玩一下,可以使用APRSdroid - APRS for Android

2020年4月10日更新:

APRS_CWOP气象报告 – Google Maps APRS - aprs.fi.webp

本作品转载自cyqsd’s blog采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。

以前我也想着捣鼓过树莓派搭建,那当然也是可以的。我觉得这应该是建立APRS站点成本最低的方式了,理论上已经无法再低了。国内玩这个的人我觉得是很少的,就算是有兴趣捣鼓,也是买的完完整整的成品,价格稍贵,自然人就少了。

至于并不是无线电爱好站看到本篇文章,但是颇有兴趣的,可以看下啥是APRS。

自动位置回报系统(Automatic Packet Report System,简称APRS) 为业余无线电中的一个项目,结合业余无线电和全球卫星定位系统(GPS)以AFSK AX.25通讯模式达到即时位置传送的目的。世界各地的APRS接收电台可连结上网际网路来上传该电台所接收到的APRS封包资讯至APRS伺服器,这些全世界各地的APRS伺服器将资料汇整供使用者读取。

准备

首先你得有个呼号,其次用呼号得到一个通行码,我不太认可说这个叫密码,因为。。。。。就不说了。

生成的工具是开源,免费的,所以有非常多的衍生版本。我似乎很久以前还看过收费生成的。。。。

http://apps.magicbug.co.uk/passcode/index.php http://www.hocool.com/radio/aprspassword

连接至china.aprs2.net

https://github.com/bg6cq/aprs

怎么连接到china.aprs2.net,我主要是看了这份源码:bg6jji/ESP8266_APRSWeather。(我找了一圈也没见着个请求API的手册)
友台BG6JJI的ESP8266基于Lua写的,但是用Lua不太方便,不能使用Arduino多得夸张的传感器例程,于是就改写到了Arduino上,这下就很方便了。

我只接上去了一个DHT11来读取温湿度,大气压,雨量传感器这些我都没装,如果你想加入进去,可以看ESP8266的传感器使用合集,里面列举了很多常见传感器连接ESP8266的例程。

代码很简单,就是简单的TCP连接。我不喜欢UDP连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <ESP8266WiFi.h>
#include "DHTesp.h"

#ifdef ESP32
#pragma message(THIS EXAMPLE IS FOR ESP8266 ONLY!)
#error Select ESP8266 board.
#endif

DHTesp dht;
WiFiClient client;
bool auth = false;
bool connect_wifi = false;
const char *host = "china.aprs2.net";
const int port = 14580;
const char *logininfo = "user BG8JUN pass XXXXX vers ESP8266Weather 0.0.1 filter m/500\r\n";
char senddata[150] = {0};

bool autoConfig()
{
WiFi.begin();
for (int i = 0; i < 20; i++)
{
int wstatus = WiFi.status();
if (wstatus == WL_CONNECTED)
{
Serial.println("AutoConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
WiFi.printDiag(Serial);
connect_wifi = true;
return true;
}
else
{
connect_wifi = false;
Serial.print("AutoConfig Waiting......");
Serial.println(wstatus);
delay(1000);
}
}
Serial.println("AutoConfig Faild!" );
return false;
}

void smartConfig()
{
WiFi.mode(WIFI_STA);
Serial.println("\r\nWait for Smartconfig");
WiFi.beginSmartConfig();
while (1)
{
Serial.print(".");
if (WiFi.smartConfigDone())
{
Serial.println("SmartConfig Success");
Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
WiFi.setAutoConnect(true); // 设置自动连接
break;
}
delay(1000); // 这个地方一定要加延时,否则极易崩溃重启
}
}

void setup()
{
Serial.begin(115200);
Serial.println("");
// if (!autoConfig())
// {
// Serial.println("Start module");
// smartConfig();
// }
dht.setup(5, DHTesp::DHT11); // Connect DHT sensor to GPIO 5
WiFi.mode(WIFI_STA);
WiFi.begin("WIFI名称", "WIFI密码");
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("Connected");
Serial.print("IP Address:");
Serial.println(WiFi.localIP());
connect_wifi = true;
}

void loop()
{
delay(10000);

if (connect_wifi)
{
while (!client.connected())
{
if (!client.connect(host, port))
{
Serial.println("wait connection.to server ......");
}
}

while (client.connected())//当处于链接状态 时
{
if (client.available())//如果缓冲区字符串大于0
{
String line = client.readStringUntil('\r\n');//获取字符串
Serial.println(line);//把字符串传给串口

if (line.indexOf("javAPRSSrvr") != -1) { // !=-1含有 ==-1不含有
Serial.println("javAPRSSrvr");
} else if (line.indexOf("aprsc") != -1) {
Serial.println("aprsc");
client.print(logininfo);//向服务器反馈登录信息

} else if (line.indexOf("verified") != -1) {
//验证成功
Serial.println("verified");

auth = true;
} else {
Serial.println("none");
}

//
if (auth == true) {
//
int humidity = dht.getHumidity();
int temperature = dht.getTemperature();
int temperaturef = dht.toFahrenheit(temperature);
//
// -- c000s000g000t086r000p000h53b10020
// -- 每秒输出35个字节,包括数据末尾的换行符(OD,OA)
//
// -- 数据解析:
// -- c000:风向角度,单位:度。
// -- s000:前1分钟风速,单位:英里每小时
// -- g000:前5分钟最高风速,单位:英里每小时
// -- t086:温度(华氏)
// -- r000:前一小时雨量(0.01英寸)
// -- p000:前24小时内的降雨量(0.01英寸)
// -- h53:湿度(00%= 100%)
// -- b10020:气压(0.1 hpa)

snprintf(senddata, sizeof(senddata), "BG8JUN-0>AP51DW,qAS,:=2932.13N/10636.25E_000/000g000t0%dr000p000h%db09691 cyqsd's APRS use ESP32 144.800Mhz~ 3.3V\r\n", temperaturef, humidity);
client.print(senddata);//向服务器反馈信息

// Serial.print("APRS POST: ");
Serial.println(senddata);
// Serial.println("");
}
}
}
}
}

代码中很多我都注释了,因为我只是测试。可以根据自己需要修改。还有就是温度是℉,不是摄氏度,要转换。
需要配网连接Wifi的打开下面的注释,并注释掉WiFi.begin("WIFI名称", "WIFI密码");

1
2
3
4
5
//  if (!autoConfig())
// {
// Serial.println("Start module");
// smartConfig();
// }

串口怎么输出就随你了。如果对电量有要求,可以打开ESP8266的深度休眠,来省电,这些前几篇文章均有提及,这里就不在赘述了。

2020022114395437412953webp

服务器收到后是下面的样子,会对其解析。

2020022114381112225670webp

3. 连接至aprs.fi

aprs.fi也是一个非常好的网站,界面相对于前面的china.aprs2.net稍微现代化了很多,但是可能需要一些魔法才能看得到。如果你只是在上面看看信息,那就无需注册,如果需要上传就需要API码。注册之后就可以得到下面的信息:

202002220128491093760webp

然后查看手册: aprs.fi Application Programming Interface · Google Maps APRS 都写得清清楚楚的,一目了然。
代码很简单,就是简单的HTTP连接。官方有例程,我就不用贴代码了。下面截取的API手册的片段,使用JSON来传输,可以参考ESP8266使用MQTT连接阿里云物联网平台(官方NONOS_SDK和Arduino_IDE )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
https://api.aprs.fi/api/get?name=OH7RDA&what=loc&apikey=APIKEY&format=json

{
"command":"get",
"result":"ok",
"what":"loc",
"found":1,
"entries": [
{
"name":"OH7RDA",
"type":"l",
"time":"1267445689",
"lasttime":"1270580127",
"lat":"63.06717",
"lng":"27.66050",
"symbol":"\/#",
"srccall":"OH7RDA",
"dstcall":"APND12",
"phg":"44603",
"comment":"\/R,W,Wn,Tn Siilinjarvi",
"path":"WIDE2-2,qAR,OH7AA"
}
]
}

2020022201280684614122webp

其他

又水了一篇文,挺好。后面的小节是对前文的补充。

关于显示在地图上面的图标可以参考这个网页:APRS图标 关于呼号后面的数字后缀是啥意思,这里有中文的翻译:APRS协议规范的SSID

除了我上面写的两个站点,还有下面两个国内的。他们的数据是同步的,所以实现其中一个就可以了。 hamclub hellocq

2020022202342749628495webp

2020022202342749628495webp

如果你只是想更加简单的玩一下,可以使用APRSdroid - APRS for Android

2020年4月10日更新:

APRSCWOP气象报告 – Google Maps APRS  aprsfiwebp