diff --git a/data_embed/index.html b/data_embed/index.html index 29e5a21..1375df6 100644 --- a/data_embed/index.html +++ b/data_embed/index.html @@ -510,9 +510,17 @@ III: Values above are referring to latitude; distance between two latitudes is a
- +
+
+ + +
+
+ + +
@@ -989,7 +997,8 @@ Hard readable lengths / too long for our display:
This version runs the DL9SAU/DL3EL fork: https://github.com/DL9SAU/TTGO-T-Beam-LoRa-APRS
Licensed under: CC BY-NC-SA
-
+
+
diff --git a/data_embed/style.css b/data_embed/style.css index 2a11eb5..31e4e5e 100644 --- a/data_embed/style.css +++ b/data_embed/style.css @@ -845,7 +845,7 @@ header { margin: 10px; } #logo { - background-image: url('data:image/gif;base64,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'); + /* background-image: url('data:image/gif;base64,=='); */ width: 280px; height: 220px; margin: 0 auto; @@ -858,4 +858,4 @@ header { #file-progress.show { display: inline; -} \ No newline at end of file +} diff --git a/include/preference_storage.h b/include/preference_storage.h index b097ee2..7fb03c1 100644 --- a/include/preference_storage.h +++ b/include/preference_storage.h @@ -179,6 +179,10 @@ static const char *const PREF_APRSIS_SERVER_PORT_INIT = "aprsis_srv_p_i"; static const char *const PREF_APRSIS_SERVER_PORT = "aprsis_srv_p"; static const char *const PREF_APRSIS_FILTER_INIT = "aprsis_fltr_i"; static const char *const PREF_APRSIS_FILTER = "aprsis_fltr"; +static const char *const PREF_APRSIS_FILTER_LOCAL_INCOMING_INIT = "aprsis_fli_i"; +static const char *const PREF_APRSIS_FILTER_LOCAL_INCOMING = "aprsis_fli"; +static const char *const PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING_INIT = "aprsis_fliw_i"; +static const char *const PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING = "aprsis_fliw"; static const char *const PREF_APRSIS_CALLSIGN_INIT = "aprsis_call_i"; static const char *const PREF_APRSIS_CALLSIGN = "aprsis_call"; static const char *const PREF_APRSIS_PASSWORD_INIT = "aprsis_pw_i"; diff --git a/src/TTGO_T-Beam_LoRa_APRS.ino b/src/TTGO_T-Beam_LoRa_APRS.ino index d61947d..fb516fc 100644 --- a/src/TTGO_T-Beam_LoRa_APRS.ino +++ b/src/TTGO_T-Beam_LoRa_APRS.ino @@ -144,6 +144,9 @@ boolean aprsis_enabled = false; String aprsis_host = "euro.aprs2.net"; uint16_t aprsis_port = 14580; String aprsis_filter = ""; +String aprsis_own_filters_in = ""; +boolean aprsis_own_filter_in_is_whitelist = true; +String aprsis_own_filters_words_in = ""; String aprsis_callsign = ""; String aprsis_password = "-1"; uint8_t aprsis_data_allow_inet_to_rf = 0; // 0: disable (default). 1: gate to main qrg. 2: gate to secondary qrg. 3: gate to both frequencies @@ -1996,7 +1999,7 @@ void set_callsign() { #endif #endif } - Tcall = s; + Tcall = String(s); } // telemetry frames @@ -3099,6 +3102,8 @@ void load_preferences_cfg_file() aprsis_host = jsonElementFromPreferenceCFGString(PREF_APRSIS_SERVER_NAME,PREF_APRSIS_SERVER_NAME_INIT); aprsis_port = jsonElementFromPreferenceCFGInt(PREF_APRSIS_SERVER_PORT,PREF_APRSIS_SERVER_PORT_INIT); aprsis_filter = jsonElementFromPreferenceCFGString(PREF_APRSIS_FILTER,PREF_APRSIS_FILTER_INIT); + aprsis_own_filters_in = jsonElementFromPreferenceCFGString(PREF_APRSIS_FILTER_LOCAL_INCOMING,PREF_APRSIS_FILTER_LOCAL_INCOMING_INIT); + aprsis_own_filters_words_in = jsonElementFromPreferenceCFGString(PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING,PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING_INIT); aprsis_callsign = jsonElementFromPreferenceCFGString(PREF_APRSIS_CALLSIGN,PREF_APRSIS_CALLSIGN_INIT); aprsis_password = jsonElementFromPreferenceCFGString(PREF_APRSIS_PASSWORD,PREF_APRSIS_PASSWORD_INIT); aprsis_data_allow_inet_to_rf = jsonElementFromPreferenceCFGInt(PREF_APRSIS_ALLOW_INET_TO_RF,PREF_APRSIS_ALLOW_INET_TO_RF_INIT); @@ -3612,6 +3617,27 @@ void load_preferences_from_flash() } aprsis_filter = preferences.getString(PREF_APRSIS_FILTER, ""); + if (!preferences.getBool(PREF_APRSIS_FILTER_LOCAL_INCOMING_INIT)){ + preferences.putBool(PREF_APRSIS_FILTER_LOCAL_INCOMING_INIT, true); + preferences.putString(PREF_APRSIS_FILTER_LOCAL_INCOMING, ""); + } + aprsis_own_filters_in = preferences.getString(PREF_APRSIS_FILTER_LOCAL_INCOMING, ""); + if (!aprsis_own_filters_in.isEmpty() && aprsis_own_filters_in.startsWith("-")) { + aprsis_own_filter_in_is_whitelist = false; + aprsis_own_filters_in = String((aprsis_own_filters_in.c_str())+1); + aprsis_own_filters_in.trim(); + aprsis_own_filters_in.replace(" ", ""); + } else { + aprsis_own_filter_in_is_whitelist = true; + } + + if (!preferences.getBool(PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING_INIT)){ + preferences.putBool(PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING_INIT, true); + preferences.putString(PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING, ""); + } + aprsis_own_filters_words_in = preferences.getString(PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING, ""); + aprsis_own_filters_words_in.trim(); + if (!preferences.getBool(PREF_APRSIS_CALLSIGN_INIT)){ preferences.putBool(PREF_APRSIS_CALLSIGN_INIT, true); preferences.putString(PREF_APRSIS_CALLSIGN, aprsis_callsign); @@ -3730,6 +3756,13 @@ void setup_phase2_soft_reconfiguration(boolean runtime_reconfiguration) { if (runtime_reconfiguration) { setup_oled_timer_values(); + // fix exception: reference to free'd Tcall in OledHdr -> New copy of current Tcall + OledHdr = String(Tcall); + OledLine1 = "setup_phase2"; + OledLine2 = String(""); + OledLine3 = String(""); + OledLine4 = String(""); + OledLine5 = String(""); writedisplaytext(OledHdr,OledLine1,OledLine2,OledLine3,OledLine4,OledLine5); } // else: in setup() during boot, we have several unpredictable delays. That's why it's not called here @@ -3791,8 +3824,9 @@ void setup() // initialize ESP32 Process WDT, 120s T/O esp_task_wdt_init(120, true); - // Our BUILD_NUMBER. The define is not available in the WEBSERVR -> we need to assign a global variable - buildnr = BUILD_NUMBER; + // Our BUILD_NUMBER. The define is not available in the WEBSERVER -> we need to assign a global variable + //buildnr = BUILD_NUMBER; + buildnr = VERS_XXSHORT_BN; setup_compile_flags_info(); SPI.begin(SPI_sck,SPI_miso,SPI_mosi,SPI_ss); //DO2JMG Heltec Patch @@ -3956,7 +3990,6 @@ void setup() set_callsign(); writedisplaytext("LoRa-APRS","by DL9SAU & DL3EL","Build:" + buildnr,"Hello de " + Tcall,"For Factory Reset:"," press middle Button"); - Serial.println("LoRa-APRS by DL9SAU & DL3EL Build:" + buildnr); Serial.println("Time used since start (-2000ms delay): " + String(millis()-t_setup_entered-2000) + "ms"); delay(2000); diff --git a/src/taskWebServer.cpp b/src/taskWebServer.cpp index 17601d6..cd4a2b3 100644 --- a/src/taskWebServer.cpp +++ b/src/taskWebServer.cpp @@ -68,6 +68,9 @@ extern String to_aprsis_data; extern boolean aprsis_enabled; extern String aprsis_host; extern uint16_t aprsis_port; +extern String aprsis_own_filters_in; +extern boolean aprsis_own_filter_in_is_whitelist; +extern String aprsis_own_filters_words_in; extern String aprsis_filter; extern String aprsis_callsign; extern String aprsis_password; @@ -547,6 +550,8 @@ void refill_preferences_as_jsonData() s = s + "\n " + jsonLineFromPreferenceString(PREF_APRSIS_SERVER_NAME); s = s + "\n " + jsonLineFromPreferenceInt(PREF_APRSIS_SERVER_PORT); s = s + "\n " + jsonLineFromPreferenceString(PREF_APRSIS_FILTER); + s = s + "\n " + jsonLineFromPreferenceString(PREF_APRSIS_FILTER_LOCAL_INCOMING); + s = s + "\n " + jsonLineFromPreferenceString(PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING); s = s + "\n " + jsonLineFromPreferenceString(PREF_APRSIS_CALLSIGN); s = s + "\n " + jsonLineFromPreferenceString(PREF_APRSIS_PASSWORD); s = s + "\n " + jsonLineFromPreferenceInt(PREF_APRSIS_ALLOW_INET_TO_RF); @@ -1203,6 +1208,17 @@ void handle_SaveAPRSCfg() { s.trim(); preferences.putString(PREF_APRSIS_FILTER, s); } + if (server.hasArg(PREF_APRSIS_FILTER_LOCAL_INCOMING)){ + String s = server.arg(PREF_APRSIS_FILTER_LOCAL_INCOMING); + s.trim(); + s.replace(" ", ""); + preferences.putString(PREF_APRSIS_FILTER_LOCAL_INCOMING, s); + } + if (server.hasArg(PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING)){ + String s = server.arg(PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING); + s.trim(); + preferences.putString(PREF_APRSIS_FILTER_LOCAL_WORDS_INCOMING, s); + } if (server.hasArg(PREF_APRSIS_CALLSIGN)){ String s = server.arg(PREF_APRSIS_CALLSIGN); s.toUpperCase(); s.trim(); @@ -1762,6 +1778,79 @@ String generate_third_party_packet(String callsign, String packet_in) { return packet_out; } +char aprsis_own_filter_check(const char *data) { + char *q; + int len; + char call[10]; + + switch (data[0]) { + + case '!': + case '=': + len = strlen(data); + if ((len > 12 && data[12] == '_') || (len > 19 && data[19] == '_')) + return 'w'; + return 'p'; + case '/': + case '@': + len = strlen(data); + if ((len > 18 && data[18] == '_') || (len > 26 && data[26] == '_')) + return 'w'; + return 'p'; + case '[': + case '$': case '\'': + case '`': + return 'p'; + case ';': + len = strlen(data); + if ((len > 29 && data[29] == '_') || (len > 36 && data[36] == '_') || + (len > 20 && data[20] == '_') || (len > 28 && data[27] == '_')) + return 'w'; + return 'o'; + case ')': + return 'i'; + case '#': + case '*': + case '_': + return 'w'; + case 'T': + return 't'; + case ':': + if (strlen(data) < 11 || data[10] != ':') + return '~'; + strncpy(call, data+1, 9); + call[9] = 0; + if ((q = strchr(call, ' '))) + *q = 0; + if (!strncmp(call, "BLN", 3)) + return 'b'; + else if (!strncmp(call, "NWS-", 4)) + return 'n'; + else if (is_call_blacklisted(call)) + return '~'; + else if (data[11] == '?') + return 'q'; + else if (!strncmp(data + 11, "PARM.", 5) || !strncmp(data + 11, "UNIT.", 5) || !strncmp(data + 11, "EQNS.", 5) ||!strncmp(data + 11, "BITS.", 5)) + return 't'; + return 'm'; + case '?': + case '<': + return 'q'; + case '>': + return 's'; + case '}': + if ((q = strchr(data+1, ':')) && q > strchr(data+1, '>')) + return aprsis_own_filter_check(q + 1); + return '~'; + case '{': + case ',': + return 'u'; + case '%': + return 'p'; + } + return '~'; +} + // read packets from APRSIS void read_from_aprsis(void) { @@ -1948,6 +2037,44 @@ void read_from_aprsis(void) { if (is_call_blacklisted(s.c_str())) return; + if (!header_end[1]) + return; + + + if (!aprsis_own_filters_in.isEmpty()) { + char aprs_data_type = aprsis_own_filter_check(header_end+1); + + // Sometheing really negative, like a blacklisted call writing a message + if (aprs_data_type == '~') + return; + + if (aprsis_own_filter_in_is_whitelist) { + if (aprsis_own_filters_in.indexOf(aprs_data_type) == -1) { + // Data type is not whitelisted + return; + // else: // Found in whitelist, may pass + } + } else { + if (aprsis_own_filters_in.indexOf(aprs_data_type) != -1) { + // Data type is blacklisted + return; + } // else: Not found in blacklist, may pass + } + } + if (!aprsis_own_filters_words_in.isEmpty()) { + char *p = strdup(aprsis_own_filters_words_in.c_str()); + char *q = strtok(p, " "); + while(q) { + if (s.indexOf(q) > -1) { + free(p); + return; + } + q = strtok(NULL, " "); + } + if (p) + free(p); + } + String answer_message = handle_aprs_messsage_addressed_to_us(s.c_str()); boolean its_an_aprs_message_for_us = !answer_message.isEmpty(); if (answer_message == "M") { diff --git a/tools/buildscript_versioning.py b/tools/buildscript_versioning.py index c66e1db..06f974b 100644 --- a/tools/buildscript_versioning.py +++ b/tools/buildscript_versioning.py @@ -22,9 +22,22 @@ try: git_id = Popen('git rev-parse --short HEAD', stdout=PIPE, shell=True).stdout.read().strip().decode('ascii') version_full = "%s-%s" % (version_full, git_id) except: + #git_id = "0000000" + git_id = "No_GIT.." pass -version_string = "{} - {}".format(version_full, datetime.datetime.now()) +date_now = "%.16s" % datetime.datetime.now() +version_string = "{} - {}".format(version_full, date_now) + +# dl9sau: build_no in base62 -> base62 (0-9, a-z, A-Z) +# This gives us room for (62**2)-1 = 3843 builds between git commits. Should be enough +# git_id: length of 5 has hopefully enough entropy. +# VERS_XXSHORT_BN may also be sent on RF -> We keep it short. 8 bytes now, instead of typically 3 bytes before. +s="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +bnA=s[(int(build_no / len(s))) % len(s)] +bnB=s[build_no % len(s)] +vers_xxshort_bn="%.5s.%c%c" % (git_id, bnA, bnB) + hf = """ #ifndef BUILD_NUMBER #define BUILD_NUMBER "{}" @@ -35,7 +48,10 @@ hf = """ #ifndef VERSION_SHORT #define VERSION_SHORT "{}" #endif -""".format(build_no, version_string, version_full) +#ifndef VERS_XXSHORT_BN + #define VERS_XXSHORT_BN "{}" +#endif +""".format(build_no, version_string, version_full, vers_xxshort_bn) with open(FILENAME_VERSION_H, 'w+') as f: f.write(hf)