From 1a47fef6b08db02086f41cd1f7bcdff14cd2ff24 Mon Sep 17 00:00:00 2001 From: bg4uvr Date: Thu, 3 Oct 2024 06:42:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B6=88=E6=81=AF=E8=AF=AD=E5=8F=A5=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E7=94=B5=E5=8E=8BADC=E5=80=BC=EF=BC=8C=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + esp8266mws.ino | 1583 ++++++++++++++++++++++++------------------------ readme.md | 44 +- 3 files changed, 804 insertions(+), 824 deletions(-) diff --git a/.gitignore b/.gitignore index 79fd3b2..b099319 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/ *.bin +build/ diff --git a/esp8266mws.ino b/esp8266mws.ino index c6e4f61..df1853b 100644 --- a/esp8266mws.ino +++ b/esp8266mws.ino @@ -2,13 +2,13 @@ Esp8266MWS (Esp8266 Mini Weather Station) bg4uvr @ 2021.5 */ -//#define DEBUG_MODE //调试模式时不把语句发往服务器 Do not send statements to the server while debugging mode -//#define EEPROM_CLEAR //调试时清除EEPROM Clear EEPROM while debugging +// #define DEBUG_MODE //调试模式时不把语句发往服务器 Do not send statements to the server while debugging mode +// #define EEPROM_CLEAR //调试时清除EEPROM Clear EEPROM while debugging -const char fw[20] = {"0.17"}; // firmware version number +const char fw[20] = {"0.17a"}; // firmware version number -//包含头文件 -// Include header file +// 包含头文件 +// Include header file #include #include #include @@ -23,477 +23,476 @@ const char fw[20] = {"0.17"}; // firmware version number // ADC mode is set to measurement power mode ADC_MODE(ADC_VCC); -//判断到已连接调试主机时,将把调试消息发往主机 -// When the debug host is determined to be connected, the debug message is sent to the host +// 判断到已连接调试主机时,将把调试消息发往主机 +// When the debug host is determined to be connected, the debug message is sent to the host #define DBGPRINT(x) \ - if (client_dbg.connected()) \ - client_dbg.print(x); + if (client_dbg.connected()) \ + client_dbg.print(x); #define DBGPRINTLN(x) \ - if (client_dbg.connected()) \ - client_dbg.println(x); + if (client_dbg.connected()) \ + client_dbg.println(x); - -//系统状态枚举 -// System state enumeration +// 系统状态枚举 +// System state enumeration typedef enum { - SYS_FAIL, //非法状态 // Illegal status - SYS_CFG, //配置状态 // Configure the state - SYS_RUN, //运行状态 // Run status + SYS_FAIL, // 非法状态 // Illegal status + SYS_CFG, // 配置状态 // Configure the state + SYS_RUN, // 运行状态 // Run status } sys_mode_t; -//语言枚举 -// Language enumeration +// 语言枚举 +// Language enumeration typedef enum { - CN = 0, - EN = 1, + CN = 0, + EN = 1, } language_t; #define LANGUAGE_NUM 2 -//配置数据结构 -// Configure the data structure +// 配置数据结构 +// Configure the data structure typedef struct { - uint32_t crc; //校验值 // Check the value - float lon; //经度 // longitude - float lat; //纬度 // latitude - uint16_t password; // APRS密码 // APRS password - uint16_t send_interval; //最小发送间隔 //Minimum transmission interval - uint16_t aprs_server_port; // APRS服务器端口 //APRS server port - uint16_t debug_server_port; //调试主机端口 //Debug host port - char aprs_server_addr[26]; // APRS服务器地址 //APRS server address - char debug_server_addr[26]; //调试主机地址 //Debugging host address - char callsign[8]; //呼号 //callsign - char ssid[4]; // SSID - sys_mode_t sysstate; - language_t language; //语言 //language + uint32_t crc; // 校验值 // Check the value + float lon; // 经度 // longitude + float lat; // 纬度 // latitude + uint16_t password; // APRS密码 // APRS password + uint16_t send_interval; // 最小发送间隔 //Minimum transmission interval + uint16_t aprs_server_port; // APRS服务器端口 //APRS server port + uint16_t debug_server_port; // 调试主机端口 //Debug host port + char aprs_server_addr[26]; // APRS服务器地址 //APRS server address + char debug_server_addr[26]; // 调试主机地址 //Debugging host address + char callsign[8]; // 呼号 //callsign + char ssid[4]; // SSID + sys_mode_t sysstate; + language_t language; // 语言 //language } cfg_t; -//系统全局变量 -// System global variable -cfg_t mycfg; //系统配置参数 //System configuration parameters -WiFiClient client_aprs, client_dbg; //实例化aprs服务器连接和调试连接 //Instantiate APRS server connections and debug connections +// 系统全局变量 +// System global variable +cfg_t mycfg; // 系统配置参数 //System configuration parameters +WiFiClient client_aprs, client_dbg; // 实例化aprs服务器连接和调试连接 //Instantiate APRS server connections and debug connections Adafruit_BMP280 bmp; // BMP280实例 Adafruit_AHTX0 aht; // AHT20实例 -float voltage; //电池电压 //The battery voltage -uint32_t last_send; //上一次发送时刻 //Last Send Time +float voltage; // 电池电压 //The battery voltage +uint32_t last_send; // 上一次发送时刻 //Last Send Time uint32_t sleepsec; -char msgbuf[150] = {0}; //消息格式化缓存 //Message format cache -bool bm280_state = false; // bmp280状态(初始化是否成功) -bool aht20_state = false; // aht20状态(初始化是否成功) +char msgbuf[150] = {0}; // 消息格式化缓存 //Message format cache +bool bm280_state = false; // bmp280状态(初始化是否成功) +bool aht20_state = false; // aht20状态(初始化是否成功) // CRC32 校验程序 // CRC32 Check uint32_t mycrc32(uint8_t *data, int length) { - uint32_t crc = 0xffffffff; - uint8_t *ldata = data; - while (length--) - { - uint8_t c = *(ldata++); - for (uint32_t i = 0x80; i > 0; i >>= 1) + uint32_t crc = 0xffffffff; + uint8_t *ldata = data; + while (length--) { - bool bit = crc & 0x80000000; - if (c & i) - bit = !bit; - crc <<= 1; - if (bit) - crc ^= 0x04c11db7; + uint8_t c = *(ldata++); + for (uint32_t i = 0x80; i > 0; i >>= 1) + { + bool bit = crc & 0x80000000; + if (c & i) + bit = !bit; + crc <<= 1; + if (bit) + crc ^= 0x04c11db7; + } } - } - return crc; + return crc; } -//自动配网 -// Automatic distribution network +// 自动配网 +// Automatic distribution network void WiFisetup() { - WiFiManager wifiManager; - // wifiManager.resetSettings(); - wifiManager.setConfigPortalTimeout(300); - - if (!wifiManager.autoConnect("Esp8266MWS-SET")) - { - delay(3000); - ESP.reset(); - //重置并重试 - // Reset and retry - delay(5000); - } + WiFiManager wifiManager; + // wifiManager.resetSettings(); + wifiManager.setConfigPortalTimeout(300); + + if (!wifiManager.autoConnect("Esp8266MWS-SET")) + { + delay(3000); + ESP.reset(); + // 重置并重试 + // Reset and retry + delay(5000); + } } -//读取BMP280 -// read BMP280 +// 读取BMP280 +// read BMP280 bool read_bmp280(float *temperature, float *pressure) { - if (bm280_state == false) - { - const char *msg1[] = { - "BMP280读取失败", - "BMP280 read failed", + if (bm280_state == false) + { + const char *msg1[] = { + "BMP280读取失败", + "BMP280 read failed", + }; + DBGPRINTLN(msg1[mycfg.language]); + return false; + } + + // 设置BMP280采样参数 + // Set the BMP280 sampling parameter + bmp.setSampling(Adafruit_BMP280::MODE_FORCED, // FORCE模式读完自动转换回sleep模式 // return to sleep mode automatically after reading in FORCE mode + Adafruit_BMP280::SAMPLING_X1, + Adafruit_BMP280::SAMPLING_X4, + Adafruit_BMP280::FILTER_X16, + Adafruit_BMP280::STANDBY_MS_1); + *temperature = bmp.readTemperature(); + *pressure = bmp.readPressure(); + const char *msg2[] = { + "BMP280读取成功", + "BMP280 read successfully", }; - DBGPRINTLN(msg1[mycfg.language]); - return false; - } - - //设置BMP280采样参数 - // Set the BMP280 sampling parameter - bmp.setSampling(Adafruit_BMP280::MODE_FORCED, // FORCE模式读完自动转换回sleep模式 // return to sleep mode automatically after reading in FORCE mode - Adafruit_BMP280::SAMPLING_X1, - Adafruit_BMP280::SAMPLING_X4, - Adafruit_BMP280::FILTER_X16, - Adafruit_BMP280::STANDBY_MS_1); - *temperature = bmp.readTemperature(); - *pressure = bmp.readPressure(); - const char *msg2[] = { - "BMP280读取成功", - "BMP280 read successfully", - }; - DBGPRINTLN(msg2[mycfg.language]); - return true; + DBGPRINTLN(msg2[mycfg.language]); + return true; } -//读取AHT20 -// read AHT20 +// 读取AHT20 +// read AHT20 bool read_aht20(float *temperature, float *humidity) { - if (aht20_state == false) - { - const char *msg1[] = { - "AHT20读取失败", - "AHT20 read failed", - }; - DBGPRINTLN(msg1[mycfg.language]); - return false; - } + if (aht20_state == false) + { + const char *msg1[] = { + "AHT20读取失败", + "AHT20 read failed", + }; + DBGPRINTLN(msg1[mycfg.language]); + return false; + } - sensors_event_t humAHT, tempAHT; + sensors_event_t humAHT, tempAHT; - aht.getEvent(&humAHT, &tempAHT); + aht.getEvent(&humAHT, &tempAHT); - *temperature = tempAHT.temperature; - *humidity = humAHT.relative_humidity; - const char *msg2[] = { - "AHT20读取成功", - "AHT20 read successfully", - }; - DBGPRINTLN(msg2[mycfg.language]); - return true; + *temperature = tempAHT.temperature; + *humidity = humAHT.relative_humidity; + const char *msg2[] = { + "AHT20读取成功", + "AHT20 read successfully", + }; + DBGPRINTLN(msg2[mycfg.language]); + return true; } -//读取传感器并发送一次数据 -// Read the sensor and send the data once +// 读取传感器并发送一次数据 +// Read the sensor and send the data once void send_data() { - float temperatureAHT, humidity, temperatureBMP, pressure; //保存湿传感器的温度温度度,BMP280的温度、气压的浮点变量 - // Save the humidity sensor temperature, BMP280 temperature, air pressure floating point variables - char temperatureS[4] = {"..."}; - char humidityS[3] = {".."}; - char pressureS[6] = {"....."}; - - bool bmpRES = read_bmp280(&temperatureBMP, &pressure); //读取BMP280 Read BMP280 - bool ahtRES = read_aht20(&temperatureAHT, &humidity); //读取AHT20温度湿度 Read AHT20 temperature and humidity - - // BMP280读取成功 - // BMP280 read successfully - if (bmpRES) - { - snprintf(temperatureS, sizeof(temperatureS), "%03d", (int8_t)(temperatureBMP * 9 / 5 + 32)); //保存温度字符串 // Save the temperature string - snprintf(pressureS, sizeof(pressureS), "%05d", (uint16_t)(pressure / 10)); //保存气压字符串 // Save the barometric string - if (client_dbg.connected()) + float temperatureAHT, humidity, temperatureBMP, pressure; // 保存湿传感器的温度温度度,BMP280的温度、气压的浮点变量 + // Save the humidity sensor temperature, BMP280 temperature, air pressure floating point variables + char temperatureS[4] = {"..."}; + char humidityS[3] = {".."}; + char pressureS[6] = {"....."}; + + bool bmpRES = read_bmp280(&temperatureBMP, &pressure); // 读取BMP280 Read BMP280 + bool ahtRES = read_aht20(&temperatureAHT, &humidity); // 读取AHT20温度湿度 Read AHT20 temperature and humidity + + // BMP280读取成功 + // BMP280 read successfully + if (bmpRES) { - switch (mycfg.language) - { - case CN: - client_dbg.printf("\nbmp280温度:%0.2f\tbmp280气压:%0.2f\n", temperatureBMP, pressure); - break; - case EN: - client_dbg.printf("\nbmp280 temperature:%0.2f\tbmp280 pressure:%0.2f\n", temperatureBMP, pressure); - break; - default: - break; - } + snprintf(temperatureS, sizeof(temperatureS), "%03d", (int8_t)(temperatureBMP * 9 / 5 + 32)); // 保存温度字符串 // Save the temperature string + snprintf(pressureS, sizeof(pressureS), "%05d", (uint16_t)(pressure / 10)); // 保存气压字符串 // Save the barometric string + if (client_dbg.connected()) + { + switch (mycfg.language) + { + case CN: + client_dbg.printf("\nbmp280温度:%0.2f\tbmp280气压:%0.2f\n", temperatureBMP, pressure); + break; + case EN: + client_dbg.printf("\nbmp280 temperature:%0.2f\tbmp280 pressure:%0.2f\n", temperatureBMP, pressure); + break; + default: + break; + } + } } - } - // AHT20读取成功 - // AHT20 read successfully - if (ahtRES) - { - snprintf(temperatureS, sizeof(temperatureS), "%03d", (int8_t)(temperatureAHT * 9 / 5 + 32)); //保存温度字符串 // Save the temperature string - snprintf(humidityS, sizeof(humidityS), "%02d", (uint8_t)humidity); //保存湿度字符串 // Save the humidity string - if (client_dbg.connected()) + // AHT20读取成功 + // AHT20 read successfully + if (ahtRES) { - switch (mycfg.language) - { - case CN: - client_dbg.printf("aht20温度:%0.2f\taht20湿度:%0.2f\n", temperatureAHT, humidity); - break; - case EN: - client_dbg.printf("aht20 temperature:%0.2f\taht20 humidity:%0.2f\n", temperatureAHT, humidity); - break; - default: - break; - } + snprintf(temperatureS, sizeof(temperatureS), "%03d", (int8_t)(temperatureAHT * 9 / 5 + 32)); // 保存温度字符串 // Save the temperature string + snprintf(humidityS, sizeof(humidityS), "%02d", (uint8_t)humidity); // 保存湿度字符串 // Save the humidity string + if (client_dbg.connected()) + { + switch (mycfg.language) + { + case CN: + client_dbg.printf("aht20温度:%0.2f\taht20湿度:%0.2f\n", temperatureAHT, humidity); + break; + case EN: + client_dbg.printf("aht20 temperature:%0.2f\taht20 humidity:%0.2f\n", temperatureAHT, humidity); + break; + default: + break; + } + } } - } - //如果BMP280和AHT20均读取成功,那么平均两个传感器的温度 - // If both BMP280 and AHT20 are read successfully, then average the temperature of both sensors - if (ahtRES && bmpRES) - { - snprintf(temperatureS, sizeof(temperatureS), "%03d", (int8_t)(((temperatureAHT + temperatureBMP) / 2) * 9 / 5 + 32)); - if (client_dbg.connected()) - switch (mycfg.language) - { - case CN: - client_dbg.printf("两传感器平均温度:%0.2f\n", (temperatureAHT + temperatureBMP) / 2); - break; - case EN: - client_dbg.printf("Average temperature of two sensors:%0.2f\n", (temperatureAHT + temperatureBMP) / 2); - break; - default: - break; - } - } - - // 发送用户自定义消息 send user custom messsage - uint8_t sync_timeout = 50; // NTP同步超时计数器,设置为5秒 - configTime(0, 0, "ntp.aliyun.com", "time.asia.apple.com", "pool.ntp.org"); //设置时间格式以及时间服务器的网址 - DBGPRINTLN("Waiting for time sync from NTP server"); //等待时间和NTP服务器同步 - while (time(nullptr) <= 100000 && sync_timeout--) //这里需要多读几次,以等待时间同步完成(较早的时间可以认为同步尚未完成) - { - DBGPRINT("."); - delay(100); - } - time_t now = time(nullptr); //获取当前时间 - struct tm *timenow = gmtime(&now); //转换成年月日的数字 - snprintf(msgbuf, sizeof(msgbuf), "\nGMT time is: %s", asctime(timenow)); //格式化时间 - DBGPRINTLN(msgbuf); //发送到调试主机显示 - if ((timenow->tm_hour % 3 == 0) && (timenow->tm_min < (mycfg.send_interval / 60) + 1)) //指定时间间隔发送一次(最多可能会多发一次) - { - snprintf(msgbuf, sizeof(msgbuf), "%s-%s>APUVR:>esp8266mws ver%s https://github.com/bg4uvr/esp8266mws", mycfg.callsign, mycfg.ssid, fw); + // 如果BMP280和AHT20均读取成功,那么平均两个传感器的温度 + // If both BMP280 and AHT20 are read successfully, then average the temperature of both sensors + if (ahtRES && bmpRES) + { + snprintf(temperatureS, sizeof(temperatureS), "%03d", (int8_t)(((temperatureAHT + temperatureBMP) / 2) * 9 / 5 + 32)); + if (client_dbg.connected()) + switch (mycfg.language) + { + case CN: + client_dbg.printf("两传感器平均温度:%0.2f\n", (temperatureAHT + temperatureBMP) / 2); + break; + case EN: + client_dbg.printf("Average temperature of two sensors:%0.2f\n", (temperatureAHT + temperatureBMP) / 2); + break; + default: + break; + } + } + + // 发送用户自定义消息 send user custom messsage + uint8_t sync_timeout = 50; // NTP同步超时计数器,设置为5秒 + configTime(0, 0, "ntp.aliyun.com", "time.asia.apple.com", "pool.ntp.org"); // 设置时间格式以及时间服务器的网址 + DBGPRINTLN("Waiting for time sync from NTP server"); // 等待时间和NTP服务器同步 + while (time(nullptr) <= 100000 && sync_timeout--) // 这里需要多读几次,以等待时间同步完成(较早的时间可以认为同步尚未完成) + { + DBGPRINT("."); + delay(100); + } + time_t now = time(nullptr); // 获取当前时间 + struct tm *timenow = gmtime(&now); // 转换成年月日的数字 + snprintf(msgbuf, sizeof(msgbuf), "\nGMT time is: %s", asctime(timenow)); // 格式化时间 + DBGPRINTLN(msgbuf); // 发送到调试主机显示 + if ((timenow->tm_hour % 3 == 0) && (timenow->tm_min < (mycfg.send_interval / 60) + 1)) // 指定时间间隔发送一次(最多可能会多发一次) + { + snprintf(msgbuf, sizeof(msgbuf), "%s-%s>APUVR:>esp8266mws ver%s https://github.com/bg4uvr/esp8266mws", mycfg.callsign, mycfg.ssid, fw); #ifndef DEBUG_MODE - client_aprs.println(msgbuf); //数据发往服务器 // The data is sent to the server + client_aprs.println(msgbuf); // 数据发往服务器 // The data is sent to the server #endif - DBGPRINTLN(msgbuf); - } + DBGPRINTLN(msgbuf); + } - // 发送气象报文 Send weather messages - snprintf(msgbuf, sizeof(msgbuf), - "%s-%s>APUVR:!%07.2f%cR%08.2f%c_.../...g...t%sr...p...h%sb%s", - mycfg.callsign, mycfg.ssid, mycfg.lat, mycfg.lat > 0 ? 'N' : 'S', mycfg.lon, mycfg.lon > 0 ? 'E' : 'W', - temperatureS, humidityS, pressureS); + // 发送气象报文 Send weather messages + snprintf(msgbuf, sizeof(msgbuf), + "%s-%s>APUVR:!%07.2f%cR%08.2f%c_.../...g...t%sr...p...h%sb%sBat:%d", + mycfg.callsign, mycfg.ssid, mycfg.lat, mycfg.lat > 0 ? 'N' : 'S', mycfg.lon, mycfg.lon > 0 ? 'E' : 'W', + temperatureS, humidityS, pressureS, ESP.getVcc()); #ifndef DEBUG_MODE - client_aprs.println(msgbuf); //数据发往服务器 // The data is sent to the server + client_aprs.println(msgbuf); // 数据发往服务器 // The data is sent to the server #endif - DBGPRINTLN(msgbuf); + DBGPRINTLN(msgbuf); } -//登陆APRS服务器发送数据 -// Log on to the APRS server and send data +// 登陆APRS服务器发送数据 +// Log on to the APRS server and send data bool loginAPRS() { - const char *msg[] = { - "正在连接APRS服务器", - "Connecting to the APRS server", - }; - DBGPRINTLN(msg[mycfg.language]); - - uint8_t timeout = 0; //超时计数器 - while (timeout++ < 5) // 5次都未能成功连接服务器 // Failed to connect to the server - { - if (client_aprs.connect(mycfg.aprs_server_addr, mycfg.aprs_server_port)) + const char *msg[] = { + "正在连接APRS服务器", + "Connecting to the APRS server", + }; + DBGPRINTLN(msg[mycfg.language]); + + uint8_t timeout = 0; // 超时计数器 + while (timeout++ < 5) // 5次都未能成功连接服务器 // Failed to connect to the server { - const char *msg1[] = { - "APRS服务器已连接", - "The APRS server is connected", - }; - DBGPRINTLN(msg1[mycfg.language]); - - timeout = 0; //超时计数清零 - while (timeout++ < 100) //等待发送成功 - { - if (client_aprs.available()) //如果缓冲区字符串大于0 // If the buffer string is greater than 0 + if (client_aprs.connect(mycfg.aprs_server_addr, mycfg.aprs_server_port)) { - String line = client_aprs.readStringUntil('\n'); //获取字符串 // Get the string - DBGPRINTLN(line); //转发到调试主机 // Forward to the debug host - - //如果已经连接到服务器,则开始登录 - // If you are already connected to the server, start logging in - if (line.indexOf("aprsc") != -1 || // aprsc服务器 - line.indexOf("javAPRSSrvr") != -1) // javAPRSSrvr服务器 - { - const char *msg2[] = { - "正在登录ARPS服务器...", - "Logging on to the ARPS server...", - }; - DBGPRINTLN(msg2[mycfg.language]); - sprintf(msgbuf, "user %s-%s pass %d vers esp8266mws %s", mycfg.callsign, mycfg.ssid, mycfg.password, fw); - client_aprs.println(msgbuf); //发送登录语句 // Send the logon statement - DBGPRINTLN(msgbuf); - timeout = 0; //超时计数清零 - } - //登陆验证成功或者失败都发送数据(失败“unverified”也包含“verified”,验证失败也可发送数据,但会显示未验证) - // Send data if login verification is successful or fails (" Unverified "includes" Verified "if failed, or data can be sent if verification fails, but it will show" Unverified ") - else if (line.indexOf("verified") != -1) - { - const char *msg3[] = { - "APRS服务器登录成功", - "APRS server login successful", + const char *msg1[] = { + "APRS服务器已连接", + "The APRS server is connected", }; - DBGPRINTLN(msg3[mycfg.language]); - send_data(); //发送数据 send data - if (client_aprs.flush(10000)) //最长等待10秒来完成发送 Wait up to 10 seconds to complete the transmission - { - const char *msg100[] = { - "数据发送成功", - "data sent success", - }; - DBGPRINTLN(msg100[mycfg.language]); - return true; - } - else + DBGPRINTLN(msg1[mycfg.language]); + + timeout = 0; // 超时计数清零 + while (timeout++ < 100) // 等待发送成功 { - const char *msg101[] = { - "数据发送超时", - "data sending timeout", - }; - DBGPRINTLN(msg101[mycfg.language]); - return false; + if (client_aprs.available()) // 如果缓冲区字符串大于0 // If the buffer string is greater than 0 + { + String line = client_aprs.readStringUntil('\n'); // 获取字符串 // Get the string + DBGPRINTLN(line); // 转发到调试主机 // Forward to the debug host + + // 如果已经连接到服务器,则开始登录 + // If you are already connected to the server, start logging in + if (line.indexOf("aprsc") != -1 || // aprsc服务器 + line.indexOf("javAPRSSrvr") != -1) // javAPRSSrvr服务器 + { + const char *msg2[] = { + "正在登录ARPS服务器...", + "Logging on to the ARPS server...", + }; + DBGPRINTLN(msg2[mycfg.language]); + sprintf(msgbuf, "user %s-%s pass %d vers esp8266mws %s", mycfg.callsign, mycfg.ssid, mycfg.password, fw); + client_aprs.println(msgbuf); // 发送登录语句 // Send the logon statement + DBGPRINTLN(msgbuf); + timeout = 0; // 超时计数清零 + } + // 登陆验证成功或者失败都发送数据(失败“unverified”也包含“verified”,验证失败也可发送数据,但会显示未验证) + // Send data if login verification is successful or fails (" Unverified "includes" Verified "if failed, or data can be sent if verification fails, but it will show" Unverified ") + else if (line.indexOf("verified") != -1) + { + const char *msg3[] = { + "APRS服务器登录成功", + "APRS server login successful", + }; + DBGPRINTLN(msg3[mycfg.language]); + send_data(); // 发送数据 send data + if (client_aprs.flush(10000)) // 最长等待10秒来完成发送 Wait up to 10 seconds to complete the transmission + { + const char *msg100[] = { + "数据发送成功", + "data sent success", + }; + DBGPRINTLN(msg100[mycfg.language]); + return true; + } + else + { + const char *msg101[] = { + "数据发送超时", + "data sending timeout", + }; + DBGPRINTLN(msg101[mycfg.language]); + return false; + } + } + // 服务器已满 Server full + else if (line.indexOf("Server full") != -1 || + line.indexOf("Port full") != -1) + { + const char *msg4[] = { + "服务器已负荷已满,将稍后重试", + "The server is full and will try again later", + }; + DBGPRINTLN(msg4[mycfg.language]); + return false; + } + } + delay(100); // 每0.1秒检查一次是否接收到新数据 } - } - //服务器已满 Server full - else if (line.indexOf("Server full") != -1 || - line.indexOf("Port full") != -1) - { - const char *msg4[] = { - "服务器已负荷已满,将稍后重试", - "The server is full and will try again later", + // 10秒未接收预期数据,认为超时 + const char *msg6[] = { + "错误:接收APRS服务器数据超时", + "Error: Receive APRS server data timeout", }; - DBGPRINTLN(msg4[mycfg.language]); + DBGPRINTLN(msg6[mycfg.language]); return false; - } } - delay(100); //每0.1秒检查一次是否接收到新数据 - } - // 10秒未接收预期数据,认为超时 - const char *msg6[] = { - "错误:接收APRS服务器数据超时", - "Error: Receive APRS server data timeout", - }; - DBGPRINTLN(msg6[mycfg.language]); - return false; + // 连接APRS服务器失败 + // Failed to connect to the APRS server + else + delay(2000); } - //连接APRS服务器失败 - // Failed to connect to the APRS server - else - delay(2000); - } - - const char *msg7[] = { - "\n5次未能成功连接APRS服务器,将休眠1分钟后再重试", - "\nError: Failing to connect to APRS server for 5 times, will sleep for 1 minute and then try again", - }; - DBGPRINTLN(msg7[mycfg.language]); - return false; + + const char *msg7[] = { + "\n5次未能成功连接APRS服务器,将休眠1分钟后再重试", + "\nError: Failing to connect to APRS server for 5 times, will sleep for 1 minute and then try again", + }; + DBGPRINTLN(msg7[mycfg.language]); + return false; } -//显示配置数据 -// Display the configuration data +// 显示配置数据 +// Display the configuration data void dispset() { - switch (mycfg.language) - { + switch (mycfg.language) + { case CN: - client_dbg.println("系统当前配置:"); - client_dbg.print("aprs服务器地址:\t"); - client_dbg.println(mycfg.aprs_server_addr); - client_dbg.print("aprs服务器端口:\t"); - client_dbg.println(mycfg.aprs_server_port); - client_dbg.print("调试主机地址:\t"); - client_dbg.println(mycfg.debug_server_addr); - client_dbg.print("调试主机端口:\t"); - client_dbg.println(mycfg.debug_server_port); - client_dbg.print("呼号:\t"); - client_dbg.println(mycfg.callsign); - client_dbg.print("SSID:\t"); - client_dbg.println(mycfg.ssid); - client_dbg.print("APRS验证码:\t"); - client_dbg.println(mycfg.password); - client_dbg.print("发送间隔:\t"); - client_dbg.println(mycfg.send_interval); - client_dbg.print("经度:\t"); - client_dbg.printf("%0.2f\n", mycfg.lon); - client_dbg.print("纬度:\t"); - client_dbg.printf("%0.2f\n", mycfg.lat); - client_dbg.print("语言设置:\t"); - client_dbg.println(mycfg.language); - client_dbg.print("系统运行状态:\t"); - client_dbg.println(mycfg.sysstate); - break; + client_dbg.println("系统当前配置:"); + client_dbg.print("aprs服务器地址:\t"); + client_dbg.println(mycfg.aprs_server_addr); + client_dbg.print("aprs服务器端口:\t"); + client_dbg.println(mycfg.aprs_server_port); + client_dbg.print("调试主机地址:\t"); + client_dbg.println(mycfg.debug_server_addr); + client_dbg.print("调试主机端口:\t"); + client_dbg.println(mycfg.debug_server_port); + client_dbg.print("呼号:\t"); + client_dbg.println(mycfg.callsign); + client_dbg.print("SSID:\t"); + client_dbg.println(mycfg.ssid); + client_dbg.print("APRS验证码:\t"); + client_dbg.println(mycfg.password); + client_dbg.print("发送间隔:\t"); + client_dbg.println(mycfg.send_interval); + client_dbg.print("经度:\t"); + client_dbg.printf("%0.2f\n", mycfg.lon); + client_dbg.print("纬度:\t"); + client_dbg.printf("%0.2f\n", mycfg.lat); + client_dbg.print("语言设置:\t"); + client_dbg.println(mycfg.language); + client_dbg.print("系统运行状态:\t"); + client_dbg.println(mycfg.sysstate); + break; case EN: - client_dbg.println("System current configuration:"); - client_dbg.print("APRS server address:\t"); - client_dbg.println(mycfg.aprs_server_addr); - client_dbg.print("APRS server port:\t"); - client_dbg.println(mycfg.aprs_server_port); - client_dbg.print("Debug host address:\t"); - client_dbg.println(mycfg.debug_server_addr); - client_dbg.print("Debug host port:\t"); - client_dbg.println(mycfg.debug_server_port); - client_dbg.print("Callsign:\t"); - client_dbg.println(mycfg.callsign); - client_dbg.print("SSID:\t"); - client_dbg.println(mycfg.ssid); - client_dbg.print("APRS verification code:\t"); - client_dbg.println(mycfg.password); - client_dbg.print("Ssend interval:\t"); - client_dbg.println(mycfg.send_interval); - client_dbg.print("Longitude:\t"); - client_dbg.printf("%0.2f\n", mycfg.lon); - client_dbg.print("Latitude:\t"); - client_dbg.printf("%0.2f\n", mycfg.lat); - client_dbg.print("Language:\t"); - client_dbg.println(mycfg.language); - client_dbg.print("System state:\t"); - client_dbg.println(mycfg.sysstate); - break; + client_dbg.println("System current configuration:"); + client_dbg.print("APRS server address:\t"); + client_dbg.println(mycfg.aprs_server_addr); + client_dbg.print("APRS server port:\t"); + client_dbg.println(mycfg.aprs_server_port); + client_dbg.print("Debug host address:\t"); + client_dbg.println(mycfg.debug_server_addr); + client_dbg.print("Debug host port:\t"); + client_dbg.println(mycfg.debug_server_port); + client_dbg.print("Callsign:\t"); + client_dbg.println(mycfg.callsign); + client_dbg.print("SSID:\t"); + client_dbg.println(mycfg.ssid); + client_dbg.print("APRS verification code:\t"); + client_dbg.println(mycfg.password); + client_dbg.print("Ssend interval:\t"); + client_dbg.println(mycfg.send_interval); + client_dbg.print("Longitude:\t"); + client_dbg.printf("%0.2f\n", mycfg.lon); + client_dbg.print("Latitude:\t"); + client_dbg.printf("%0.2f\n", mycfg.lat); + client_dbg.print("Language:\t"); + client_dbg.println(mycfg.language); + client_dbg.print("System state:\t"); + client_dbg.println(mycfg.sysstate); + break; default: - break; - } + break; + } } -//显示系统信息 -// Display system information +// 显示系统信息 +// Display system information void dispsysinfo() { - //显示系统名称 - // Display the system name - client_dbg.println("\nEsp8266MWS 迷你气象站"); - client_dbg.println("\nEsp8266MWS Mini Weather Station"); - - //显示设备当前局域网IP地址 - // Displays the device's current LAN IP address - const char *msg[] = { - "\n当前设备IP地址为:", - "\nThe current device IP address is:", - }; - DBGPRINTLN(msg[mycfg.language]); - client_dbg.println(WiFi.localIP()); - - //显示当前配置 - // Displays the current configuration - dispset(); - - //显示提示消息 - // Display the prompt message - switch (mycfg.language) - { + // 显示系统名称 + // Display the system name + client_dbg.println("\nEsp8266MWS 迷你气象站"); + client_dbg.println("\nEsp8266MWS Mini Weather Station"); + + // 显示设备当前局域网IP地址 + // Displays the device's current LAN IP address + const char *msg[] = { + "\n当前设备IP地址为:", + "\nThe current device IP address is:", + }; + DBGPRINTLN(msg[mycfg.language]); + client_dbg.println(WiFi.localIP()); + + // 显示当前配置 + // Displays the current configuration + dispset(); + + // 显示提示消息 + // Display the prompt message + switch (mycfg.language) + { case CN: - client_dbg.println("\n\ + client_dbg.println("\n\ 配置命令格式说明:\n\ \n\ cfg -参数1 参数1数据 [-参数2 参数2数据] [...] [-参数n 参数n数据]\n\ @@ -531,9 +530,9 @@ void dispsysinfo() 所有命令发送时需要以 \\n 来结尾:\n\ rst\\n\n\ "); - break; + break; case EN: - client_dbg.println("\n\ + client_dbg.println("\n\ Format description of configuration command:\n\ \n\ cfg -parameter1 parameter1_data [-parameter2 parameter2_data] [...] [-parameterN parameterN_data]\n\ @@ -571,454 +570,454 @@ Rst command:\n\ All commands need to end with \\n when sending, such as:\n\ rst\\n\n\ "); - break; + break; default: - break; - } + break; + } } -//保存配置数据 -// Save the configuration data +// 保存配置数据 +// Save the configuration data void eeprom_save() { - mycfg.crc = mycrc32((uint8_t *)&mycfg + 4, sizeof(mycfg) - 4); //计算校验值 Calculate the checksum - for (uint8_t i = 0; i < sizeof(cfg_t); i++) //写入配置数据 Write configuration data - EEPROM.write(i, ((uint8_t *)&mycfg)[i]); - EEPROM.commit(); //提交数据 + mycfg.crc = mycrc32((uint8_t *)&mycfg + 4, sizeof(mycfg) - 4); // 计算校验值 Calculate the checksum + for (uint8_t i = 0; i < sizeof(cfg_t); i++) // 写入配置数据 Write configuration data + EEPROM.write(i, ((uint8_t *)&mycfg)[i]); + EEPROM.commit(); // 提交数据 } -//配置数据初始化 -// Configure data initialization +// 配置数据初始化 +// Configure data initialization void cfg_init() { - mycfg.lon = 100.0f; // default lon: 01d00.00` - mycfg.lat = 100.0f; // default lat: 001d00.00` - mycfg.send_interval = 900; - mycfg.password = 0; - mycfg.aprs_server_port = 14580; - mycfg.debug_server_port = 12345; - strcpy(mycfg.aprs_server_addr, "rotate.aprs2.net"); // default server - strcpy(mycfg.debug_server_addr, "192.168.1.125"); - strcpy(mycfg.callsign, "NOCALL"); - strcpy(mycfg.ssid, "13"); - mycfg.sysstate = SYS_CFG; - mycfg.language = CN; - eeprom_save(); + mycfg.lon = 100.0f; // default lon: 01d00.00` + mycfg.lat = 100.0f; // default lat: 001d00.00` + mycfg.send_interval = 900; + mycfg.password = 0; + mycfg.aprs_server_port = 14580; + mycfg.debug_server_port = 12345; + strcpy(mycfg.aprs_server_addr, "rotate.aprs2.net"); // default server + strcpy(mycfg.debug_server_addr, "192.168.1.125"); + strcpy(mycfg.callsign, "NOCALL"); + strcpy(mycfg.ssid, "13"); + mycfg.sysstate = SYS_CFG; + mycfg.language = CN; + eeprom_save(); } -//系统配置程序 -// System configurator +// 系统配置程序 +// System configurator void set_cfg() { - //如果没有接收到数据直接返回 - // If no data is received, return it directly - if (!client_dbg.available()) - return; - - //获取调试服务器发来的字符串 - // Get the string sent by the debug server - String line = client_dbg.readStringUntil('\n'); //每次解析到换行符 Each parse to a newline character - const char *msg[] = { - "\n您发送的命令为:", - "\nThe command you sent is:", - }; - DBGPRINTLN(msg[mycfg.language]); - client_dbg.println(line); //命令回显 // command echo - - //开始解析命令字符串 - // Begin parsing the command string - char *buf = new char[line.length() + 1]; //新建临时缓存,用于String类型转换为char[]类型 // Create a temporary cache for converting String to char[] - - strcpy(buf, line.c_str()); //复制字符串 // Copy the string - - // is RST command? - if (strncmp(buf, "rst", 3) == 0) - { - const char *msg0[] = { - "收到复位使命令,重新启动中...\n", - "Reset command received, restarting...\n", + // 如果没有接收到数据直接返回 + // If no data is received, return it directly + if (!client_dbg.available()) + return; + + // 获取调试服务器发来的字符串 + // Get the string sent by the debug server + String line = client_dbg.readStringUntil('\n'); // 每次解析到换行符 Each parse to a newline character + const char *msg[] = { + "\n您发送的命令为:", + "\nThe command you sent is:", }; - DBGPRINTLN(msg0[mycfg.language]); - delay(2000); - ESP.reset(); // reset~ - } - - //判断命令是否正确 - // Determine if the command is correct - if (strncmp(buf, "cfg ", 4) != 0) - { - const char *msg1[] = { - "命令格式不正确,请重新输入", - "Command format incorrect, please retry", - }; - DBGPRINTLN(msg1[mycfg.language]); - return; - } - - //分割存储参数 - // Split storage parameters - char *p; //新建用于分割字符串的指针 Create a new pointer to split the string - char *cmd[50] = {0}; //命令数组 Ordered array - uint8_t cnt; //参数计数 Parameters of the count - p = strtok(buf, " "); //字符串中搜索空格 Search for Spaces in a string - for (cnt = 0; cnt < sizeof(cmd) && p != NULL; cnt++) //搜索配置参数,保存到指针数组 Search for configuration parameters and save them to a pointer array - { - cmd[cnt] = p; - p = strtok(NULL, " "); - } - - //解析各参数 - // Parse the parameters - optind = 0; // optind 为getopt函数使用的全局变量,用于存储getopt的索引个数。此处必须清零,否则下次将无法工常工作 - /* Optind is a global variable used by getopt. It is used to store the number of indexes of - getopt.This place must be cleared, otherwise will not be able to work regularly next time */ - int ch; - bool fail = false; - bool ok = false; - while ((ch = getopt(cnt, cmd, "c:w:o:a:s:d:p:e:g:v:r:n:x:l:")) != -1) - { - switch (ch) + DBGPRINTLN(msg[mycfg.language]); + client_dbg.println(line); // 命令回显 // command echo + + // 开始解析命令字符串 + // Begin parsing the command string + char *buf = new char[line.length() + 1]; // 新建临时缓存,用于String类型转换为char[]类型 // Create a temporary cache for converting String to char[] + + strcpy(buf, line.c_str()); // 复制字符串 // Copy the string + + // is RST command? + if (strncmp(buf, "rst", 3) == 0) { - case 'c': - if (strlen(optarg) < 4 || strlen(optarg) > 6) - { - const char *msg3[] = { - "呼号长度小于4位或大于6位", - "Call sign length is less than 4 digits or longer than 6 digits", - }; - DBGPRINTLN(msg3[mycfg.language]); - fail = true; - } - else - { - strcpy(mycfg.callsign, optarg); - ok = true; - } - break; - case 'w': - if (atol(optarg) < 0 || atol(optarg) > 32768) - { - const char *msg16[] = { - "密码不合法", - "Password is not valid", - }; - DBGPRINTLN(msg16[mycfg.language]); - fail = true; - } - else - { - mycfg.password = atoi(optarg); - ok = true; - } - break; - case 'o': - if (atof(optarg) > 18000.0f || atof(optarg) < -18000.0f) - { - const char *msg11[] = { - "经度值超出范围", - "Longitude value out of range", - }; - DBGPRINTLN(msg11[mycfg.language]); - fail = true; - } - else - { - mycfg.lon = atof(optarg); - ok = true; - } - break; - case 'a': - if (atof(optarg) > 9000.0f || atof(optarg) < -9000.0f) - { - const char *msg12[] = { - "纬度值超过范围", - "Latitude value out of range", - }; - DBGPRINTLN(msg12[mycfg.language]); - fail = true; - } - else - { - mycfg.lat = atof(optarg); - ok = true; - } - break; - case 's': - if (strlen(optarg) > 25) - { - const char *msg17[] = { - "APRS服务器地址太长(最多25字节)", - "The APRS server address is too long(max 25 bytes)", - }; - DBGPRINTLN(msg17[mycfg.language]); - fail = true; - } - else - { - strcpy(mycfg.aprs_server_addr, optarg); - ok = true; - } - break; - case 'd': - if (strlen(optarg) > 2 || strlen(optarg) == 0) - { - const char *msg4[] = { - "SSID 长度大于2位或是等于0", - "SSID length longer than 2 digits or equal to 0", - }; - DBGPRINTLN(msg4[mycfg.language]); - fail = true; - } - else - { - strcpy(mycfg.ssid, optarg); - ok = true; - } - break; - case 'p': - if (atoi(optarg) < 20 || atoi(optarg) > 20000) - { - const char *msg20[] = { - "APRS服务器端口号不合法(正确范围:20-20000)", - "The APRS server port number is not valid (correct range: 20-20000)", - }; - DBGPRINTLN(msg20[mycfg.language]); - fail = true; - } - { - mycfg.aprs_server_port = atoi(optarg); - ok = true; - } - break; - case 'g': - if (strlen(optarg) > 25) - { - const char *msg18[] = { - "调试主机地址太长(最长25字节)", - "The debug host address is too long(max 25 bytes)", - }; - DBGPRINTLN(msg18[mycfg.language]); - fail = true; - } - else - { - strcpy(mycfg.debug_server_addr, optarg); - ok = true; - } - break; - case 'e': - if (atoi(optarg) < 1024 || atoi(optarg) > 65000) - { - const char *msg21[] = { - "调试主机端口号不合法(正确范围:1024-65000)", - "The debug host port number is not valid (correct range: 1024-65000)", - }; - DBGPRINTLN(msg21[mycfg.language]); - fail = true; - } - else - { - mycfg.debug_server_port = atoi(optarg); - ok = true; - } - break; - case 'n': - if (atoi(optarg) < 600 || atoi(optarg) > 1200) - { - const char *msg5[] = { - "发送间隔设置超出范围", - "The send interval setting out range", - }; - DBGPRINTLN(msg5[mycfg.language]); - fail = true; - } - else - { - mycfg.send_interval = atoi(optarg); - ok = true; - } - break; - case 'l': - if (atoi(optarg) < 0 || atoi(optarg) >= LANGUAGE_NUM) - { - const char *msg13[] = { - "语言设置值超出有效范围", - "The language setting value is out of the valid range", - }; - DBGPRINTLN(msg13[mycfg.language]); - fail = true; - } - else + const char *msg0[] = { + "收到复位使命令,重新启动中...\n", + "Reset command received, restarting...\n", + }; + DBGPRINTLN(msg0[mycfg.language]); + delay(2000); + ESP.reset(); // reset~ + } + + // 判断命令是否正确 + // Determine if the command is correct + if (strncmp(buf, "cfg ", 4) != 0) + { + const char *msg1[] = { + "命令格式不正确,请重新输入", + "Command format incorrect, please retry", + }; + DBGPRINTLN(msg1[mycfg.language]); + return; + } + + // 分割存储参数 + // Split storage parameters + char *p; // 新建用于分割字符串的指针 Create a new pointer to split the string + char *cmd[50] = {0}; // 命令数组 Ordered array + uint8_t cnt; // 参数计数 Parameters of the count + p = strtok(buf, " "); // 字符串中搜索空格 Search for Spaces in a string + for (cnt = 0; cnt < sizeof(cmd) && p != NULL; cnt++) // 搜索配置参数,保存到指针数组 Search for configuration parameters and save them to a pointer array + { + cmd[cnt] = p; + p = strtok(NULL, " "); + } + + // 解析各参数 + // Parse the parameters + optind = 0; // optind 为getopt函数使用的全局变量,用于存储getopt的索引个数。此处必须清零,否则下次将无法工常工作 + /* Optind is a global variable used by getopt. It is used to store the number of indexes of + getopt.This place must be cleared, otherwise will not be able to work regularly next time */ + int ch; + bool fail = false; + bool ok = false; + while ((ch = getopt(cnt, cmd, "c:w:o:a:s:d:p:e:g:v:r:n:x:l:")) != -1) + { + switch (ch) { - mycfg.language = (language_t)(atoi(optarg)); - ok = true; - } - break; + case 'c': + if (strlen(optarg) < 4 || strlen(optarg) > 6) + { + const char *msg3[] = { + "呼号长度小于4位或大于6位", + "Call sign length is less than 4 digits or longer than 6 digits", + }; + DBGPRINTLN(msg3[mycfg.language]); + fail = true; + } + else + { + strcpy(mycfg.callsign, optarg); + ok = true; + } + break; + case 'w': + if (atol(optarg) < 0 || atol(optarg) > 32768) + { + const char *msg16[] = { + "密码不合法", + "Password is not valid", + }; + DBGPRINTLN(msg16[mycfg.language]); + fail = true; + } + else + { + mycfg.password = atoi(optarg); + ok = true; + } + break; + case 'o': + if (atof(optarg) > 18000.0f || atof(optarg) < -18000.0f) + { + const char *msg11[] = { + "经度值超出范围", + "Longitude value out of range", + }; + DBGPRINTLN(msg11[mycfg.language]); + fail = true; + } + else + { + mycfg.lon = atof(optarg); + ok = true; + } + break; + case 'a': + if (atof(optarg) > 9000.0f || atof(optarg) < -9000.0f) + { + const char *msg12[] = { + "纬度值超过范围", + "Latitude value out of range", + }; + DBGPRINTLN(msg12[mycfg.language]); + fail = true; + } + else + { + mycfg.lat = atof(optarg); + ok = true; + } + break; + case 's': + if (strlen(optarg) > 25) + { + const char *msg17[] = { + "APRS服务器地址太长(最多25字节)", + "The APRS server address is too long(max 25 bytes)", + }; + DBGPRINTLN(msg17[mycfg.language]); + fail = true; + } + else + { + strcpy(mycfg.aprs_server_addr, optarg); + ok = true; + } + break; + case 'd': + if (strlen(optarg) > 2 || strlen(optarg) == 0) + { + const char *msg4[] = { + "SSID 长度大于2位或是等于0", + "SSID length longer than 2 digits or equal to 0", + }; + DBGPRINTLN(msg4[mycfg.language]); + fail = true; + } + else + { + strcpy(mycfg.ssid, optarg); + ok = true; + } + break; + case 'p': + if (atoi(optarg) < 20 || atoi(optarg) > 20000) + { + const char *msg20[] = { + "APRS服务器端口号不合法(正确范围:20-20000)", + "The APRS server port number is not valid (correct range: 20-20000)", + }; + DBGPRINTLN(msg20[mycfg.language]); + fail = true; + } + { + mycfg.aprs_server_port = atoi(optarg); + ok = true; + } + break; + case 'g': + if (strlen(optarg) > 25) + { + const char *msg18[] = { + "调试主机地址太长(最长25字节)", + "The debug host address is too long(max 25 bytes)", + }; + DBGPRINTLN(msg18[mycfg.language]); + fail = true; + } + else + { + strcpy(mycfg.debug_server_addr, optarg); + ok = true; + } + break; + case 'e': + if (atoi(optarg) < 1024 || atoi(optarg) > 65000) + { + const char *msg21[] = { + "调试主机端口号不合法(正确范围:1024-65000)", + "The debug host port number is not valid (correct range: 1024-65000)", + }; + DBGPRINTLN(msg21[mycfg.language]); + fail = true; + } + else + { + mycfg.debug_server_port = atoi(optarg); + ok = true; + } + break; + case 'n': + if (atoi(optarg) < 600 || atoi(optarg) > 1200) + { + const char *msg5[] = { + "发送间隔设置超出范围", + "The send interval setting out range", + }; + DBGPRINTLN(msg5[mycfg.language]); + fail = true; + } + else + { + mycfg.send_interval = atoi(optarg); + ok = true; + } + break; + case 'l': + if (atoi(optarg) < 0 || atoi(optarg) >= LANGUAGE_NUM) + { + const char *msg13[] = { + "语言设置值超出有效范围", + "The language setting value is out of the valid range", + }; + DBGPRINTLN(msg13[mycfg.language]); + fail = true; + } + else + { + mycfg.language = (language_t)(atoi(optarg)); + ok = true; + } + break; - default: - break; + default: + break; + } } - } - delete[] buf; //注销临时缓存 Unregister temporary cache + delete[] buf; // 注销临时缓存 Unregister temporary cache - // 如果有参数设置错误,不保存退出 - // If there is a parameter setting error, do not save and return - if (fail == true || ok == false) - { - const char *msg15[] = { - "设置的参数有误,设置未保存,请重新输入", - "There is error in setting parameters. Settings are not saved. Please try again", - }; - DBGPRINTLN(msg15[mycfg.language]); - return; - } + // 如果有参数设置错误,不保存退出 + // If there is a parameter setting error, do not save and return + if (fail == true || ok == false) + { + const char *msg15[] = { + "设置的参数有误,设置未保存,请重新输入", + "There is error in setting parameters. Settings are not saved. Please try again", + }; + DBGPRINTLN(msg15[mycfg.language]); + return; + } - //判断是否已经设置必设参数 - // Determines whether the required parameters have been set - if (mycfg.callsign == "" || mycfg.password == 0) - { - const char *msg2[] = { - "有必设参数未被设置,请重新输入。(呼号、密码这2项数据为必设参数)", - "Required parameters are not set. Please reenter.(Callsign and Password must be set)", + // 判断是否已经设置必设参数 + // Determines whether the required parameters have been set + if (mycfg.callsign == "" || mycfg.password == 0) + { + const char *msg2[] = { + "有必设参数未被设置,请重新输入。(呼号、密码这2项数据为必设参数)", + "Required parameters are not set. Please reenter.(Callsign and Password must be set)", + }; + DBGPRINTLN(msg2[mycfg.language]); + return; + } + + // 设置成功,保存退出 + //// Set successfully, save exit + mycfg.sysstate = SYS_RUN; // 更改系统状态为运行状态 Change the system state to running state + eeprom_save(); // 保存配置数据 Save configuration data + dispset(); // 显示系统当前配置 Displays the current system configuration + const char *msg14[] = { + "设置已保存(注意:此处无法准确检查所有参数的正确性,请自行检查确认)", + "Settings saved (Attention: that the correctness of all parameters cannot be checked accurately here. Please check and confirm by yourself.", }; - DBGPRINTLN(msg2[mycfg.language]); + DBGPRINTLN(msg14[mycfg.language]); return; - } - - //设置成功,保存退出 - //// Set successfully, save exit - mycfg.sysstate = SYS_RUN; //更改系统状态为运行状态 Change the system state to running state - eeprom_save(); //保存配置数据 Save configuration data - dispset(); //显示系统当前配置 Displays the current system configuration - const char *msg14[] = { - "设置已保存(注意:此处无法准确检查所有参数的正确性,请自行检查确认)", - "Settings saved (Attention: that the correctness of all parameters cannot be checked accurately here. Please check and confirm by yourself.", - }; - DBGPRINTLN(msg14[mycfg.language]); - return; } -//连接调试主机时的空闲扫描 -// Idle scan when connecting the debug host +// 连接调试主机时的空闲扫描 +// Idle scan when connecting the debug host void freeloop() { - set_cfg(); //处理配置命令 Processing configuration commands - ArduinoOTA.handle(); // OTA处理 OTA processing - delay(10); //延时 Time delay + set_cfg(); // 处理配置命令 Processing configuration commands + ArduinoOTA.handle(); // OTA处理 OTA processing + delay(10); // 延时 Time delay } -//系统初始化 -// System initialization +// 系统初始化 +// System initialization void setup() { - //配置指示灯并点亮 - // config and turnon LED - pinMode(2, OUTPUT); - digitalWrite(LED_BUILTIN, 0); + // 配置指示灯并点亮 + // config and turnon LED + pinMode(2, OUTPUT); + digitalWrite(LED_BUILTIN, 0); - WiFisetup(); //自动配网 Automatic distribution network - ArduinoOTA.setHostname("Esp8266MWS-OTA"); //设置OTA主机名 Set OTA hostname - ArduinoOTA.begin(); //初始化OTA Initialize OTA - EEPROM.begin(256); //初始化EEPROM initialize EEPROM + WiFisetup(); // 自动配网 Automatic distribution network + ArduinoOTA.setHostname("Esp8266MWS-OTA"); // 设置OTA主机名 Set OTA hostname + ArduinoOTA.begin(); // 初始化OTA Initialize OTA + EEPROM.begin(256); // 初始化EEPROM initialize EEPROM #ifdef DEBUG_MODE #ifdef EEPROM_CLEAR - //调试时用于清除EEPROM设置数据 - // Use to clear EEPROM setup data during debugging - for (uint8_t i = 0; i < 128; i++) - EEPROM.write(i, 0xff); - EEPROM.commit(); + // 调试时用于清除EEPROM设置数据 + // Use to clear EEPROM setup data during debugging + for (uint8_t i = 0; i < 128; i++) + EEPROM.write(i, 0xff); + EEPROM.commit(); #endif #endif - //读取系统配置数据 - // Read the system configuration data - for (uint8_t i = 0; i < sizeof(cfg_t); i++) - *((uint8_t *)&mycfg + i) = EEPROM.read(i); + // 读取系统配置数据 + // Read the system configuration data + for (uint8_t i = 0; i < sizeof(cfg_t); i++) + *((uint8_t *)&mycfg + i) = EEPROM.read(i); - //校验配置数据,如果校验失败,初始化默认配置数据并进入置模式 - // Validate the configuration data. If the validation fails, initialize the default configuration data and enter set mode - if (mycrc32((uint8_t *)&mycfg + 4, sizeof(mycfg) - 4) != mycfg.crc) - cfg_init(); + // 校验配置数据,如果校验失败,初始化默认配置数据并进入置模式 + // Validate the configuration data. If the validation fails, initialize the default configuration data and enter set mode + if (mycrc32((uint8_t *)&mycfg + 4, sizeof(mycfg) - 4) != mycfg.crc) + cfg_init(); - //重定义I2C端口(SDA、SCL) //Redefine I2C ports (SDA, SCL) - Wire.begin(12, 14); + // 重定义I2C端口(SDA、SCL) //Redefine I2C ports (SDA, SCL) + Wire.begin(12, 14); - //初始化bmp280 - if (bmp.begin()) // if (!bmp.begin(BMP280_ADDRESS_ALT)) - bm280_state = true; + // 初始化bmp280 + if (bmp.begin()) // if (!bmp.begin(BMP280_ADDRESS_ALT)) + bm280_state = true; - //初始化aht20 - if (aht.begin()) - aht20_state = true; + // 初始化aht20 + if (aht.begin()) + aht20_state = true; } -//程序主循环 -// main loop +// 程序主循环 +// main loop void loop() { - sleepsec = mycfg.send_interval; - - //判断当前系统状态 - // Determine the current system state - switch (mycfg.sysstate) - { - //运行状态 - // run state - case SYS_RUN: + sleepsec = mycfg.send_interval; - client_dbg.connect(mycfg.debug_server_addr, mycfg.debug_server_port); - dispsysinfo(); + // 判断当前系统状态 + // Determine the current system state + switch (mycfg.sysstate) + { + // 运行状态 + // run state + case SYS_RUN: - //只要服务器连接未断开就一直循环运行 - // Loops as long as the server connection is not broken - do { - //如果登录发送数据 失败 - // If logon and send data fail - if (!loginAPRS()) - sleepsec = 60; + client_dbg.connect(mycfg.debug_server_addr, mycfg.debug_server_port); + dispsysinfo(); - client_aprs.stop(); //关闭已经创建的连接 Close the connection that has been created - last_send = millis(); //保存最后发送的时间 Save the last sent time + // 只要服务器连接未断开就一直循环运行 + // Loops as long as the server connection is not broken + do + { + // 如果登录发送数据 失败 + // If logon and send data fail + if (!loginAPRS()) + sleepsec = 60; + + client_aprs.stop(); // 关闭已经创建的连接 Close the connection that has been created + last_send = millis(); // 保存最后发送的时间 Save the last sent time + + // 没有到达延迟时间,并且调试连接还在连接,一直等待 + // There is no latency and the debug connection is still connected + while (millis() - last_send < sleepsec * 1000 && client_dbg.connected()) + freeloop(); + } while (client_dbg.connected()); + + client_aprs.stop(); // 关闭已经创建的连接 Close the connection that has been created + delay(100); // 延时0.1秒,以等待连接关闭完成 Delay 0.1 second to wait for the connection closing to complete + + // 工作完成,准备休眠 + // Work done, ready for hibernation + digitalWrite(LED_BUILTIN, 1); // 关灯 turnoff LED + ESP.deepSleep((uint64_t)sleepsec * 1000 * 1000); // 休眠 sleep + break; - //没有到达延迟时间,并且调试连接还在连接,一直等待 - // There is no latency and the debug connection is still connected - while (millis() - last_send < sleepsec * 1000 && client_dbg.connected()) - freeloop(); - } - while (client_dbg.connected()); + case SYS_CFG: + default: + // 一直尝试连接配置服务器 + // Always try to connect to the configuration server + while (!client_dbg.connect(mycfg.debug_server_addr, mycfg.debug_server_port)) + { + ArduinoOTA.handle(); // OTA处理 OTA processing + delay(2000); // 延时2秒 Delay 2 seconds, + } - client_aprs.stop(); //关闭已经创建的连接 Close the connection that has been created - delay(100); //延时0.1秒,以等待连接关闭完成 Delay 0.1 second to wait for the connection closing to complete + // 已连接到默认配置服务器,显示系统信息 + // The default configuration server is connected + dispsysinfo(); - //工作完成,准备休眠 - // Work done, ready for hibernation - digitalWrite(LED_BUILTIN, 1); //关灯 turnoff LED - ESP.deepSleep((uint64_t)sleepsec * 1000 * 1000); //休眠 sleep - break; + client_dbg.println("系统配置数据校验失败,已重新初始化,请发送配置命令配置系统"); + client_dbg.println("System configuration data validation failed, has been reinitialized, please send configuration command to configure the system"); - case SYS_CFG: - default: - //一直尝试连接配置服务器 - // Always try to connect to the configuration server - while (!client_dbg.connect(mycfg.debug_server_addr, mycfg.debug_server_port)) - { - ArduinoOTA.handle(); // OTA处理 OTA processing - delay(2000); //延时2秒 Delay 2 seconds, - } - - //已连接到默认配置服务器,显示系统信息 - // The default configuration server is connected - dispsysinfo(); - - client_dbg.println("系统配置数据校验失败,已重新初始化,请发送配置命令配置系统"); - client_dbg.println("System configuration data validation failed, has been reinitialized, please send configuration command to configure the system"); - - //此处解析配置命令,直到配置成功转为运行状态 - // The configuration command is parsed here until the configuration is successfully turned into a running state - while (client_dbg.connected() && mycfg.sysstate != SYS_RUN) - freeloop(); - break; - } + // 此处解析配置命令,直到配置成功转为运行状态 + // The configuration command is parsed here until the configuration is successfully turned into a running state + while (client_dbg.connected() && mycfg.sysstate != SYS_RUN) + freeloop(); + break; + } } diff --git a/readme.md b/readme.md index f596d7f..171ada2 100644 --- a/readme.md +++ b/readme.md @@ -2,27 +2,21 @@ **Esp8266 Mini Weather Station** -最新更新(2024.10.2): +**最新更新**(2024.10.2): 电压检测功能由于硬件限制及软件不够完善,存在一定不足,不仅增加了制作难度,且对使用非太阳能电源的朋友毫无用处,现取消电压检测相关的全部功能,另取消了相应的设置功能。 -本版本代码改动较大,尚未经长期运行检验,有存在BUG的可能(0.16e版为的稳定版本)。 - -**鉴于相关政策法规的原因,本资料仅限学习Arduino编程及APRS相关知识使用,制作使用者需要自行承担一切后果,特此声明!!!** - +本版本代码改动较大,尚未经长期运行检验,有存在BUG的可能(0.16e版为稳定版本)。 +**鉴于相关政策法规的原因,本资料仅限学习Arduino编程及APRS相关知识使用,制作使用者需要自行承担一切后果,特此声明!!!** 本代码在以下地址同步更新: -​ https://github.com/bg4uvr/esp8266mws +​ -​ https://gitee.com/bg4uvr/esp8266mws (国内建议使用) +​ (国内建议使用) ![](doc/pic/1.jpg) - - - - ## 简介 这是我学习ESP8266 Arduino的一个实验,它使用常见的esp8266实验板(如NODEMCU、WEMOS等),外接两只I2C总线的气压温度湿度传感器,来实现了简单的APRS气象站功能(说是简单是因为它并没有风向、风速和雨量功能,而且后期也没有加入这些功能的打算,原因是这类传感器价格比较贵,不太适应瞎折腾玩 :-p)。实现类似功能的开源小制作估计也有不少,相比较而言,我这个的最主要特点以下几个: @@ -49,9 +43,7 @@ 我自己制作小气象站的运行状态可以在这里看到 -​ https://aprs.fi/weather/a/BG4UVR-13 - - +​ ## 重要提示 @@ -61,8 +53,6 @@ - 这个制作在使用的时候,需要设置相关的个人呼号和验证码,这些信息请自行准备,我不提供关于此方面的信息。 - 如果您基于本代码重新修改发布您自己的作品时,也千万注意不要将上述信息内置于您的代码中。 - - ## 硬件连接 由于电路结构非常简单,此处只画个简单的示意图(注意上面没有画太阳能电池板、充电控制板和锂电保护板) @@ -81,11 +71,11 @@ **划重点**:BMP280模块,根据模块硬件接线不同,会有两种不同的硬件i2c设备地址,如果你的代码无法检测到它,请把代码中 - `bmp.begin()` + `bmp.begin()` - 更改为 + 更改为 - `bmp.begin(BMP280_ADDRESS_ALT)` + `bmp.begin(BMP280_ADDRESS_ALT)` 按目前的实际情况来看,一般某宝的AHT20+BMP280一体模块,用的是第一种地址;单独的BMP280模块,是用的第二种地址。 @@ -93,14 +83,13 @@ 为了使系统休眠后可以自动唤醒,需要将GPIO16(D0)与RST脚通过470欧电阻互相连接。因为电路板上RST脚原有外围电路的影响,如果此电阻值小于470欧,模块上原有的复位按键功能可能会失效(但无其他不良影响);如果此电阻阻值大于约1K欧,系统休眠后将可能会无法自动唤醒(由于不同厂商的模块元件参数可能会有所差别,所以上述具体数值将会有所变化)。所以此电阻选择的原则是:宁小勿大,甚至直接短接也可以,只是原有的复位按键会不起作用。 - ## 代码编译与固件烧写 1. 熟悉arduino的朋友,可以直接使用Arduino IDE,在安装好相应库的情况下,直接进行编译和下载。代码中已经包含了OTA的代码,首次烧写固件后,后面可以直接OTA(空中无线固件更新)。 2. 不熟悉arduino的朋友,可以下载乐鑫官方的的固件下载工具,目前的最新版官方地址如下: - https://www.espressif.com/sites/default/files/tools/flash_download_tool_v3.8.8_0.zip + 下载好解压完成后,双击主程序文件运行,如果是windows10的操作系统,会弹出一个警告,选择“仍要运行”即可。出现的窗口中,“Chip Type”选择 “ESP8266”,“Work Mode”选择“develop”,选择已编译好的.bin文件,再选择你自己的串口号,其他选项按下图中的设置,然后点击 start 即可下载。 @@ -112,8 +101,6 @@ 可以参考一下,相信如果仔细阅读应该会有所帮助。 - - ## 使用方法 - 连接WiFi @@ -131,7 +118,7 @@ 1. 准备一个网络调试工具软件,我用的是下面这个(免费,菜单支持中、英文显示): - http://free.cmsoft.cn/download/cmsoft/assistant/netassist4.3.29.zip + 2. 配置你的无线路由器内网IP地址段为 192.168.1.X 网段,并且开启DHCP功能,然后设置你电脑的IP地址为192.168.1.125 。 @@ -145,28 +132,21 @@ 5. 配置命令的具体使用,窗口会显示详细的说明,按说明设置即可。 - - - 正常运行 - 使用命令行配置好系统后,系统就已经正常运行了。在打开网络调试工具并且已经和esp8266连接的情况下,窗口中会显示相应的运行状态。此时电路是一直工作的,不会进入休眠状态,OTA系统正常运行,可以直接使用 Arduino IDE 进行代码更新。 - 如果关闭了电脑上的网络调试工具,或者点击关闭来结束了网络连接,那么esp8266马上会进入节能休眠状态。一但唤醒时间到达,系统将自动进行一次测量,并送相应数据到APRS服务器,然后再次进入休眠状态。 - 因为系统处于间歇运行状态,每次工作时间大约只有10秒钟,所以如果需要进行空中固件更新,那么只要打开网络调试软件的服务器状态,等待系统再次工作时,就会自动连接上电脑,此时就可以进行空中更新操作了。 - - ## 补充说明 1. 因为写本说明的时候,我自己的装置已经制作完成,所以没来得及拍相关的照片,并且可能是没有原理图的原因,有爱好者反应看完说明有种无从下手的感觉,好在火腿**BG4VRG** 在实际制作过程中,为了方便其他人理解,专门拍摄照片并写了自己的制作注意事项,非常有参考价值,在此向他表示感谢。 - **[《BG4VRG写的制作参考》](doc/newbie-setup.md)** (注意:0.17版本已不需) + **[《BG4VRG写的制作参考》](doc/newbie-setup.md)** (注意:0.17版本之后的版本已不再需要拆除两只电阻!) 2. 待补充~ - - **最后:关于代码中的英文** ~~因为我的英文不好,所以在阅读外国朋友的代码时特别麻烦,正因为如此,我特别能理解外国朋友看到中文介绍和代码注释时的感觉。所以我特别使用电脑翻译了全部的说明文字和代码的注释内容,以方便外国朋友。但我知道电脑翻译的正确性和准确性是极差的,所以英文的内容仅是无奈情况下的参考。~~ 我把原来英文说明部分删除了,因为是机器翻译的,感觉不仅实用价值不大,还造成了文本内容过长,外国朋友大可以直接使用浏览器的翻译功能,代码中的英文注释仍然保留着,欢迎有能力的朋友能帮忙完善代码中这些英文内容,可以通过发 pull requests 来更新维护,在此表示感谢~ -