Porównaj commity

...

2 Commity

Autor SHA1 Wiadomość Data
Thomas Osterried 9b66a4cc07 New important fix. And: Many new features. bug fixes and improvements
Last commit from today lead to a stack smash (buffer overflow) in is_call_valid().
Found the bug -> fixed with this commit.

is_call_blacklisted() and the new handle_aprs_messsage_addressed_to_us() function
may be called concurrently in the main program thread and in the taskWebServer-thread.
I'm not really sure if buffer operation on the local variable is thread safe; that's
why I added sema locking for both. Needed? Not? I'd be glad to remove it ;)

handle_aprs_messsage_addressed_to_us() returns a String. I'm also not sure what
happens on concurrency -> I now return a copy of the String: String(answer_message).

The changelog of the last commit of today:
==========================================

New features
------------

- personal text messages addressed to the tracker
  - are now recognized and stay local -> will not be digipeated or sent to aprsis
     see handle_aprs_messsage_addressed_to_us(), used in the main program (for
     rf-received packets) and in taskWebServer for packets received from aprs-is
     It also displays the message
        +      writedisplaytext(" ((MSG))","",String(msg_from) + String(": ") + String(header_normal_or_third_party_end + 11+1),"","","");
     for 30s:
        +      display_dont_update_until = millis() + 30000L;
     We display the msg in favour of the RX packet.
  - we send an ACK when a message-ID is present
  - This is also useful for the winlink feature described below

- Winlink feature
  - if enabled, your device will add the word @winlink to the position beacon text
    every hour (Refresh interval at winlink is two hours).
    You can choose if it is sent always, or only if a bluetooth device (i.e. aprsdroid)
    is connected.
    Winlink documented that you could send a "status packet" instead. This would be
    really nice and I implemented it - but they ignore it. -> I've commented
    it our (for not loosing it), and switched to the position comment approach.
  - If we get a notification, a "W" is in the display as indicator that new messages
    are waiting for you.
    The W is displayed for 24h. Because winlink only informs every 24 hours, and the
    tracker does not know if you read the message at the winlink system

- Powerdown:
  - If you disconnect external power (USB) from the TTGO device, it can shutdown if
    you configured the shutdown delay. This is useful when operating in the car and
    you park. Someone configured a shutdown delay of 1h. -> Shutdown info is displayed
    a few seconds (you can interrupt the shutdown by pressing the middle button), and
    info is displayed again a few seconds right before the actual shutdown (also with
    the option do interrupt the shutdown by pressing the middle button).
  - On shutdown, a message QRT status message could be sent automatically, if configured

- LoRa APRS digipeating: you can configure now an alias reacting as digipeater
  (kenwood names it "UITRACE". We react only on this call if it's in the first
  position of the path and has no digipeated flag.

- Serial console tnc cli:
  - Show reboot and halt info also at the display if you issue the reboot or shutdown command
    And send a status packet if configured (see new status packet feature described above)

Bug fixes
---------
- Fix for AXP2101 on T-Beam V1.2:
  In setup() (setup_phase2_soft_reconfiguration()), we needed to add
    +        axp.setButtonBatteryChargeVoltage(3300);                             // enable charge of the gps battery
    +        axp.enableButtonBatteryCharge();
  else the button battery (which is a capacitor) never became loaded. This lead
  to a time of 10 min to get a GPS fix after powering on (because it needed to wait
  for the almanach)
- Fix and improve of smartbeaconing computation. Sometimes the timer went too fast down,
  sometimes it went up to max again -> we mostly relied on the course-change event.
  You will really notice the improvement.
- Wrong info if lora TX is enabled and current state of dont_send_own_position_packets
  is true. Display showed "LoRa-TX; dis", but only the active sending of the
  beacon is stopped:
    -  if (dont_send_own_position_packets || !(lora_tx_enabled || aprsis_enabled)) {
    +  if (dont_send_own_position_packets) {
    +    OledLine2 = wifi_info + " Own Bcn: dis";
    +  } else if (!(lora_tx_enabled || aprsis_enabled)) {
         OledLine2 = wifi_info + " LoRa-TX: dis";
- Beautifying the lines in the display output (indents for different speed units,
  missing blank if no "p" or "P", etc.
  I had developed that with the device with-display-and-no-GPS and saw the indent
  errors at the moment I used my new-device-with-display-and-GPS
- During debugging, I was not able to send a packet with digipeated flag via serial
  console converse mode: typo (q instead of p check):
    -          if (! (*p == '-' || *p == '>' || *p == ',' || *q == '*' || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')))
    +          if (! (*p == '-' || *p == '>' || *p == ',' || *p == '*' || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')))
  ;))
- Moved update_speed_from_gps() higher, before gps.speed() calls, because update_speed_from_gps()
  implicitly changes the gps.speed.isUpdated() value during his calls. This lead to unexpected fewer conditions of speed updates
- Improved gps location / speed / ... handling:
  Work on bestHdop section. You know, gps jumps at low speeds, and it's hard to find a good value
  between needed updates and wrong-positives. I left old values commented-out in the code. In my test, it now behaves much better.
  If situation is "p" (had a fix, but lost it), not only set aprsPresetShown "p" but also call displayInvalidGPS();
  If situation is " ": we had no gps fix, but we now have a fix:
           // No GPS signal for a long time? Enforce tx:
    +      if ((millis() - lastPositionTX) > ((sb_max_interval - sb_min_interval) / 2))
    +        nextTX = sb_min_interval;
  Sounds good?
- removed time_to_refresh = millis() + showRXTime from handle_usb_serial_input(), KISSTX, TX-Xdigi, ..
  because this value is only intended for the time of displaying ((RX)-packets and must not be
  overloaded by other stuff. See also new variable display_dont_update_until.
- taskWebServer send_queue_to_aprsis():
  While \r is a valid char in an aprs message text (for separating lines), it must not be
    sent to aprs-is, because they interpret \r, \n, \r\n as EOL. That means, the rest of the
    line will be interpreted and may lead to a disconnect due to garbled input.
    -> +  data.replace("\r", " "); data.replace("\n", " "); data.replace("\0", " ");
- is_call_blacklisted(): moved strcat after length check:
    -      // our ssid filter construct: -0 means search for call with ssid 0 zero.
    -      if (!(r = strchr(buf, '-')))
    -        strcat(buf, "-0");
           // after modifications above, is len(buf) still < 10 (space for ',' and \0)?
           if (strlen(buf) > 10)
             return 0;
    +      // our ssid filter construct: -0 means search for call with ssid 0 zero.
    +      if (!(r = strchr(buf, '-')))
    +        strcat(buf, "-0");
- Fixed: average_speed array: last field was always zero (%4 instead of %5)
    -  average_speed[point_avg_speed] = (((curr_hdop < 1.5 && curr_sats >= 3 && curr_kmph <= 16.0) || (curr_hdop < 4.0 && curr_sats >= 3 && curr_kmph > 16.0)) ? curr_kmph : average_speed[(point_avg_speed-1) % 4]);   // calculate smart beaconing. Even a not-updated old speed is ok here.
    +  average_speed[point_avg_speed] = ((curr_kmph < 1.8 || (curr_hdop < 1.5 && curr_sats >= 3 && curr_kmph <= 16.0) || (curr_hdop < 4.0 && curr_sats >= 3 && curr_kmph > 16.0)) ? curr_kmph : average_speed[(point_avg_speed-1) % 5]);   // calculate smart beaconing. Even a not-updated old speed is ok here.

average_speed_final = (average_speed[0]+average_speed[1]+average_speed[2]+average_speed[3]+average_speed[4])/5;

Other changes
-------------

- Instead of
    if (enable_bluetooth && SerialBT.hasClient()){
  we now have
    if (serial_bt_client_is_connected){
  in TTGO_T-Beam_LoRa_APRS.ino, taskWebServer.cpp and taskTNC.cpp
  and the current state is checked in the main loop.
  This way we also learn if a new serial_bt_client is connected
- Oled: new display_dont_update_until prevents display changes -> important messages
  are not interrupted
- Better space handling for beacon text additions like "@winlink" "Batt=", "P=", " !W..!"
- nextTX and next_fixed_beacon adjustments moved now in sendpacket(), because
  they need to be adjusted (and only then) after calling this function:
    +  lastPositionTX = millis();
    +  // reset timer for automatic fixed beacon after manual beacon
    +  next_fixed_beacon = millis() + fix_beacon_interval;
    +  nextTX = sb_max_interval;
- enableOled_now() enforces Oled to be on (without waiting for the next loop in main loop).
  display_dont_update_until is set to the needed values
- variable name lastTX changed to lastPositionTX because this variable is only needed
  in the context of the last position transmission. -> the name lastTX was misleading.
- is_call_blacklisted():
  whitelist call "WLNK", because it has to be valid for winlink communication
- at various places in the code:
  moved display routines like writedisplaytext("((KISSTX))","",inputBuf,"","",""); to the
  place before loraSend -> more time for the user to read the message
- Middle button code:
    - fillDisplayLines3to5(0) added to MAN TX / FIX TX part
    - handle the power-off-timer condition (-> timeslots for being able to abort the poweroff)
- variables t_last_smart_beacon_sent and lastPositionTX had the same meaning.
  -> Abandoned t_last_smart_beacon_sent.
- New tmp_t_since_last_sb_tx in the loop: avoid unnecessary recomputations of
  millis() - lastPositionTX at various places afterwards in the loop.
- taskWebserver:
  - On shutdown or reboot, send statusPacket if configured.
    And set a delay for do_send_status_message_about_shutdown_to_aprsis()
  - code beautify in handle_SaveAPRSCfg() and read_from_aprsis() (here: changed
    log message read_from_aprs to the more correct read_from_aprsis).
  - aprs-message ack-handling (described above)
  - renamed send_to_aprsis() to send_queue_to_aprsis(), because we have in  TTGO_T-Beam_LoRa_APRS.ino
    the same function (which only fills the queue, which is handled by send_queue_to_aprsis() in
    taskWebServer.cpp).
  - More comprehensible:
      -                        <label for="aprs_llfgps">Use lat/lon of current GPS position</label>
      +                        <label for="aprs_llfgps">Instead, store current GPS position</label>

Signed-off-by: Thomas Osterried <dl9sau@darc.de>
2024-02-24 21:34:10 +01:00
Thomas Osterried 08997f5720 Many new features. bug fixes and improvements
New features
------------

- personal text messages addressed to the tracker
  - are now recognized and stay local -> will not be digipeated or sent to aprsis
     see handle_aprs_messsage_addressed_to_us(), used in the main program (for
     rf-received packets) and in taskWebServer for packets received from aprs-is
     It also displays the message
        +      writedisplaytext(" ((MSG))","",String(msg_from) + String(": ") + String(header_normal_or_third_party_end + 11+1),"","","");
     for 30s:
        +      display_dont_update_until = millis() + 30000L;
     We display the msg in favour of the RX packet.
  - we send an ACK when a message-ID is present
  - This is also useful for the winlink feature described below

- Winlink feature
  - if enabled, your device will add the word @winlink to the position beacon text
    every hour (Refresh interval at winlink is two hours).
    You can choose if it is sent always, or only if a bluetooth device (i.e. aprsdroid)
    is connected.
    Winlink documented that you could send a "status packet" instead. This would be
    really nice and I implemented it - but they ignore it. -> I've commented
    it our (for not loosing it), and switched to the position comment approach.
  - If we get a notification, a "W" is in the display as indicator that new messages
    are waiting for you.
    The W is displayed for 24h. Because winlink only informs every 24 hours, and the
    tracker does not know if you read the message at the winlink system

- Powerdown:
  - If you disconnect external power (USB) from the TTGO device, it can shutdown if
    you configured the shutdown delay. This is useful when operating in the car and
    you park. Someone configured a shutdown delay of 1h. -> Shutdown info is displayed
    a few seconds (you can interrupt the shutdown by pressing the middle button), and
    info is displayed again a few seconds right before the actual shutdown (also with
    the option do interrupt the shutdown by pressing the middle button).
  - On shutdown, a message QRT status message could be sent automatically, if configured

- LoRa APRS digipeating: you can configure now an alias reacting as digipeater
  (kenwood names it "UITRACE". We react only on this call if it's in the first
  position of the path and has no digipeated flag.

- Serial console tnc cli:
  - Show reboot and halt info also at the display if you issue the reboot or shutdown command
    And send a status packet if configured (see new status packet feature described above)

Bug fixes
---------
- Fix for AXP2101 on T-Beam V1.2:
  In setup() (setup_phase2_soft_reconfiguration()), we needed to add
    +        axp.setButtonBatteryChargeVoltage(3300);                             // enable charge of the gps battery
    +        axp.enableButtonBatteryCharge();
  else the button battery (which is a capacitor) never became loaded. This lead
  to a time of 10 min to get a GPS fix after powering on (because it needed to wait
  for the almanach)
- Fix and improve of smartbeaconing computation. Sometimes the timer went too fast down,
  sometimes it went up to max again -> we mostly relied on the course-change event.
  You will really notice the improvement.
- Wrong info if lora TX is enabled and current state of dont_send_own_position_packets
  is true. Display showed "LoRa-TX; dis", but only the active sending of the
  beacon is stopped:
    -  if (dont_send_own_position_packets || !(lora_tx_enabled || aprsis_enabled)) {
    +  if (dont_send_own_position_packets) {
    +    OledLine2 = wifi_info + " Own Bcn: dis";
    +  } else if (!(lora_tx_enabled || aprsis_enabled)) {
         OledLine2 = wifi_info + " LoRa-TX: dis";
- Beautifying the lines in the display output (indents for different speed units,
  missing blank if no "p" or "P", etc.
  I had developed that with the device with-display-and-no-GPS and saw the indent
  errors at the moment I used my new-device-with-display-and-GPS
- During debugging, I was not able to send a packet with digipeated flag via serial
  console converse mode: typo (q instead of p check):
    -          if (! (*p == '-' || *p == '>' || *p == ',' || *q == '*' || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')))
    +          if (! (*p == '-' || *p == '>' || *p == ',' || *p == '*' || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')))
  ;))
- Moved update_speed_from_gps() higher, before gps.speed() calls, because update_speed_from_gps()
  implicitly changes the gps.speed.isUpdated() value during his calls. This lead to unexpected fewer conditions of speed updates
- Improved gps location / speed / ... handling:
  Work on bestHdop section. You know, gps jumps at low speeds, and it's hard to find a good value
  between needed updates and wrong-positives. I left old values commented-out in the code. In my test, it now behaves much better.
  If situation is "p" (had a fix, but lost it), not only set aprsPresetShown "p" but also call displayInvalidGPS();
  If situation is " ": we had no gps fix, but we now have a fix:
           // No GPS signal for a long time? Enforce tx:
    +      if ((millis() - lastPositionTX) > ((sb_max_interval - sb_min_interval) / 2))
    +        nextTX = sb_min_interval;
  Sounds good?
- removed time_to_refresh = millis() + showRXTime from handle_usb_serial_input(), KISSTX, TX-Xdigi, ..
  because this value is only intended for the time of displaying ((RX)-packets and must not be
  overloaded by other stuff. See also new variable display_dont_update_until.
- taskWebServer send_queue_to_aprsis():
  While \r is a valid char in an aprs message text (for separating lines), it must not be
    sent to aprs-is, because they interpret \r, \n, \r\n as EOL. That means, the rest of the
    line will be interpreted and may lead to a disconnect due to garbled input.
    -> +  data.replace("\r", " "); data.replace("\n", " "); data.replace("\0", " ");
- is_call_blacklisted(): moved strcat after length check:
    -      // our ssid filter construct: -0 means search for call with ssid 0 zero.
    -      if (!(r = strchr(buf, '-')))
    -        strcat(buf, "-0");
           // after modifications above, is len(buf) still < 10 (space for ',' and \0)?
           if (strlen(buf) > 10)
             return 0;
    +      // our ssid filter construct: -0 means search for call with ssid 0 zero.
    +      if (!(r = strchr(buf, '-')))
    +        strcat(buf, "-0");
- Fixed: average_speed array: last field was always zero (%4 instead of %5)
    -  average_speed[point_avg_speed] = (((curr_hdop < 1.5 && curr_sats >= 3 && curr_kmph <= 16.0) || (curr_hdop < 4.0 && curr_sats >= 3 && curr_kmph > 16.0)) ? curr_kmph : average_speed[(point_avg_speed-1) % 4]);   // calculate smart beaconing. Even a not-updated old speed is ok here.
    +  average_speed[point_avg_speed] = ((curr_kmph < 1.8 || (curr_hdop < 1.5 && curr_sats >= 3 && curr_kmph <= 16.0) || (curr_hdop < 4.0 && curr_sats >= 3 && curr_kmph > 16.0)) ? curr_kmph : average_speed[(point_avg_speed-1) % 5]);   // calculate smart beaconing. Even a not-updated old speed is ok here.

average_speed_final = (average_speed[0]+average_speed[1]+average_speed[2]+average_speed[3]+average_speed[4])/5;

Other changes
-------------

- Instead of
    if (enable_bluetooth && SerialBT.hasClient()){
  we now have
    if (serial_bt_client_is_connected){
  in TTGO_T-Beam_LoRa_APRS.ino, taskWebServer.cpp and taskTNC.cpp
  and the current state is checked in the main loop.
  This way we also learn if a new serial_bt_client is connected
- Oled: new display_dont_update_until prevents display changes -> important messages
  are not interrupted
- Better space handling for beacon text additions like "@winlink" "Batt=", "P=", " !W..!"
- nextTX and next_fixed_beacon adjustments moved now in sendpacket(), because
  they need to be adjusted (and only then) after calling this function:
    +  lastPositionTX = millis();
    +  // reset timer for automatic fixed beacon after manual beacon
    +  next_fixed_beacon = millis() + fix_beacon_interval;
    +  nextTX = sb_max_interval;
- enableOled_now() enforces Oled to be on (without waiting for the next loop in main loop).
  display_dont_update_until is set to the needed values
- variable name lastTX changed to lastPositionTX because this variable is only needed
  in the context of the last position transmission. -> the name lastTX was misleading.
- is_call_blacklisted():
  whitelist call "WLNK", because it has to be valid for winlink communication
- at various places in the code:
  moved display routines like writedisplaytext("((KISSTX))","",inputBuf,"","",""); to the
  place before loraSend -> more time for the user to read the message
- Middle button code:
    - fillDisplayLines3to5(0) added to MAN TX / FIX TX part
    - handle the power-off-timer condition (-> timeslots for being able to abort the poweroff)
- variables t_last_smart_beacon_sent and lastPositionTX had the same meaning.
  -> Abandoned t_last_smart_beacon_sent.
- New tmp_t_since_last_sb_tx in the loop: avoid unnecessary recomputations of
  millis() - lastPositionTX at various places afterwards in the loop.
- taskWebserver:
  - On shutdown or reboot, send statusPacket if configured.
    And set a delay for do_send_status_message_about_shutdown_to_aprsis()
  - code beautify in handle_SaveAPRSCfg() and read_from_aprsis() (here: changed
    log message read_from_aprs to the more correct read_from_aprsis).
  - aprs-message ack-handling (described above)
  - renamed send_to_aprsis() to send_queue_to_aprsis(), because we have in  TTGO_T-Beam_LoRa_APRS.ino
    the same function (which only fills the queue, which is handled by send_queue_to_aprsis() in
    taskWebServer.cpp).
  - More comprehensible:
      -                        <label for="aprs_llfgps">Use lat/lon of current GPS position</label>
      +                        <label for="aprs_llfgps">Instead, store current GPS position</label>

Signed-off-by: Thomas Osterried <dl9sau@darc.de>
2024-02-24 12:29:10 +01:00
5 zmienionych plików z 883 dodań i 283 usunięć

Wyświetl plik

@ -29,9 +29,9 @@
</select>
</div>
</div>
<div class="grid-container full">
<h5 class="u-full-width">AP settings</h5>
</div>
<div class="grid-container full">
<h5 class="u-full-width">AP settings</h5>
</div>
<div class="grid-container halves">
<div>
<label for="ap_password">AUTO AP Password</label>
@ -48,9 +48,9 @@
</select>
</div>
</div>
<div class="grid-container full">
<h5 class="u-full-width">STA settings</h5>
</div>
<div class="grid-container full">
<h5 class="u-full-width">STA settings</h5>
</div>
<div class="grid-container full">
<div id="wifi_list">
<label for="wifi_ssid">Network</label>
@ -202,7 +202,7 @@ You like to enable, if you use your tracker portable and it should automatically
<div class="grid-container quarters">
<div>
<label for="aprs_callsign">Callsign and SSID *</label>
<input class="u-full-width" type="text" minlength="3" maxlength="9" name="aprs_callsign" placeholder="NOCALL-1" id="aprs_callsign" title="your callsign with SSID. SSID is optional and should be in the range of -1 to -15". SSIDs like "-L4" are ok for aprs-is, but should not be sent on RF; thus LoRa TX will be disabled.">
<input class="u-full-width" type="text" minlength="3" maxlength="9" name="aprs_callsign" placeholder="NOCALL-1" id="aprs_callsign" title="your callsign with SSID. SSID is optional and should be in the range of -1 to -15. SSIDs like '-L4' are ok for aprs-is, but should not be sent on RF; thus LoRa TX will be disabled.">
</div>
<div>
<label for="aprs_relay_path">Relay Path</label>
@ -222,7 +222,7 @@ You like to enable, if you use your tracker portable and it should automatically
</div>
<div>
<label for="aprs_alt_r">Altitude ratio [%]</label>
<input name="aprs_alt_r" id="aprs_alt_r" type="number" min="0" max="100" title="Altitude ratio every n'th packet.. Use 100 for every packet, 0 for no altitude, or any number in between. Recommended: 10 (altitude every tenth's frame). If you use compression ('Send Course/Speed along with Altitude switched off') for reducing airtime (-> then n% altitude-encoding packets are sent, and 100-n% course/speed-encoded packets), you may choose 50 for ballons, where speed/dir and altitude are of equal interrest; if you choose 10, then 90 % of your transmissions encode speed/direction. Cave: if you set it to 100 and switch next option 'Always send CSE/SPD and ALT' to off, then only altitude packets are sen; if you switch the next option on, then altitude is always sent and this configured Altitude ratio is ignored. If you configure 0, altitude is never sent (-> only compressed speed). If ratio is < 100 and gps altitude is not refreshed, only encoded speed is sent." placeholder="10">
<input name="aprs_alt_r" id="aprs_alt_r" type="number" min="0" max="100" title="Altitude ratio every n'th packet.. Use 100 for every packet, 0 for no altitude, or any number in between. Recommended: 10 (altitude every tenth's beacon). If you use compression ('Send Course/Speed along with Altitude switched off') for reducing airtime (-> then n% altitude-encoding packets are sent, and 100-n% course/speed-encoded packets), you may choose 50 for ballons, where speed/dir and altitude are of equal interrest; if you choose 10, then 90 % of your transmissions encode speed/direction. Cave: if you set it to 100 and switch next option 'Always send CSE/SPD and ALT' to off, then only altitude packets are sen; if you switch the next option on, then altitude is always sent and this configured Altitude ratio is ignored. If you configure 0, altitude is never sent (-> only compressed speed). If ratio is < 100 and gps altitude is not refreshed, only encoded speed is sent." placeholder="10">
</div>
<div>
<label for="aprs_CSandA">Always send CSE/SPD and ALT</label>
@ -244,6 +244,18 @@ You like to enable, if you use your tracker portable and it should automatically
<label for="aprs_batt">Show Battery</label>
<input name="aprs_batt" id="aprs_batt" type="checkbox" value="1" title=" show battery voltage after personal comment">
</div>
<div>
<label for="aprs_stat_wl">Add winlink notification to position comment once/hour</label>
<select id="aprs_stat_wl" name="aprs_stat_wl">
<option value="0">off (default)</option>
<option value="1">on, if bluetooth kiss device is connected</option>
<option value="2">on</option>
</select>
</div>
<div>
<label for="aprs_stat_qrt">Send Status-packet on shutdown</label>
<input name="aprs_stat_qrt" id="aprs_stat_qrt" type="checkbox" value="1" title=" Send status packet 'QRT' right before shutdown on RF. See also option 'Auto power off' if external power is switched off">
</div>
<div>
*) requires reboot only if connection to aprs-is enabled
</div>
@ -289,7 +301,7 @@ You like to enable, if you use your tracker portable and it should automatically
<div class="grid-container thirds">
<div>
<label for="aprs_fixed_beac">Fixed Beacon</label>
<input name="aprs_fixed_beac" id="aprs_fixed_beac" type="checkbox" value="1" title="enable fixed beacon when GPS is disabled or no fix">
<input name="aprs_fixed_beac" id="aprs_fixed_beac" type="checkbox" value="1" title="enable fixed beacon when GPS is disabled or no fix". If set to off, your tracker will not send the configured fixed position (but only the current GPS position, after GPS-fix), except: if you maually send a position-beacon (using middle button, web-interface, cli), it starts sending in Fixed Beacon Interval (like Fixed Beacon would be enabled)>
</div>
<div>
<label for="aprs_fb_interv">Fixed Beacon Interval [s]</label>
@ -306,7 +318,7 @@ You like to enable, if you use your tracker portable and it should automatically
<input type="text" minlength="0" name="aprs_lon_p" id="aprs_lon_p" title="longtitude for fixed beacon, APRS format for example 02050.59Ei (this is ddd.mmE). 'ddd.nnnnE', '-ddd.nnnn', 'ddd mm[.nnn]' and 'ddd mm ss' and 'ddd mm' ss&quot;' and 'ddd-mm[.nn]E' are also supported. If position is west, you can prefix '-' instead of writing 'W' behind the coordinate. '-nnW' does not make sense.. If you like to add a locator, add it to the latitude field and leave this field blank. APRS spec defindes a special notation for null-position: 0000.00N 00000.00W (W, not E). If GPS is enabled and a valid position has been received, that one will be used instead of the configured one, until the next system reboot">
</div>
<div>
<label for="aprs_llfgps">Use lat/lon of current GPS position</label>
<label for="aprs_llfgps">Instead, store current GPS position</label>
<input name="aprs_llfgps" id="aprs_llfgps" type="checkbox" value="1" title="Use lat/lon of current GPS position. Overwrites Latitude / Longitude settings above. Requires valid GPS position.">
</div>
</div>
@ -444,6 +456,12 @@ III: Values above are referring to latitude; distance between two latitudes is a
</select>
</div>
</div>
<div class="grid-container full">
<div>
<label for="lora_myalias">My alias for digipeating (optional)</label>
<input class="u-full-width" type="text" minlength="0" maxlength="9" name="lora_myalias" id="lora_myalias" title="My alias for digipeting (empty recommended). Kenwood users know it as UITRACE. We repeat this alias, if found in first position of the path and if it is not already repeated. Don't set this to WIDEn-m. This option is only active in LoRa Repeater Mode >= 1">
</div>
</div>
<div class="grid-container halves">
<div>
<label for="lora_dig_x_m">Digipeat heard stations from MAIN frequency to which frequencies</label>
@ -577,17 +595,17 @@ III: Values above are referring to latitude; distance between two latitudes is a
</div>
<div>
<label for="shutdown_act">Auto power off</label>
<input name="shutdown_act" id="shutdown_act" type="checkbox" value="1" title="activate auto shutdown after usb plug off (not for T-BEAM 0.7). The power off the device will be switched off. Default: 'yes' (power off). Consider if you need to switch it to 'no' (keep power on), i.e. if you are an additionaly battery-connected igate, or if you have a not reliable power source.">
<input name="shutdown_act" id="shutdown_act" type="checkbox" value="1" title="activate auto shutdown after usb plug off (not for T-BEAM 0.7). The evice will powered down. You can interrupt this by pressing the middle button in time (right after pluging of or short before it powers down). Default: 'yes' (power off). Consider if you need to switch it to 'no' (keep power on), i.e. if you are an additionaly battery-connected igate, or if you have a not reliable power source.Please also look at the option 'Send Status-packet when shutdown' above.">
</div>
<div>
<label for="shutdown_dt">Auto power off delay [s]</label>
<input name="shutdown_dt" id="shutdown_dt" type="number" min="3" max="3600" title="auto shutdown delay in seconds. Range 3 to 3600.">
<input name="shutdown_dt" id="shutdown_dt" type="number" min="3" max="3600" title="auto shutdown delay in seconds. Range 3 to 3600. You can interrupt the timer by pressing the middle-button.">
</div>
</div>
<div class="grid-container thirds">
<div>
<label for="sh_rxtime">Display show RX time [s]</label>
<input name="sh_rxtime" id="sh_rxtime" type="number" min="0" max="45" title="show RX packet for seconds, refresh rate for OLED. Range 0 to 45.">
<label for="sh_rxtime">Seconds to show RX packets [s]</label>
<input name="sh_rxtime" id="sh_rxtime" type="number" min="0" max="45" title="show a received packet for seconds, refresh rate for OLED. Range 0 to 45.">
</div>
<div>
<label for="sh_oledtime">Display timeout [s]</label>
@ -627,7 +645,7 @@ See also the option beneath about showing maidenhead grid locator.">
<option value="4">C2: Lat/Lon in nautical notation, left side</option>
</select>
</div>
<div>
<div>
<label for="oledlocator">Maidenhead Locator</label>
<select id="oledlocator" name="oledlocator" title="Show Maidenhead grid locator: always (instead of Lat/Lon), never, alternating. Alternating:
0: Never
@ -848,8 +866,8 @@ Hard readable lengths / too long for our display:
</ul>
If OLED is disabled or OLED is ON:<br>
<ul>
<li>with GPS-FIX immediate sending of a frame with the position from the GPS</li>
<li>without GPS-FIX, immediate sending of the frame with the position saved in the configuration</li>
<li>with GPS-FIX immediate send a beacon with the position from the GPS</li>
<li>without GPS-FIX, immediate send a beacon with the position saved in the configuration</li>
</ul>
<h6 class="u-full-width">long press</h6>
<ul>
@ -882,7 +900,7 @@ Hard readable lengths / too long for our display:
</ul>
Fixed Beacon off:<br>
<ul>
<li>Smart Beaconing is activated, if there is no fixed beacon</li>
<li>Smart Beaconing is activated and GPS position is sent (if GPS fix is available). There is no fixed beacon. Beaconing starts after first GPS-fix (or if you manualy send a fixed beacon). If GPS-fix is lost, the last GPS position is sent in Fixed Beacon Interval.</li>
</ul>
</div>
<div class="grid-container full">

Wyświetl plik

@ -59,6 +59,8 @@ static const char *const PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET_INIT = "lora_cra
static const char *const PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET = "lora_cradapt";
static const char *const PREF_APRS_DIGIPEATING_MODE_PRESET_INIT = "lora_dig_mode_i";
static const char *const PREF_APRS_DIGIPEATING_MODE_PRESET = "lora_dig_mode";
static const char *const PREF_APRS_DIGIPEATING_MYALIAS_INIT = "lora_myalias_i";
static const char *const PREF_APRS_DIGIPEATING_MYALIAS = "lora_myalias";
static const char *const PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET_INIT = "lora_dig_x_m_i";
static const char *const PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET = "lora_dig_x_m";
@ -83,6 +85,10 @@ static const char *const PREF_APRS_ALWAYS_SEND_CSE_SPEED_AND_ALTITUDE_INIT = "ap
static const char *const PREF_APRS_SHOW_BATTERY = "aprs_batt";
static const char *const PREF_APRS_SHOW_BATTERY_INIT = "aprs_batt_init";
static const char *const PREF_APRS_STATUS_WINLINK_NOTIFICATION = "aprs_stat_wl";
static const char *const PREF_APRS_STATUS_WINLINK_NOTIFICATION_INIT = "aprs_stat_wl_i";
static const char *const PREF_APRS_STATUS_SHUTDOWN_NOTIFICATION = "aprs_stat_qrt";
static const char *const PREF_APRS_STATUS_SHUTDOWN_NOTIFICATION_INIT = "aprs_stat_qrt_i";
static const char *const PREF_APRS_LATITUDE_PRESET = "aprs_lat_p";
static const char *const PREF_APRS_LATITUDE_PRESET_INIT = "aprs_lat_p_init";
static const char *const PREF_APRS_LATLON_FROM_GPS = "aprs_llfgps"; // no _INIT needed. Checkbox is evaluated on "save" in the Web interface

Wyświetl plik

@ -2,11 +2,11 @@
#include <esp_task_wdt.h>
extern boolean enable_bluetooth;
extern uint8_t usb_serial_data_type;
#ifdef ENABLE_BLUETOOTH
BluetoothSerial SerialBT;
extern boolean serial_bt_client_is_connected;
#endif
QueueHandle_t tncReceivedQueue = nullptr;
@ -46,7 +46,7 @@ void handleKISSData(char character, int bufferIndex) {
Serial.print(inTNCData);
}
#ifdef ENABLE_BLUETOOTH
if (enable_bluetooth && SerialBT.hasClient()) {
if (serial_bt_client_is_connected) {
SerialBT.print(inTNCData);
}
#endif
@ -91,7 +91,7 @@ void handleKISSData(char character, int bufferIndex) {
}
}
#ifdef ENABLE_BLUETOOTH
if (enable_bluetooth && SerialBT.hasClient()) {
if (serial_bt_client_is_connected) {
while (SerialBT.available() > 0) {
char character = SerialBT.read();
handleKISSData(character, 1);
@ -114,7 +114,7 @@ void handleKISSData(char character, int bufferIndex) {
if (!usb_serial_data_type)
Serial.print(kissEncoded);
#ifdef ENABLE_BLUETOOTH
if (enable_bluetooth && SerialBT.hasClient()){
if (serial_bt_client_is_connected){
SerialBT.print(kissEncoded);
}
#endif

Wyświetl plik

@ -95,8 +95,6 @@ extern String OledLine3;
extern String OledLine4;
extern String OledLine5;
extern char src_call_blacklist;
#ifdef IF_SEMAS_WOULD_WORK
extern xSemaphoreHandle sema_lora_chip;
#else
@ -117,6 +115,8 @@ String aprsis_status = "Disconnected";
// aprsis 3rd party traffic encoding
String generate_third_party_packet(String, String);
void do_send_status_message_about_reboot_to_aprsis();
extern void sendStatusPacket(const String &);
extern String handle_aprs_messsage_addressed_to_us(const char *);
#if defined(T_BEAM_V1_0) || defined(T_BEAM_V1_2)
void do_send_status_message_about_shutdown_to_aprsis();
#endif
@ -144,6 +144,7 @@ IPAddress IP_GATEWAY_NULL(0,0,0,0);
// APRS-IS status message (about connection reset or our rboot)
extern boolean send_status_message_to_aprsis;
extern boolean send_status_message_about_shutdown_to_rf;
String aprsis_time_last_successful_connect = "";
int aprsis_connect_tries = 0;
extern char gps_time_s[];
@ -180,6 +181,8 @@ WiFiClient aprsis_client;
#endif
void send_queue_to_aprsis();
void sendCacheHeader() { server.sendHeader("Cache-Control", "max-age=3600"); }
void sendGzipHeader() { server.sendHeader("Content-Encoding", "gzip"); }
@ -422,7 +425,12 @@ void handle_Shutdown() {
#endif
Serial.println("WebServer: Shutdown Request -> Shutdown...");
server.send(200,"text/html", "Shutdown");
if (send_status_message_about_shutdown_to_rf) {
String msg = String("B") + buildnr + String(",up:") + String((int ) (millis()/1000/60)) + String(" qrt");
sendStatusPacket(msg);
}
do_send_status_message_about_shutdown_to_aprsis();
delay(500);
#ifdef T_BEAM_V1_0
axp.setChgLEDMode(AXP20X_LED_OFF);
#elif T_BEAM_V1_2
@ -476,6 +484,7 @@ void refill_preferences_as_jsonData()
s = s + "\n " + jsonLineFromPreferenceInt(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET);
s = s + "\n " + jsonLineFromPreferenceBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET);
s = s + "\n " + jsonLineFromPreferenceInt(PREF_APRS_DIGIPEATING_MODE_PRESET);
s = s + "\n " + jsonLineFromPreferenceString(PREF_APRS_DIGIPEATING_MYALIAS);
s = s + "\n " + jsonLineFromPreferenceInt(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET);
s = s + "\n " + jsonLineFromPreferenceInt(PREF_LORA_TX_BEACON_AND_KISS_TO_FREQUENCIES_PRESET);
s = s + "\n " + jsonLineFromPreferenceBool(PREF_LORA_TX_BEACON_AND_KISS_TO_APRSIS_PRESET);
@ -503,6 +512,8 @@ void refill_preferences_as_jsonData()
s = s + "\n " + jsonLineFromPreferenceInt(PREF_APRS_SB_TURN_SLOPE_PRESET);
s = s + "\n " + jsonLineFromPreferenceInt(PREF_APRS_SB_TURN_TIME_PRESET);
s = s + "\n " + jsonLineFromPreferenceBool(PREF_APRS_SHOW_BATTERY);
s = s + "\n " + jsonLineFromPreferenceInt(PREF_APRS_STATUS_WINLINK_NOTIFICATION);
s = s + "\n " + jsonLineFromPreferenceBool(PREF_APRS_STATUS_SHUTDOWN_NOTIFICATION);
s = s + "\n " + jsonLineFromPreferenceBool(PREF_APRS_FIXED_BEACON_PRESET);
//s = s + "\n " + jsonLineFromPreferenceBool(PREF_APRS_SHOW_ALTITUDE);
s = s + "\n " + jsonLineFromPreferenceInt(PREF_APRS_ALTITUDE_RATIO);
@ -950,6 +961,56 @@ void handle_SaveAPRSCfg() {
if (server.hasArg(PREF_APRS_DIGIPEATING_MODE_PRESET)){
preferences.putInt(PREF_APRS_DIGIPEATING_MODE_PRESET, server.arg(PREF_APRS_DIGIPEATING_MODE_PRESET).toInt());
}
if (server.hasArg(PREF_APRS_DIGIPEATING_MYALIAS)) {
String s = server.arg(PREF_APRS_DIGIPEATING_MYALIAS);
s.toUpperCase();
s.trim();
if (s.endsWith("-0"))
s.replace("-0", "");
if (!s.isEmpty()) {
uint8_t is_valid = 1;
const char *p;
const char *q;
p = s.c_str();
for (q = p; *q; q++) {
if (isalnum(*q) || *q == '-')
continue;
is_valid = 0;
break;
}
if (is_valid) {
is_valid = 0;
q = strchr(p, '-');
if (q) {
if (q > p && q-p <= 6 && strlen(q) > 1 && strlen(q) <= 3) {
q++;
if (q[0]) {
if (q[1]) {
if (q[0] == '1' && q[1] >= '0' && q[1] <= '5') {
is_valid = 1;
}
} else {
if (q[0] > '0' && q[0] <= '9') {
is_valid = 1;
}
}
}
}
} else {
if (*p && strlen(p) <= 6) {
is_valid = 1;
}
}
}
if (!is_valid) {
s = "";
}
}
preferences.putString(PREF_APRS_DIGIPEATING_MYALIAS, s);
}
if (server.hasArg(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET)){
preferences.putInt(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET, server.arg(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET).toInt());
}
@ -995,17 +1056,19 @@ void handle_SaveAPRSCfg() {
if (q) {
if (q > p && q-p <= 6 && strlen(q) > 1 && strlen(q) <= 3) {
q++;
if ((q[0] >= 'A' && q[0] <= 'Z') || (q[1] >= 'A' && q[1] <= 'Z')) {
// non-conformal SSIDs like "-L4" are ok for aprs-is, but should not be sent on RF -> disable TX
is_valid = 1;
} else {
if (q[1]) {
if (q[0] == '1' && q[1] >= '0' && q[1] <= '5') {
is_valid = 2;
}
if (q[0]) {
if ((q[0] >= 'A' && q[0] <= 'Z') || (q[1] >= 'A' && q[1] <= 'Z')) {
// non-conformal SSIDs like "-L4" are ok for aprs-is, but should not be sent on RF -> disable TX
is_valid = 1;
} else {
if (q[0] > '0' && q[0] <= '9') {
is_valid = 2;
if (q[1]) {
if (q[0] == '1' && q[1] >= '0' && q[1] <= '5') {
is_valid = 2;
}
} else {
if (q[0] > '0' && q[0] <= '9') {
is_valid = 2;
}
}
}
}
@ -1152,6 +1215,8 @@ void handle_SaveAPRSCfg() {
}
preferences.putBool(PREF_APRS_SHOW_BATTERY, server.hasArg(PREF_APRS_SHOW_BATTERY));
preferences.putInt(PREF_APRS_STATUS_WINLINK_NOTIFICATION, server.arg(PREF_APRS_STATUS_WINLINK_NOTIFICATION).toInt());
preferences.putBool(PREF_APRS_STATUS_SHUTDOWN_NOTIFICATION, server.hasArg(PREF_APRS_STATUS_SHUTDOWN_NOTIFICATION));
preferences.putBool(PREF_ENABLE_TNC_SELF_TELEMETRY, server.hasArg(PREF_ENABLE_TNC_SELF_TELEMETRY));
//preferences.putBool(PREF_APRS_SHOW_ALTITUDE, server.hasArg(PREF_APRS_SHOW_ALTITUDE));
if (server.hasArg(PREF_APRS_ALTITUDE_RATIO)){
@ -1292,21 +1357,21 @@ void restart_AP_or_STA(void) {
used_wifi_ModeSTA_PASS = String(APs[pos].pw);
static uint32_t last_write = 0L;
boolean changed = false;
boolean changed = false;
// store last successfull association into flash (preferences); ratelimit writing to flash
if (!last_write || millis() > last_write + 5*60*1000L) {
if (used_wifi_ModeSTA_PASS != preferences.getString(PREF_WIFI_PASSWORD, "")) {
preferences.putString(PREF_WIFI_PASSWORD, used_wifi_ModeSTA_PASS);
changed = true;
changed = true;
last_write = millis();
}
if (used_wifi_ModeSTA_SSID != preferences.getString(PREF_WIFI_SSID, "")) {
preferences.putString(PREF_WIFI_SSID, used_wifi_ModeSTA_SSID);
changed = true;
changed = true;
last_write = millis();
}
#if defined(ENABLE_SYSLOG)
if (changed && debug_verbose)
if (changed && debug_verbose)
syslog_log(LOG_DEBUG, String("FlashWrite preferences: restart_AP_or_STA()"));
#endif
}
@ -1325,8 +1390,8 @@ void restart_AP_or_STA(void) {
if (wifi_do_fallback_to_mode_AP || (!mode_sta_once_successfully_connected && millis() < +3*60*1000L)) {
start_soft_ap = true;
} else {
// if not fallback to mode ap, we are finished here. wifi_connection_status is still WIFI_SEARCHING_FOR_AP
return;
// if not fallback to mode ap, we are finished here. wifi_connection_status is still WIFI_SEARCHING_FOR_AP
return;
}
}
@ -1812,9 +1877,9 @@ void read_from_aprsis(void) {
}
if (!err) {
for (q = s.c_str(); *q && *q != ':'; q++) {
// q (for qAR) is also a valid character
// q (for qAR) is also a valid character
//if (! ( (*q >= '0' && *q <= '9') || (*q >= 'A' && *q <= 'Z') || *q == 'q' || *q == '>' || *q == '-' || *q == ',' || *q == '*' ) ) {
//No, unfortunately some aprs-submitters have a lowercase call
//No, unfortunately some aprs-submitters have a lowercase call
if (! ( isalnum(*q) || *q == '>' || *q == '-' || *q == ',' || *q == '*' ) ) {
err = 1;
log_msg = "bad character in header";
@ -1835,7 +1900,7 @@ void read_from_aprsis(void) {
aprsis_client.stop();
log_msg = "disconnecting: ";
}
log_msg = "APRS-IS: read_from_aprs(): " + log_msg + ": '" + s + "'";
log_msg = "APRS-IS: read_from_aprsis(): " + log_msg + ": '" + s + "'";
#if defined(ENABLE_SYSLOG)
syslog_log(LOG_INFO, log_msg);
#endif
@ -1848,7 +1913,7 @@ void read_from_aprsis(void) {
// Needs to be resolved. This is a the&D fix:
// search for ':' in String s again
if (!header_end && !(header_end = strchr(s.c_str(), ':'))) {
log_msg = "APRS-IS: read_from_aprs(): BUG! header_end is NULL: '" + s + "'";
log_msg = "APRS-IS: read_from_aprsis(): BUG! header_end is NULL: '" + s + "'";
#if defined(ENABLE_SYSLOG)
syslog_log(LOG_INFO, log_msg);
#endif
@ -1856,7 +1921,6 @@ void read_from_aprsis(void) {
return;
}
// packet has our call in i.E. ...,qAR,OURCALL:...
q = strstr(s.c_str(), (',' + aprsis_callsign + ':').c_str());
if (q && q < header_end) return;
@ -1881,6 +1945,14 @@ void read_from_aprsis(void) {
if (is_call_blacklisted(s.c_str()))
return;
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") {
// It was a message for us. It contained an answer message we have to send;
// or a Placeholder "M" if no ack message is required -- in this case, clear the message
answer_message = "";
}
// generate third party packet. Use aprs_callsign (deriving from webServerCfg->callsign), because aprsis_callsign may have a non-aprs (but only aprsis-compatible) ssid like '-L4'
String third_party_packet = generate_third_party_packet(aprs_callsign, s);
if (!third_party_packet.isEmpty()) {
@ -1893,6 +1965,17 @@ void read_from_aprsis(void) {
if (!src_call_not_for_rf)
sendToTNC(third_party_packet);
#endif
if (its_an_aprs_message_for_us) {
if (!answer_message.isEmpty()) {
to_aprsis_data = String(answer_message);
send_queue_to_aprsis();
if (usb_serial_data_type & 2)
Serial.println(third_party_packet);
}
return; // Message was for us -> do not TX on RF
}
if (lora_tx_enabled && aprsis_data_allow_inet_to_rf && !src_call_not_for_rf) {
// not query or aprs-message addressed to our call (check both, aprs_callsign and aprsis_callsign)
// Format: "..::DL9SAU-15:..."
@ -1947,7 +2030,7 @@ void read_from_aprsis(void) {
// forward packets to APRSIS
void send_to_aprsis()
void send_queue_to_aprsis()
{
if (!aprsis_client.connected() || !to_aprsis_data || !to_aprsis_data.length())
return;
@ -1956,6 +2039,8 @@ void send_to_aprsis()
String data = String(to_aprsis_data);
// clear queue
to_aprsis_data = "";
// Avoid breaking aprs-is protocol with frames lile "FOO>APRS:>helo\rworld"
data.replace("\r", " "); data.replace("\n", " "); data.replace("\0", " ");
data.trim();
char *p = strchr(data.c_str(), '>');
char *header_end;
@ -2229,6 +2314,7 @@ void send_to_aprsis()
// main loop
while (true) {
esp_task_wdt_reset();
if (apcnt && millis() - webserver_started > 60*1000L &&
@ -2313,7 +2399,7 @@ void send_to_aprsis()
log_msg = String("APRS-IS: on_Err: '") + aprsis_status + String("' [") + aprsis_client.remoteIP().toString() + String("], tries ") + String(aprsis_connect_tries) + String(", connect_to_aprsis() returned ") + String(ret);
aprsis_client.stop();
if (!enable_bluetooth)
WiFi.setSleep(true);
WiFi.setSleep(true);
// Known problems which usually resolve by reboot:
if (ret == -5 /* login denied, until reboot. Reason unknown */ ||
(ret == -1 /* sometimes after boot it can't connect. DNS- or IP-stack Problem? */ && lora_digipeating_mode > 1) /* we are a digi */ ) {
@ -2348,7 +2434,7 @@ void send_to_aprsis()
}
}
if (log_msg.length()) {
if (log_msg.length()) {
#if defined(ENABLE_SYSLOG)
syslog_log(LOG_INFO, log_msg);
#endif
@ -2356,7 +2442,7 @@ void send_to_aprsis()
}
if (!aprsis_status.startsWith("Error: "))
aprsis_status = "Disconnected";
}
}
}
// session died during read / write?
@ -2365,10 +2451,10 @@ void send_to_aprsis()
read_from_aprsis();
}
// session died during read / write? - log^
// session died during read / write? - log
if (aprsis_client.connected()) {
// forward packets to APRSIS
send_to_aprsis();
send_queue_to_aprsis();
}
// anti-idle timer for the tcp session
@ -2380,7 +2466,7 @@ void send_to_aprsis()
} else {
if (!enable_bluetooth)
WiFi.setSleep(true);
WiFi.setSleep(true);
if (aprsis_status != "Error: no internet") {
aprsis_status = "Error: no internet";