Compare commits

...

20 Commits

Author SHA1 Message Date
US1GHQ e6576de2ab Revert "Upload LIB"
This reverts commit 74442ebf12.
2022-02-22 13:27:11 +02:00
US1GHQ 74442ebf12 Upload LIB 2022-02-21 19:23:36 +02:00
US1GHQ 2a46d4d438 Reduce SPI speed for more stability 1W Modules (TEST) 2022-02-05 20:20:12 +02:00
US1GHQ 04a705520e More chnges from DL9SAU
Changes to the web interface

Web index.html:
- Input field "APRS-IS status" and "Current RX Frequency" are now
"readonly".
- These fields are not intended for input. They show status. User now
sees that he cannot enter anything.
- Upon request, frequency resolution is now 100 Hz. This was the only
point for implementing this feature.
But have in mind that this equpment is not such frequency stable at
all.

Web index.html and taskWebserver.cpp:
- New function jsonLineFromDouble now displays lora_freq_rx_curr with
decimal point (instead of my previous hack int(*= 1000))

aprs-is login:

aprs-is servers answers on login with i.e.
  # logresp DL9SAU-T1 unverified, server T2EISBERG

This answer was not intuitive and I checked the word " verified" for
successful login.
In other aprs-is client software, I read they parsed "logresp" as answer
from aprs-is servers.

=> My change

+           if (s.indexOf(" logresp") == -1) { aprsis_status = "Error:
Login denied: " + s; aprsis_status.trim(); goto on_err; }
+           if (s.indexOf(" verified") == -1) { aprsis_status = "Notice:
server responsed not verified: " + s; aprsis_status.trim();

An other response of a server is:
  user DL9SAU-TEST pass -1 scripttest 0.1
  # Invalid username format
We'll send Login denied here, with that message-
[Reason for deny is: len(SSID) is max 2 by definition of the
aprs-is-authors.]

If password is wrong, we stay connected.
Notice message in aprsis_staus shows in the web-interface what went
wrong.
2022-02-04 23:25:44 +02:00
US1GHQ 05abffafe3 BBS support via KISS 2022-01-27 21:11:23 +02:00
US1GHQ 0cae1f75cf Some changes 2022-01-26 22:30:20 +02:00
US1GHQ 0f3df6df71 Some changes 2022-01-24 21:37:52 +02:00
US1GHQ 6f02517eff Disable digi by default 2022-01-24 20:45:15 +02:00
US1GHQ 0ad9aed5d5 Some fixes 2022-01-24 20:43:41 +02:00
US1GHQ 24719c759f Remove code for calculate RSSI and SNR, because we using newest lib of RadioHead 2022-01-24 20:32:20 +02:00
US1GHQ 3fc013ea64 Fix TX PIN for PA LNA 2022-01-24 20:19:52 +02:00
US1GHQ b8a2c5b5d6 Fix ESP32 Working 2022-01-23 17:47:29 +02:00
US1GHQ c0275e1946 Correct BATT READ on the TTGO LoRa v2.1.6 2022-01-21 23:40:56 +02:00
US1GHQ f964289f6d Almost Done 2022-01-21 23:30:05 +02:00
US1GHQ ce8b02cabd Disable SNR Report to KISS 2022-01-21 23:03:55 +02:00
US1GHQ 5fdc8b10bb Fix KISS LIB 2022-01-21 22:04:15 +02:00
US1GHQ 3ae7e9ab89 Changes 2022-01-20 22:12:47 +02:00
US1GHQ 713614ec11 Forgot for ESP32-DEV-V1 2022-01-20 21:10:56 +02:00
US1GHQ b558cc1243 Start add packet mode and other feauters from previous version 2022-01-20 21:08:34 +02:00
US1GHQ d1e51d8039 DIGI_IGATE feauters from dl9sau 2022-01-20 20:47:52 +02:00
28 changed files with 2680 additions and 505 deletions

View File

@ -4,7 +4,7 @@ Tracker can be used on its own.
You can also connect it via blueetooth with APRSdroid.
After connection with APRX based DIGI it can be used as KISS-TNC
![diagram](http://git.mis.ks.ua/US1GHQ/TTGO-T-Beam-LoRa-APRS/raw/branch/lora-esp32-dev/img/digi-schemat.png)
![diagram](https://github.com/SQ9MDD/TTGO-T-Beam-LoRa-APRS/blob/master/img/digi-schemat.png)
## Contributors
* Initial work: OE1ACM, OE3CJB

View File

@ -44,22 +44,18 @@
</article>
</section>
<section>
<article>
<div class="grid-container full">
<h2 class="u-full-width">APRS Settings</h2>
</div>
<article>
<form action="/save_aprs_cfg" method="post">
<div class="grid-container full">
<h5 class="u-full-width">Transmission Settings</h5>
</div>
<div class="grid-container 3 columns">
<div class="grid-container halves">
<div>
<label for="lora_freq">Frequency [MHz]</label>
<input name="lora_freq" id="lora_freq" type="number" min="144.000" max="928.000" step="0.001" title="LoRa center frequency between 433.001 and 928.000">
</div>
<div>
<label for="txPower">dBm</label>
<input name="txPower" id="txPower" type="number" min="0" max="23" title="LoRa TX Power. Range 0 to 23dBm">
<label for="lora_freq">Main Frequency [MHz]</label>
<input name="lora_freq" id="lora_freq" type="number" min="144.0000" max="928.0000" step="0.0001" title="LoRa center frequency between 144.0001 and 928.0000. I.e. 433.775">
</div>
<div>
<label for="lora_speed">Speed</label>
@ -71,11 +67,42 @@
<option value="210">BW 125khz CR 4:7 SF 12 (209bps)</option>
<option value="180">BW 125khz CR 4:8 SF 12 (183bps)</option>
<option value="610">BW 125khz CR 4:8 SF 10 (610bps)</option>
<option value="1200">BW 125khz CR 4:5 SF 9 (PL,UA, 1200bps)</option>
<option value="1200">BW 125khz CR 4:5 SF 9 (1200bps)</option>
<option value="6000">BW 125khz CR 4:5 SF 7 (BBS, 6000bps)</option>
<option value="21000">BW 500khz CR 4:5 SF 7 (BBS, 21000bps)</option>
</select>
</div>
<div>
<label for="lora_tx_en">Enable LoRa transmitter</label>
<input name="lora_tx_en" id="lora_tx_en" type="checkbox" value="1" title="Allow TX on LoRa. Disable this if you like to prevent TX under all circumstances (i.e. if your tracker is behind an rx-amplifier)">
</div>
<div>
<label for="txPower">TX power [dBm]</label>
<input name="txPower" id="txPower" type="number" min="0" max="23" title="LoRa TX Power. Range 0 to 23dBm">
</div>
<div>
<label for="lora_cradapt">Automatic CodeRate adaption on TX</label>
<input name="lora_cradapt" id="lora_cradapt" type="checkbox" value="1" title="Enable automatic CR adaption. Use this only if you are not a WIDE1 or WIDE2 digi. Still testing, if it behaves good to our network. Currently works only for SF12 modes, because for SF 10 and lower the code for programming CR+SF+BW-combination has not been written yet">
</div>
</div>
<div class="grid-container halves">
<div>
<label for="lora_rssi2p">Add SNR and RSSI to path</label>
<select id="lora_rssi2p" name="lora_rssi2p">
<option value="0">off</option>
<option value="1">To RF during digipeating</option>
<option value="2">To KISS</option>
<option value="4">To APRSIS</option>
<option value="3">To RF and KISS</option>
<option value="5">To RF and APRSIS</option>
<option value="6">To KISS and APRSIS</option>
<option value="7">To RF and KISS and APRSIS</option>
</select>
</div>
<div>
<label for="snraprsis">SNR/RSSI-encoding on kiss: compatible to APRS-IS?</label>
<input name="snraprsis" id="snraprsis" type="checkbox" value=1 title="Add snr+rssi at last digipeater, without digipeated flag. Use this for APRSIS connections. If you have connected a digipeater software to kiss, this option has to be switched off">
</div>
</div>
<div class="grid-container full">
<h5 class="u-full-width">Station Settings</h5>
@ -83,11 +110,11 @@
<div class="grid-container quarters">
<div>
<label for="aprs_callsign">Callsign and SSID</label>
<input class="u-full-width" type="text" minlength="3" name="aprs_callsign" placeholder="NOCALL-1" id="aprs_callsign" title="your callsign with SSID">
<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">
</div>
<div>
<label for="aprs_relay_path">Relay Path</label>
<input class="u-full-width" type="text" minlength="0" name="aprs_relay_path" id="aprs_relay_path" title="APRS path, use the shortest as possible, ECHO or WIDE1-1">
<input class="u-full-width" type="text" minlength="0" name="aprs_relay_path" id="aprs_relay_path" title="APRS path, use the shortest as possible, ECHO or WIDE1-1. Only value '1' enables dest-path-digi-notation (like DL9SAU>APRS-1:..) instead of Path ..,WIDE1-1:..">
</div>
<div>
<label for="aprs_s_table">Symbol Table</label>
@ -109,6 +136,10 @@
<label for="aprs_comment">Comment</label>
<input class="u-full-width" type="text" minlength="0" maxlength="64" name="aprs_comment" id="aprs_comment" title=" personal comment">
</div>
<div>
<label for="aprs_comm_rt">Ratelimit adding comment text</label>
<input name="aprs_comm_rt" id="aprs_comm_rt" type="checkbox" value="1" title="If enabled, comment text is sent only every tenth's transmission. Saves airtime.">
</div>
<div>
<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">
@ -165,23 +196,35 @@
<div class="grid-container quarters">
<div>
<label for="sb_min_interv">Min interval [s]</label>
<input name="sb_min_interv" id="sb_min_interv" size="10" type="number" min="10" title="Minimal time for Smart Beaconing">
<input name="sb_min_interv" id="sb_min_interv" type="number" min="10" title="Minimal time for Smart Beaconing">
</div>
<div>
<label for="sb_max_interv">Max interval [s]</label>
<input name="sb_max_interv" id="sb_max_interv" size="10" type="number" min="120" title="Maximal time for Smart Beaconing">
<input name="sb_max_interv" id="sb_max_interv" type="number" min="120" title="Maximal time for Smart Beaconing">
</div>
</div>
<div class="grid-container quarters">
<div>
<label for="sb_min_speed">Min speed [km/h]</label>
<input name="sb_min_speed" id="sb_min_speed" size="10" type="number" min="0" title="Minimal speed for Smart Beaconing">
<input name="sb_min_speed" id="sb_min_speed" type="number" min="0" title="Minimal speed for Smart Beaconing">
</div>
<div>
<label for="sb_max_speed">Max speed [km/h]</label>
<input name="sb_max_speed" id="sb_max_speed" size="10" type="number" min="1" title="Maximal speed for Smart Beaconing">
<input name="sb_max_speed" id="sb_max_speed" type="number" min="1" title="Maximal speed for Smart Beaconing">
</div>
</div>
<div class="grid-container quarters">
<div>
<label for="sb_angle">Course change [degrees]</label>
<input name="sb_angle" id="sb_angle" size="10" type="number" min="1" max="360" title="Angle of course change to speed up beacon transmission">
<input name="sb_angle" id="sb_angle" type="number" min="5" max="360" title="Angle of course change to speed up beacon transmission. Recommended value: 28">
</div>
<div>
<label for="sb_turn_slope">Turn Slope [degrees]</label>
<input name="sb_turn_slope" id="sb_turn_slope" type="number" min="5" max="255" title="Smart beaconing turn slope. Use 7 for pedestrian, 11 for low/mid speed and 26 for high speed. Recommended value: 26">
</div>
<div>
<label for="sb_turn_time">Turn Time [seconds]</label>
<input name="sb_turn_time" id="sb_turn_time" type="number" min="5", title="Smart beaconing turn time. Wait at least this amount of seconds for next beacon. Recommended value: 30">
</div>
</div>
<div class="grid-container quarters">
@ -189,6 +232,131 @@
<label for="gps_enabled">GPS enabled</label>
<input name="gps_enabled" id="gps_enabled" type="checkbox" value="1" title="enable or disable GPS">
</div>
<div>
<label for="kiss_myloc_ok">Accept own positions via KISS</label>
<input name="kiss_myloc_ok" id="kiss_myloc_ok" type="checkbox" value="1" title="If set to true, we'll stop sending own position beacons if we heard a position from our call via kiss. Uncheck this, if you like to filter out those positions and keep sending it by this device.">
</div>
<div>
<label for="gps_sleep_ok">Allow GPS sleep while own positions received via KISS</label>
<input name="gps_sleep_ok" id="gps_sleep_ok" type="checkbox" value="1" title="If we have a kiss client like aprsdroid, or a digipeater software, which sends own positions (with same call as ours), we pause sending own positions (neither fixed nor smart beaconing). Uncheck, if you have a display attached and still like to see your current GPS position. This option is only honored if configuration 'Accept own positions via KISS' enabled. Why? If your filter out own positions from and this device sends on it's own, it needs gps running ;)">
</div>
</div>
<div class="grid-container full">
<h6 class="u-full-width">Additional settings for secondary frequency:<br/>EXPERIMANTAL - USE WITH CARE!</h6>
</div>
<div class="grid-container halves">
<div>
<label for="lora_freq_x">Secondary Frequency [MHz]</label>
(for cross-repeating or listening transmissions from igates)</label>
<input name="lora_freq_x" id="lora_freq_x" type="number" min="0.0000" max="928.0000" step="0.0001" title="LoRa center frequency - must be different from main frequency and between 433.0001 and 928.0000 or 0 if not needed. I.e. 433.900">
</div>
<div>
<label for="txPower_x">TX power on secondary frequency [dBm]</label>
<input name="txPower_x" id="txPower_x" type="number" min="0" max="23" title="LoRa TX Power on secondary frequency. Range 0 to 23dBm">
</div>
</div>
<div class="grid-container halves">
<div>
<label for="lora_speed_x">Speed on secondary frequency</label>
<select id="lora_speed_x" name="lora_speed_x">
<option value="1200">BW 125khz CR 4:7 SF 9 (Fast Standard. default; use high speeds on the secondary frequency)</option>
<option value="439">BW 31.25khz CR 4:5 SF 9 (300bps)</option>
<option value="1367">BW 31.25khz CR 4:5 SF 7 (1200bps)</option>
<option value="300">BW 125khz CR 4:5 SF 12 (default, 300bps)</option>
<option value="240">BW 125khz CR 4:6 SF 12 (244bps)</option>
<option value="210">BW 125khz CR 4:7 SF 12 (209bps)</option>
<option value="180">BW 125khz CR 4:8 SF 12 (183bps)</option>
<option value="610">BW 125khz CR 4:8 SF 10 (610bps)</option>
<option value="6000">BW 125khz CR 4:5 SF 7 (BBS, 6000bps)</option>
<option value="21000">BW 500khz CR 4:5 SF 7 (BBS, 21000bps)</option>
</select>
</div>
</div>
<div class="grid-container halves">
<div>
<label for="rx_qrg">RX on frequencies</label>
(Only honored, if we are NOT configured as WIDE1 or WIDE2 digi. If set to both frequencies: Ratio depends on how many packets are received on each qrg in a 10min window; we stay 20s on one qrg.)
<select id="rx_qrg" name="rx_qrg">
<option value="1">RX on main frequency</option>
<option value="2">RX on secondary frequency</option>
<option value="3">RX on both frequencies</option>
</select>
</div>
<div>
<label for="lora_freq_rx_curr">Current RX Frequency:</label>
<input name="lora_freq_rx_curr" id="lora_freq_rx_curr" type="number" readonly title="Nothing to enter here"</input>
</div>
</div>
<div class="grid-container full">
<h6 class="u-full-width">Additional settings for mode repater:<br/>EXPERIMANTAL - USE WITH CARE!</h6>
</div>
<div class="grid-container halves">
<div>
<label for="lora_dig_mode">LoRa Repater Mode</label>
<select id="lora_dig_mode" name="lora_dig_mode">
<option value="0">off. This device does not do any repeating decision.</option>
<option value="1">Repeat if own call is addressed (recommended for normal users). default.</option>
<option value="2">Act as WIDE1 (fill-in) digi</option>
<option value="3">Act as a simple WIDE2 digi (consider to instead attach a real aprs-digipeater software via KISS)</option>
</select>
</div>
</div>
<div class="grid-container halves">
<div>
<label for="lora_dig_x_m">Digipeat heard stations on which frequencies</label>
(If LoRa Repeater Mode has not been set to off)
<select id="lora_dig_x_m" name="lora_dig_x_m">
<option value="0">Repeat only to main frequency (default)</option>
<option value="1">Repeat to both frequencies</option>
<option value="2">Repeat only to cross-digi frequency</option>
</select>
</div>
<div>
<label for="tx_qrg_bc">TX beacon or from-kiss on frequencies</label>
(Only honored, if we are configured as WIDE1 or WIDE2 digi)
<select id="tx_qrg_bc" name="tx_qrg_bc">
<option value="1">TX on main frequency</option>
<option value="2">TX on secondary frequency</option>
<option value="3">TX on both frequencies</option>
</select>
</div>
</div>
<div class="grid-container full">
<h5 class="u-full-width">APRS-IS settings</h5>
</div>
<div class="grid-container halves">
<div>
<label for="aprsis_en">Enable APRS-IS connection</label>
<input name="aprsis_en" id="aprsis_en" type="checkbox" value="1" title="If we are configured as WIFI client, connet to the APRS-IS net">
</div>
<div>
<label for="aprsis_srv_h">Server Name</label>
<input type="text" name="aprsis_srv_h" id="aprsis_srv_h" title="Server name or IP Address. I.e. euro.aprs2.net">
</div>
<div>
<label for="aprsis_srv_p">TCP Port</label>
<input type="number" name="aprsis_srv_p" id="aprsis_srv_p" min="1" max="65535" title="TCP Port, i.e. 14580">
</div>
<div>
<label for="aprsis_fltr">Filter (optional)</label>
<input type="text" name="aprsis_fltr" id="aprsis_fltr" title="Request server-site filter (may be left empty). See http://www.aprs-is.net/javAPRSFilter.aspx" placeholder="may be left blank">
</div>
<div>
<label for="aprsis_call">Callsign (optional)</label>
<input type="text" name="aprsis_call" id="aprsis_call" minlength="3" maxlength="9" title="Use this callsign for the APRS-IS connection. If not configured, your default callsign is used." placeholder="may be left blank">
</div>
<div>
<label for="aprsis_pw">Password (required)</label>
<input type="password" name="aprsis_pw" id="aprsis_pw" title="Your password for the APRS-IS connection.">
</div>
<div>
<label for="aprsis_2rf">Allow traffic from inet to rf</label>
<input name="aprsis_2rf" id="aprsis_2rf" type="checkbox" value="1" title="Gate APRS-IS traffic to rf (will be encoded in 3rd-party format)">
</div>
<div>
<label for"aprsis_status">Connection status</label>
<input type="text" name="aprsis_status" id="aprsis_status" readonly title="Connection status. Nothing to enter here">
</div>
</div>
<div class="grid-container full">
<div>
@ -311,11 +479,14 @@
</div>
</form>
</div>
<div class="grid-container full">
Click <a href="/cfg">here</a> to view your configuration.
</div>
</article>
</section>
</div>
<footer>
<center><b>Contributors in order of appearance:</b><br> OE1ACM, OE3CJB, SQ9MDD, SQ5RWU, DJ1AN, M0IGA, SQ5WPR, DO2JMG, SP6VWX, SQ2WB, IU2FRL, DO3BOX, US1GHQ</center>
<center><b>Contributors in order of appearance:</b><br> OE1ACM, OE3CJB, SQ9MDD, SQ5RWU, DJ1AN, M0IGA, SQ5WPR, DO2JMG, SP6VWX, SQ2WB, IU2FRL, DO3BOX, DL9SAU</center>
<center><b>Latest stable version:</b> <a href=https://github.com/SQ9MDD/TTGO-T-Beam-LoRa-APRS>https://github.com/SQ9MDD/TTGO-T-Beam-LoRa-APRS</a></center>
<center><b>Licensed under:</b> CC BY-NC-SA</center>
<center><!--VERSION--></center>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 148 KiB

View File

@ -10,13 +10,37 @@ extern Preferences preferences;
static const char *const PREF_WIFI_SSID = "wifi_ssid";
static const char *const PREF_WIFI_PASSWORD = "wifi_password";
static const char *const PREF_AP_PASSWORD = "ap_password";
// LoRa settings
static const char *const PREF_LORA_FREQ_PRESET_INIT = "lora_freq_i";
static const char *const PREF_LORA_FREQ_PRESET = "lora_freq";
static const char *const PREF_LORA_SPEED_PRESET_INIT = "lora_speed_i";
static const char *const PREF_LORA_SPEED_PRESET = "lora_speed";
static const char *const PREF_LORA_TX_ENABLE_INIT = "lora_tx_en_i";
static const char *const PREF_LORA_TX_ENABLE = "lora_tx_en";
static const char *const PREF_LORA_TX_POWER_INIT = "txPower_i";
static const char *const PREF_LORA_TX_POWER = "txPower";
static const char *const PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET_INIT = "lora_rssi2p_i";
static const char *const PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET = "lora_rssi2p";
static const char *const PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET_INIT = "snraprsis_i";
static const char *const PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET = "snraprsis";
static const char *const PREF_LORA_FREQ_CROSSDIGI_PRESET_INIT = "lora_freq_x_i";
static const char *const PREF_LORA_FREQ_CROSSDIGI_PRESET = "lora_freq_x";
static const char *const PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET_INIT = "tx_qrg_bc_i";
static const char *const PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET = "tx_qrg_bc";
static const char *const PREF_LORA_SPEED_CROSSDIGI_PRESET_INIT = "lora_speed_x_i";
static const char *const PREF_LORA_SPEED_CROSSDIGI_PRESET = "lora_speed_x";
static const char *const PREF_LORA_TX_POWER_CROSSDIGI_PRESET_INIT = "txPower_x_i";
static const char *const PREF_LORA_TX_POWER_CROSSDIGI_PRESET = "txPower_x";
static const char *const PREF_LORA_RX_ON_FREQUENCIES_PRESET_INIT = "rx_qrg_i";
static const char *const PREF_LORA_RX_ON_FREQUENCIES_PRESET = "rx_qrg";
static const char *const PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET_INIT = "lora_cradapt_i";
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_CROSS_DIGIPEATING_MODE_PRESET_INIT = "lora_dig_x_m_i";
static const char *const PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET = "lora_dig_x_m";
// Station settings
static const char *const PREF_APRS_CALLSIGN = "aprs_callsign";
static const char *const PREF_APRS_RELAY_PATH = "aprs_relay_path";
@ -25,6 +49,8 @@ static const char *const PREF_APRS_SYMBOL_TABLE = "aprs_s_table";
static const char *const PREF_APRS_SYMBOL = "aprs_symbol";
static const char *const PREF_APRS_COMMENT = "aprs_comment";
static const char *const PREF_APRS_COMMENT_INIT = "aprs_comm_init";
static const char *const PREF_APRS_COMMENT_RATELIMIT_PRESET = "aprs_comm_rt";
static const char *const PREF_APRS_COMMENT_RATELIMIT_PRESET_INIT = "aprs_comm_rt_i";
static const char *const PREF_APRS_SHOW_ALTITUDE = "aprs_alt";
static const char *const PREF_APRS_SHOW_ALTITUDE_INIT = "aprs_alt_init";
static const char *const PREF_APRS_SHOW_BATTERY = "aprs_batt";
@ -47,6 +73,7 @@ static const char *const PREF_TNC_SELF_TELEMETRY_MIC = "tnc_tel_mic";
static const char *const PREF_TNC_SELF_TELEMETRY_MIC_INIT = "tnc_tel_mic_i";
static const char *const PREF_TNC_SELF_TELEMETRY_PATH = "tnc_tel_path";
static const char *const PREF_TNC_SELF_TELEMETRY_PATH_INIT = "tnc_tel_path_i";
// SMART BEACONING
static const char *const PREF_APRS_SB_MIN_INTERVAL_PRESET = "sb_min_interv";
static const char *const PREF_APRS_SB_MIN_INTERVAL_PRESET_INIT = "sb_min_interv_i";
@ -58,10 +85,18 @@ static const char *const PREF_APRS_SB_MAX_SPEED_PRESET = "sb_max_speed";
static const char *const PREF_APRS_SB_MAX_SPEED_PRESET_INIT = "sb_max_speed_i";
static const char *const PREF_APRS_SB_ANGLE_PRESET = "sb_angle";
static const char *const PREF_APRS_SB_ANGLE_PRESET_INIT = "sb_angle_i";
static const char *const PREF_APRS_SB_TURN_SLOPE_PRESET = "sb_turn_slope";
static const char *const PREF_APRS_SB_TURN_SLOPE_PRESET_INIT = "sb_turn_slope_i";
static const char *const PREF_APRS_SB_TURN_TIME_PRESET = "sb_turn_time";
static const char *const PREF_APRS_SB_TURN_TIME_PRESET_INIT = "sb_turn_time_i";
// Device settings
static const char *const PREF_APRS_GPS_EN = "gps_enabled";
static const char *const PREF_APRS_GPS_EN_INIT = "gps_state_init";
static const char *const PREF_ACCEPT_OWN_POSITION_REPORTS_VIA_KISS = "kiss_myloc_ok";
static const char *const PREF_ACCEPT_OWN_POSITION_REPORTS_VIA_KISS_INIT = "kiss_myloc_ok_i";
static const char *const PREF_GPS_ALLOW_SLEEP_WHILE_KISS = "gps_sleep_ok";
static const char *const PREF_GPS_ALLOW_SLEEP_WHILE_KISS_INIT = "gps_sleep_ok_i";
static const char *const PREF_APRS_SHOW_CMT = "show_cmt";
static const char *const PREF_APRS_SHOW_CMT_INIT = "show_cmt_init";
static const char *const PREF_DEV_BT_EN = "bt_enabled";
@ -77,4 +112,20 @@ static const char *const PREF_DEV_AUTO_SHUT_PRESET_INIT = "shutdown_dtini";
static const char *const PREF_DEV_SHOW_OLED_TIME = "sh_oledtime"; // set OLED timeout
static const char *const PREF_DEV_SHOW_OLED_TIME_INIT = "sh_oledtime_i";
// APRSIS settings
static const char *const PREF_APRSIS_EN_INIT = "aprsis_en_i";
static const char *const PREF_APRSIS_EN = "aprsis_en";
static const char *const PREF_APRSIS_SERVER_NAME_INIT = "aprsis_srv_h_i";
static const char *const PREF_APRSIS_SERVER_NAME = "aprsis_srv_h";
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_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";
static const char *const PREF_APRSIS_PASSWORD = "aprsis_pw";
static const char *const PREF_APRSIS_ALLOW_INET_TO_RF_INIT = "aprsis_2rf_i";
static const char *const PREF_APRSIS_ALLOW_INET_TO_RF = "aprsis_2rf";
#endif

View File

@ -1,10 +0,0 @@
#ifndef BUILD_NUMBER
#define BUILD_NUMBER "17"
#endif
#ifndef VERSION
#define VERSION "v0.4.6.17-0a01171 - 2021-12-28 21:20:22.015464"
#endif
#ifndef VERSION_SHORT
#define VERSION_SHORT "v0.4.6.17-0a01171"
#endif

View File

@ -27,6 +27,8 @@ RadioHead/RH_CC110.cpp
RadioHead/RH_CC110.h
RadioHead/RH_E32.cpp
RadioHead/RH_E32.h
RadioHead/RH_LoRaFileOps.cpp
RadioHead/RH_LoRaFileOps.h
RadioHead/RH_NRF24.cpp
RadioHead/RH_NRF24.h
RadioHead/RH_NRF51.cpp
@ -163,6 +165,8 @@ RadioHead/examples/raspi/rf95/rf95_router_server1/rf95_router_server1.cpp
RadioHead/examples/raspi/rf95/rf95_router_server1/Makefile
RadioHead/examples/raspi/rf95/rf95_mesh_server1/Makefile
RadioHead/examples/raspi/rf95/rf95_mesh_server1/rf95_mesh_server1.cpp
RadioHead/examples/lorafileops/lorafileops_client/lorafileops_client.cpp
RadioHead/examples/lorafileops/lorafileops_server/lorafileops_server.cpp
RadioHead/tools/etherSimulator.pl
RadioHead/tools/chain.conf
RadioHead/tools/simMain.cpp

View File

@ -1,12 +0,0 @@
# RadioHead
This is the RadioHead Packet Radio library for embedded microprocessors. It provides a complete object-oriented library for sending and receiving packetized messages via a variety of common data radios and other transports on a range of embedded microprocessors.
The version of the package that this documentation refers to can be downloaded from http://www.airspayce.com/mikem/arduino/RadioHead/RadioHead-1.119.zip You can find the latest version of the documentation at http://www.airspayce.com/mikem/arduino/RadioHead
You can also find online help and discussion at http://groups.google.com/group/radiohead-arduino Please use that group for all questions and discussions on this topic. Do not contact the author directly, unless it is to discuss commercial licensing. Before asking a question or reporting a bug, please read
http://en.wikipedia.org/wiki/Wikipedia:Reference_desk/How_to_ask_a_software_question
http://www.catb.org/esr/faqs/smart-questions.html
http://www.chiark.greenend.org.uk/~shgtatham/bugs.html
Caution: Developing this type of software and using data radios successfully is challenging and requires a substantial knowledge base in software and radio and data transmission technologies and theory. It may not be an appropriate project for beginners. If you are a beginner, you will need to spend some time gaining knowledge in these areas first.

View File

@ -23,6 +23,9 @@ HardwareSPI SPI(1);
#elif (RH_PLATFORM == RH_PLATFORM_STM32L0) && (defined STM32L082xx || defined STM32L072xx)
extern SPIClass radio_spi; // Created in RH_ABZ.cpp
#define SPI radio_spi
#elif (RH_PLATFORM == RH_PLATFORM_ESP32 && defined(RH_ESP32_USE_HSPI))
SPIClass SPI_HSPI(HSPI);
#define SPI SPI_HSPI
#endif
@ -99,11 +102,12 @@ void RHHardwareSPI::begin()
else
frequency = 1000000;
#if ((RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined (__arm__) && (defined(ARDUINO_SAM_DUE) || defined(ARDUINO_ARCH_SAMD))) || defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32L0) || defined(NRF52)
#if ((RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined (__arm__) && (defined(ARDUINO_SAM_DUE) || defined(ARDUINO_ARCH_SAMD))) || defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32L0) || defined(NRF52) || defined (ARDUINO_ARCH_RP2040)
// Arduino Due in 1.5.5 has its own BitOrder :-(
// So too does Arduino Zero
// So too does rogerclarkmelbourne/Arduino_STM32
// So too does GrumpyOldPizza/ArduinoCore-stm32l0
// So too does RPI Pico
::BitOrder bitOrder;
// This no longer relevant: new versions is uint8_t
//#elif (RH_PLATFORM == RH_PLATFORM_ATTINY_MEGA)

View File

@ -11,6 +11,7 @@
#if (RH_PLATFORM == RH_PLATFORM_STM32)
// Maple etc
HardwareTimer timer(MAPLE_TIMER);
#elif defined(ARDUINO_ARCH_RP2040)
#elif defined(BOARD_NAME)
// ST's Arduino Core STM32, https://github.com/stm32duino/Arduino_Core_STM32
@ -125,7 +126,7 @@ bool RH_ASK::init()
// returns 0 & nticks = 0 on fault
uint8_t RH_ASK::timerCalc(uint16_t speed, uint16_t max_ticks, uint16_t *nticks)
{
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) || (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8) || (RH_PLATFORM == RH_PLATFORM_ATTINY)
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO && !defined(ARDUINO_ARCH_RP2040)) || (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8) || (RH_PLATFORM == RH_PLATFORM_ATTINY)
// Clock divider (prescaler) values - 0/3333: error flag
uint8_t prescaler; // index into array & return bit value
unsigned long ulticks; // calculate by ntick overflow
@ -174,6 +175,15 @@ uint8_t RH_ASK::timerCalc(uint16_t speed, uint16_t max_ticks, uint16_t *nticks)
#endif
}
#if defined(RH_PLATFORM_ARDUINO) && defined(ARDUINO_ARCH_RP2040)
void set_pico_alarm(uint32_t speed)
{
uint32_t period = (1000000 / 8) / speed; // In microseconds
uint64_t target = timer_hw->timerawl + period;
timer_hw->alarm[RH_ASK_PICO_ALARM_NUM] = (uint32_t) target;
}
#endif
// The idea here is to get 8 timer interrupts per bit period
void RH_ASK::timerSetup()
{
@ -333,8 +343,6 @@ void RH_ASK::timerSetup()
timer->CTRLA |= TCB_ENABLE_bm;
#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) // Arduino specific
uint16_t nticks; // number of prescaled ticks needed
uint8_t prescaler; // Bit values for CS0[2:0]
#if defined(__arm__) && defined(CORE_TEENSY)
@ -408,8 +416,18 @@ void RH_ASK::timerSetup()
NVIC_ClearPendingIRQ(RH_ASK_DUE_TIMER_IRQ);
NVIC_EnableIRQ(RH_ASK_DUE_TIMER_IRQ);
TC_Start(RH_ASK_DUE_TIMER, RH_ASK_DUE_TIMER_CHANNEL);
#elif defined(ARDUINO_ARCH_RP2040)
// Per https://emalliab.wordpress.com/2021/04/18/raspberry-pi-pico-arduino-core-and-timers/
hw_set_bits(&timer_hw->inte, 1u << RH_ASK_PICO_ALARM_NUM);
void picoInterrupt(); // Forward declaration of interrupt handler
irq_set_exclusive_handler(RH_ASK_PICO_ALARM_IRQ, picoInterrupt);
irq_set_enabled(RH_ASK_PICO_ALARM_IRQ, true);
set_pico_alarm(_speed);
#else
uint16_t nticks; // number of prescaled ticks needed
uint8_t prescaler; // Bit values for CS0[2:0]
// This is the path for most Arduinos
// figure out prescaler value and counter match value
#if defined(RH_ASK_ARDUINO_USE_TIMER2)
@ -739,6 +757,15 @@ void TC1_Handler()
TC_GetStatus(RH_ASK_DUE_TIMER, 1);
thisASKDriver->handleTimerInterrupt();
}
#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(ARDUINO_ARCH_RP2040)
void picoInterrupt()
{
hw_clear_bits(&timer_hw->intr, 1u << RH_ASK_PICO_ALARM_NUM);
set_pico_alarm(thisASKDriver->speed());
thisASKDriver->handleTimerInterrupt();
}
#elif defined(BOARD_NAME)
// ST's Arduino Core STM32, https://github.com/stm32duino/Arduino_Core_STM32
// Declaration of the callback function changed in 1.9
@ -752,7 +779,7 @@ void interrupt(HardwareTimer*)
{
thisASKDriver->handleTimerInterrupt();
}
#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) && (defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(ARDUINO_ARCH_STM32F3) || defined(ARDUINO_ARCH_STM32F4))
#elif (RH_PLATFORM == RH_PLATFORM_ARDUINO) && (defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) || defined(ARDUINO_ARCH_STM32F3) || defined(ARDUINO_ARCH_STM32F4) || defined(ARDUINO_ARCH_RP2040))
// Roger Clark Arduino STM32, https://github.com/rogerclarkmelbourne/Arduino_STM32
void interrupt()
{

View File

@ -129,7 +129,7 @@ bool RH_CC110::init()
spiWriteRegister(RH_CC110_REG_02_IOCFG0, RH_CC110_GDO_CFG_CRC_OK_AUTORESET); // gdo0 interrupt on CRC_OK
spiWriteRegister(RH_CC110_REG_06_PKTLEN, RH_CC110_MAX_PAYLOAD_LEN); // max packet length
spiWriteRegister(RH_CC110_REG_07_PKTCTRL1, RH_CC110_CRC_AUTOFLUSH); // no append status, crc autoflush, no addr check
spiWriteRegister(RH_CC110_REG_07_PKTCTRL1, RH_CC110_CRC_AUTOFLUSH | RH_CC110_APPEND_STATUS); // append status, crc autoflush, no addr check
spiWriteRegister(RH_CC110_REG_08_PKTCTRL0, RH_CC110_PKT_FORMAT_NORMAL | RH_CC110_CRC_EN | RH_CC110_LENGTH_CONFIG_VARIABLE);
spiWriteRegister(RH_CC110_REG_13_MDMCFG1, RH_CC110_NUM_PREAMBLE_4); // 4 preamble bytes, chan spacing not used
spiWriteRegister(RH_CC110_REG_17_MCSM1, RH_CC110_CCA_MODE_RSSI_PACKET | RH_CC110_RXOFF_MODE_RX | RH_CC110_TXOFF_MODE_IDLE);
@ -163,12 +163,16 @@ void RH_CC110::handleInterrupt()
// Radio is configured to stay in RX until we move it to IDLE after a CRC_OK message for us
// We only get interrupts in RX mode, on CRC_OK
#if 0
// SIGH: reading RH_CC110_REG_34_RSSI here does not yield the RSSI of when the packet was received
// Must get it from the end of the packet read
uint8_t raw_rssi = spiBurstReadRegister(RH_CC110_REG_34_RSSI); // Was set when sync word was detected
// Conversion of RSSI value to received power level in dBm per TI section 5.18.2
if (raw_rssi >= 128)
_lastRssi = (((int16_t)raw_rssi - 256) / 2) - 74;
else
_lastRssi = ((int16_t)raw_rssi / 2) - 74;
#endif
_bufLen = spiReadRegister(RH_CC110_REG_3F_FIFO);
if (_bufLen < 4)
@ -178,7 +182,16 @@ void RH_CC110::handleInterrupt()
clearRxBuf();
return;
}
spiBurstRead(RH_CC110_REG_3F_FIFO | RH_CC110_SPI_BURST_MASK | RH_CC110_SPI_READ_MASK, _buf, _bufLen);
// Because we have RH_CC110_APPEND_STATUS set, we can read another 2 octets, the RSSI and the CRC state
spiBurstRead(RH_CC110_REG_3F_FIFO | RH_CC110_SPI_BURST_MASK | RH_CC110_SPI_READ_MASK, _buf, _bufLen + 2);
// And then decode the RSSI
int raw_rssi = _buf[_bufLen];
if (raw_rssi >= 128)
_lastRssi = (((int16_t)raw_rssi - 256) / 2) - 74;
else
_lastRssi = ((int16_t)raw_rssi / 2) - 74;
// All good so far. See if its for us
validateRxBuf();
if (_rxBufValid)

View File

@ -880,7 +880,8 @@ private:
volatile uint8_t _bufLen;
/// The receiver/transmitter buffer
uint8_t _buf[RH_CC110_MAX_PAYLOAD_LEN];
/// Allow for 2 status bytes so we can read packet RSSI
uint8_t _buf[RH_CC110_MAX_PAYLOAD_LEN + 2];
/// True when there is a valid message in the buffer
volatile bool _rxBufValid;

View File

@ -0,0 +1,279 @@
// RH_LoRaFileOpscpp
//
// Copyright (C) 2021 Mike McCauley
#include <RH_LoRaFileOps.h>
// This can only build on Linux and compatible systems
// Caution also requires Lora-file-ops driver to be installed
// See https://github.com/starnight/LoRa/tree/file-ops
#if (RH_PLATFORM == RH_PLATFORM_UNIX)
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
RH_LoRaFileOps::RH_LoRaFileOps(const char* port)
:
_port(port),
_fd(-1)
{
}
bool RH_LoRaFileOps::init()
{
if (!RHGenericDriver::init())
return false;
_fd = open(_port, O_RDWR);
if (_fd == -1)
{
Serial.print("RH_LoRaFileOps::init LORA device port open failed: ");
Serial.println(strerror(errno));
return false;
}
// These settings are compatible with RH_RF95::Bw125Cr45Sf2048 and the other defaults
// for RH_RF95
setFrequency(434000000);
setTxPower(13);
setSpreadingFactor(2048);
setBW(125000);
setCRC(true);
setLNA(0);
return true;
}
bool RH_LoRaFileOps::available()
{
// Test if the port can be read
fd_set read_fds;
struct timeval tv = {.tv_sec = 0, .tv_usec = 0};
FD_ZERO(&read_fds);
FD_SET(_fd, &read_fds);
if (select(_fd+1, &read_fds, NULL, NULL, &tv) == -1)
{
Serial.println("Select failed");
return false;
}
return FD_ISSET(_fd, &read_fds) ? true : false;
}
bool RH_LoRaFileOps::recv(uint8_t* buf, uint8_t* len)
{
if (_fd == -1) return false;
if (!available()) return false;
// Read the available packet from the driver
uint8_t readbuf[RH_LORAFILEOPS_MAX_PAYLOAD_LEN];
ssize_t sz;
sz = read(_fd, readbuf, RH_LORAFILEOPS_MAX_PAYLOAD_LEN);
// Remember the last signal to noise ratio, LORA mode
// Per page 111, SX1276/77/78/79 datasheet
_lastSNR = getSNR();
// Remember the RSSI of this packet, LORA mode
// This figure has already been massaged by the driver
_lastRssi = getRSSI();
// Test if its really for us
if (sz < 4)
return false; // Too short to be a real message
// Extract the 4 headers
_rxHeaderTo = readbuf[0];
_rxHeaderFrom = readbuf[1];
_rxHeaderId = readbuf[2];
_rxHeaderFlags = readbuf[3];
if (_promiscuous ||
_rxHeaderTo == _thisAddress ||
_rxHeaderTo == RH_BROADCAST_ADDRESS)
{
// Yes its for us
// Skip the 4 headers that are at the beginning of the rxBuf
if (*len > (sz - RH_LORAFILEOPS_HEADER_LEN))
*len = sz - RH_LORAFILEOPS_HEADER_LEN;
memcpy(buf, readbuf + RH_LORAFILEOPS_HEADER_LEN, *len);
Serial.println("recv OK");
return true;
}
// Not for us
return false;
}
bool RH_LoRaFileOps::send(const uint8_t* data, uint8_t len)
{
if (_fd == -1) return false;
if (len > RH_LORAFILEOPS_MAX_MESSAGE_LEN)
return false;
// Should never need to wait for a packet to be sent since the driver always
// waits before returning from write
// Assemble the entire message, including RadioHead header
// Header bytes, total size of RH_LORAFILEOPS_HEADER_LEN
uint8_t buf[RH_LORAFILEOPS_MAX_PAYLOAD_LEN];
buf[0] = _txHeaderTo;
buf[1] = _txHeaderFrom;
buf[2] = _txHeaderId;
buf[3] = _txHeaderFlags;
memcpy(buf+4, data, len);
// Send the message to the driver
ssize_t sz;
// Returns when the packet has been transmtted:
sz = write(_fd, buf, len+RH_LORAFILEOPS_HEADER_LEN);
return sz == len+RH_LORAFILEOPS_HEADER_LEN;
}
uint8_t RH_LoRaFileOps::maxMessageLength()
{
return RH_LORAFILEOPS_MAX_MESSAGE_LEN;
}
bool RH_LoRaFileOps::setFrequency(uint32_t centre)
{
if (_fd == -1) return false;
ioctl(_fd, LORA_SET_FREQUENCY, &centre);
return true; // FIXME??
}
uint32_t RH_LoRaFileOps::getFrequency()
{
if (_fd == -1) return 0;
uint32_t freq;
ioctl(_fd, LORA_GET_FREQUENCY, &freq);
return freq;
}
void RH_LoRaFileOps::setTxPower(int32_t power)
{
if (_fd == -1) return;
ioctl(_fd, LORA_SET_POWER, &power);
}
int32_t RH_LoRaFileOps::getTxPower()
{
if (_fd == -1) return 0;
int32_t power;
ioctl(_fd, LORA_GET_POWER, &power);
return power;
}
void RH_LoRaFileOps::setState(uint32_t state)
{
if (_fd == -1) return;
ioctl(_fd, LORA_SET_STATE, &state);
}
uint32_t RH_LoRaFileOps::getState()
{
if (_fd == -1) return LORA_STATE_SLEEP;
uint32_t state;
ioctl(_fd, LORA_GET_STATE, &state);
return state;
}
void RH_LoRaFileOps::setSpreadingFactor(int32_t sf)
{
ioctl(_fd, LORA_SET_SPRFACTOR, &sf);
}
int32_t RH_LoRaFileOps::getSpreadingFactor()
{
if (_fd == -1) return 0;
uint32_t sprf;
ioctl(_fd, LORA_GET_SPRFACTOR, &sprf);
return sprf;
}
// This gets the raw RSSI figure. Its not adjusted to dBm
int32_t RH_LoRaFileOps::getRSSI()
{
if (_fd == -1) return 0;
int32_t rssi;
ioctl(_fd, LORA_GET_RSSI, &rssi);
return rssi;
}
// Last packet SNR
int32_t RH_LoRaFileOps::getSNR()
{
if (_fd == -1) return 0;
int32_t snr;
ioctl(_fd, LORA_GET_SNR, &snr);
return snr;
}
void RH_LoRaFileOps::setLNA(int32_t lna)
{
if (_fd == -1) return;
ioctl(_fd, LORA_SET_LNA, &lna);
}
int32_t RH_LoRaFileOps::getLNA()
{
if (_fd == -1) return 0;
int32_t lna;
ioctl(_fd, LORA_GET_LNA, &lna);
return lna;
}
void RH_LoRaFileOps::setLNAAGC(int32_t lnaagc)
{
if (_fd == -1) return;
ioctl(_fd, LORA_SET_LNAAGC, &lnaagc);
}
void RH_LoRaFileOps::setBW(int32_t bw)
{
if (_fd == -1) return;
ioctl(_fd, LORA_SET_BANDWIDTH, &bw);
}
int32_t RH_LoRaFileOps::getBW()
{
if (_fd == -1) return 0;
uint32_t bw;
ioctl(_fd, LORA_GET_BANDWIDTH, &bw);
return bw;
}
void RH_LoRaFileOps::setCRC(uint32_t crc)
{
if (_fd == -1) return;
ioctl(_fd, LORA_SET_CRC, &crc);
}
int RH_LoRaFileOps::lastSNR()
{
return _lastSNR;
}
#endif

View File

@ -0,0 +1,368 @@
// RH_LoRaFileOps.h
//
// Definitions for RadioHead driver on RPi+Linux
// and using LoRa-file-ops Linux driver ioctls to
// transmit and receive RadioHead compatible messages via SX1276/77/78/79
// and compatible radios.
// Requires a modified version of LoRa-file-ops driver to be installed,
// and a compatible radio to be connected
// appropriately
// https://github.com/starnight/LoRa/tree/file-ops
//
// Tested with:
// RPi 2 + Debian 2021-03-04 (kernel 5.10.17-v7+ #1403)
// Dragino LoRa/GPS HAT, https://wiki.dragino.com/index.php?title=Lora/GPS_HAT
// modified so RFM95 pin 5 NSS was no longer connected to RPi
// pin P1-22 (GPIO6), but is instead connected to RPi Pin P1-24 (CE0) which
// is the one used by /dev/loraSPI0.0
//
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2021 Mike McCauley
//
#ifndef RH_LORAFILEOPS_h
#define RH_LORAFILEOPS_h
#include <RHGenericDriver.h>
#warning RH_LoRaFileOps unfinished
// This can only build on Linux and compatible systems
// Caution also requires Lora-file-ops driver to be installed
// See https://github.com/starnight/LoRa/tree/file-ops
#if (RH_PLATFORM == RH_PLATFORM_UNIX) || defined(DOXYGEN)
// Driver constant definitions
// These are copied from LoRa file-ops branch pull request #16
// See the instructions in the RH_LoRaFileOps documentation for getting that version from github
// which is absolutely necessary if you want to support RadioHead messages with CRC enabled,
// which we strongly recommend.
// since they are not necessarily available in the compile host file
// system.
// CAUTION: these must be kept in sync with LoRa-file-ops if it changes
/* I/O control by each command. */
#include <sys/ioctl.h>
#define LORA_IOC_MAGIC '\x74'
#define LORA_SET_STATE (_IOW(LORA_IOC_MAGIC, 0, int))
#define LORA_GET_STATE (_IOR(LORA_IOC_MAGIC, 1, int))
#define LORA_SET_FREQUENCY (_IOW(LORA_IOC_MAGIC, 2, int))
#define LORA_GET_FREQUENCY (_IOR(LORA_IOC_MAGIC, 3, int))
#define LORA_SET_POWER (_IOW(LORA_IOC_MAGIC, 4, int))
#define LORA_GET_POWER (_IOR(LORA_IOC_MAGIC, 5, int))
#define LORA_SET_LNA (_IOW(LORA_IOC_MAGIC, 6, int))
#define LORA_GET_LNA (_IOR(LORA_IOC_MAGIC, 7, int))
#define LORA_SET_LNAAGC (_IOR(LORA_IOC_MAGIC, 8, int))
#define LORA_SET_SPRFACTOR (_IOW(LORA_IOC_MAGIC, 9, int))
#define LORA_GET_SPRFACTOR (_IOR(LORA_IOC_MAGIC, 10, int))
#define LORA_SET_BANDWIDTH (_IOW(LORA_IOC_MAGIC, 11, int))
#define LORA_GET_BANDWIDTH (_IOR(LORA_IOC_MAGIC, 12, int))
#define LORA_GET_RSSI (_IOR(LORA_IOC_MAGIC, 13, int))
#define LORA_GET_SNR (_IOR(LORA_IOC_MAGIC, 14, int))
/* Mikem added 2021-04-19 fro pull request 16: */
#define LORA_SET_CRC (_IOW(LORA_IOC_MAGIC, 15, int))
#define LORA_SET_CODINGRATE (_IOW(LORA_IOC_MAGIC, 16, int))
#define LORA_GET_CODINGRATE (_IOR(LORA_IOC_MAGIC, 17, int))
#define LORA_SET_IMPLICIT (_IOW(LORA_IOC_MAGIC, 18, int))
#define LORA_SET_LDRO (_IOW(LORA_IOC_MAGIC, 19, int))
#define LORA_SET_PREAMBLE (_IOW(LORA_IOC_MAGIC, 20, int))
#define LORA_GET_PREAMBLE (_IOR(LORA_IOC_MAGIC, 21, int))
#define LORA_SET_PARAMP (_IOW(LORA_IOC_MAGIC, 22, int))
#define LORA_GET_PARAMP (_IOR(LORA_IOC_MAGIC, 23, int))
#define LORA_SET_OCPIMAX (_IOW(LORA_IOC_MAGIC, 24, int))
#define LORA_GET_OCPIMAX (_IOR(LORA_IOC_MAGIC, 25, int))
#define LORA_SET_LNABOOSTHF (_IOW(LORA_IOC_MAGIC, 26, int))
#define LORA_SET_PMAX20DBM (_IOW(LORA_IOC_MAGIC, 27, int))
/* List the state of the LoRa device. */
#define LORA_STATE_SLEEP 0
#define LORA_STATE_STANDBY 1
#define LORA_STATE_TX 2
#define LORA_STATE_RX 3
#define LORA_STATE_CAD 4
// Max number of octets the SX1278 LORA Rx/Tx FIFO can hold
#define RH_LORAFILEOPS_FIFO_SIZE 255
// This is the maximum number of bytes that can be carried by the LORA.
// We use some for headers, keeping fewer for RadioHead messages
#define RH_LORAFILEOPS_MAX_PAYLOAD_LEN RH_LORAFILEOPS_FIFO_SIZE
// The length of the headers we add.
// The headers are inside the LORA's payload
#define RH_LORAFILEOPS_HEADER_LEN 4
// This is the maximum message length that can be supported by this driver.
// Can be pre-defined to a smaller size (to save SRAM) prior to including this header
// Here we allow for 1 byte message length, 4 bytes headers, user data and 2 bytes of FCS
#ifndef RH_LORAFILEOPS_MAX_MESSAGE_LEN
#define RH_LORAFILEOPS_MAX_MESSAGE_LEN (RH_LORAFILEOPS_MAX_PAYLOAD_LEN - RH_LORAFILEOPS_HEADER_LEN)
#endif
/////////////////////////////////////////////////////////////////////
/// \class RH_LoRaFileOps RH_LoRaFileOps.h <RH_LoRaFileOps.h>
/// \brief Driver to send and receive unaddressed, unreliable datagrams via a LoRa
/// capable radio transceiver on a Linux platform (possibly Raspberry Pi), using the
/// lora-file-ops driver by Jian-Hong Pan (starnight):
/// https://github.com/starnight/LoRa/tree/file-ops
///
/// This RadioHead driver is only available to Commercial licensees. Apply to info@airspayce.com.
///
/// For an excellent discussion of LoRa range and modulations, see
/// https://medium.com/home-wireless/testing-lora-radios-with-the-limesdr-mini-part-2-37fa481217ff
/// Works with Dragino LoRa/GPS HAT, https://wiki.dragino.com/index.php?title=Lora/GPS_HAT
/// modified so RFM95 pin 5 NSS was no longer connected to RPi
/// pin P1-22 (GPIO6), but is instead connected to RPi Pin P1-24 (CE0) which
/// is the one used by /dev/loraSPI0.0. Interoperates with RH_RF95
/// with modem config RH_RF95::Bw125Cr45Sf2048
///
/// \par Overview
///
/// This class provides basic functions for sending and receiving unaddressed,
/// unreliable datagrams of arbitrary length up to 251 octets per packet.
///
/// Manager classes may use this class to implement reliable, addressed datagrams and streams,
/// mesh routers, repeaters, translators etc.
///
/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and
/// modulation scheme.
///
/// This RadioHead Driver provides an object-oriented interface for sending and receiving
/// data messages with Semtech SX1276/77/78/79
/// and compatible radio modules in LoRa mode, using the lora-file-ops Linux driver. It only runs on Linux
/// such as Raspberry Pi Debian etc.is a low-cost ISM transceiver
/// chip. It supports FSK, GFSK, OOK over a wide range of frequencies and
/// programmable data rates, and it also supports the proprietary LoRA (Long Range) mode, which
/// is the only mode supported in this RadioHead driver (because that is the only mode supported by the
/// underlying lora-file-ops Linux driver.
///
/// This Driver provides functions for sending and receiving messages of up
/// to 251 octets on any frequency supported by the radio, in a range of
/// predefined Bandwidths, Spreading Factors and Coding Rates. Frequency can be set with
/// 61Hz precision to any frequency from 240.0MHz to 960.0MHz. Caution: most modules only support a more limited
/// range of frequencies due to antenna tuning.
///
/// Up to 2 modules are supported by lora-file-ops
/// permitting the construction of translators and frequency changers, etc.
///
/// Support for other features such as transmitter power control etc is
/// also provided.
///
/// Tested with:
/// RPi 2 + Debian 2021-03-04 (kernel 5.10.17-v7+ #1403)
/// Dragino LoRa/GPS HAT, https://wiki.dragino.com/index.php?title=Lora/GPS_HAT
/// modified so RFM95 pin 5 NSS was no longer connected to RPi
/// pin P1-22 (GPIO6), but is instead connected to RPi Pin P1-24 (CE0) which
/// is the one used by /dev/loraSPI0.0
/// \par Packet Format
///
/// All messages sent and received by this RH_RF95 Driver conform to this packet format:
///
/// - LoRa mode:
/// - 8 symbol PREAMBLE
/// - Explicit header with header CRC (default CCITT, handled internally by the radio)
/// - 4 octets HEADER: (TO, FROM, ID, FLAGS)
/// - 0 to 251 octets DATA
/// - CRC (default CCITT, handled internally by the radio)
///
/// This format is compatible with the one used by RH_RF95 by default.
///
/// \par Modulation
///
/// The default modulation scheme implemented by this driver is:
/// 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
/// which is compatible withthe RH_RF95 modem config RH_RF95::Bw125Cr45Sf2048 and so this RadioHead driver will
/// interoperate with RH_RF95.
///
/// \par Installing lora-file-ops
/// For this driver to work on a Linux platform such as Raspberry Pi, it is absolutely necessary to install the
/// LoRa-file-ops Linux driver written by starnight, and which is available from github.
/// The version currently available (2021-04-19) does not support enabling CRCs in the radio, which is
/// strongly recommended, and necessary to work with any other RadioHead lora driver/
/// At this date, code to add CRC suport to the driver is avalable as a pull request on github
/// as a Git pull request #16 from flyskywhy. We strongly recommend using it as described below.
///
/// To get LoRa-file-ops from starnight, plus the necessary patches and fixes from pull request #16 from flyskywhy
/// and to build it and install it and load it into the kernel for testing:
/// \code
/// # ON a recent Debian kernel:
/// sudo apt-get install linux-headers-rpi raspberrypi-kernel-headers
/// # Enable the SPI interface in the kernel
/// sudo raspi-config:
/// -> 3 Interface Options
/// -> P4 SPI
/// -> Would you like the SPI interface to be enabled? select Yes, press Return, Return, Select Finish
/// # in a working directory, not as root:
/// git clone https://github.com/starnight/LoRa.git
/// cd LoRa/
/// git checkout file-ops
/// git fetch origin pull/16/head:file-ops-patched # only until pull #16 is not merged into master
/// git checkout file-ops-patched # only until pull #16 is not merged into master
/// cd LoRa/
/// make
/// make install
/// cd ../dts-overlay
/// make
/// cd ../
/// # and after every reboot:
/// sudo dtoverlay rpi-lora-spi
/// sudo modprobe sx1278
/// \endcode
///
/// If you want to permanently add the LoRa-file-ops Linux driver so it loads automatically
/// on every boot, add this to /boot/config.txt
/// \code
/// dtparam=rpi-lora-spi=on
/// \endcode
///
/// Note: it may be the case in the future that pull request 16 is merged into the master of LoRa-File-Ops
/// in which case 2 steps are not needed above
class RH_LoRaFileOps : public RHGenericDriver
{
public:
/// Constructor. You can have multiple instances each connected to a different LoRa port
/// \param[in] port Name of the lora-file-ops port, typically something like /dev/loraSPI0.0
RH_LoRaFileOps(const char* port);
/// Initialise the Driver transport hardware and software.
/// Opens the LorFileOps driver port and initalises the radio to default settings
/// Leaves the radio in receive mode,
/// with default configuration of: 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 2048chips/symbol, CRC on
/// which is compatible with RH_RF95::Bw125Cr45Sf2048
/// \return true if initialisation succeeded.
virtual bool init();
/// Tests whether a new message is available from the lora-file-ops Linux driver.
/// This can be called multiple times in a timeout loop
/// \return true if a new, complete, error-free uncollected message is available to be retreived by recv()
virtual bool available();
// Sigh, its not possible to implement waitAvailable and waitAvailableTimout in terms
// of select(), since the LoRa-file-ops driver does not detect any interrupts, and
// therefore select will not return when a packet is received by the radio.
// So we have to live with the RHGenericDriver implementations that call available() to poll the port.
// waitAvailableTimeout() supports an optional delay between each poll
// of available() so that on Linux at least another process can get the CPU.
/// If there is a valid message available and it is for this node, copy it to buf and return true
/// else return false.
/// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
/// You should be sure to call this function frequently enough to not miss any messages
/// or call it aafter available(), waitAvailable() or waitAvailableTimeout()
/// indicate that a message is avalable
/// It is recommended that you call it in your main loop.
/// \param[in] buf Location to copy the received message
/// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
/// \return true if a valid message addressed to this node was copied to buf
virtual bool recv(uint8_t* buf, uint8_t* len);
/// Loads a message into the transmitter and starts the transmitter. Note that a message length
/// of 0 is permitted. CAD is not supported yet.
/// The lora-file-ops driver waits for the entire message to be transmitted before resuming operations.
/// \param[in] data Array of data to be sent
/// \param[in] len Number of bytes of data to send
/// \return true if the message length was valid and it was correctly transmitted.
virtual bool send(const uint8_t* data, uint8_t len);
/// Returns the maximum message length
/// available in this Driver.
/// \return The maximum legal message length
virtual uint8_t maxMessageLength();
/// Sets the transmitter and receiver
/// centre (carrier) frequency.
/// \param[in] centre Frequency in Hz. 137000000 to 1020000000. Caution: SX1276/77/78/79 comes in several
/// different frequency ranges, and setting a frequency outside that range of your radio will probably not work
/// correctly becasue the antenna coupling or antenna wont work outside their designed frequency range
/// \return true if the selected frequency centre is within range
bool setFrequency(uint32_t centre);
/// Returns the current transmitter and receiver
/// centre frequency.
/// \return Centre frequency in Hz.
uint32_t getFrequency();
/// Returns the Signal-to-noise ratio (SNR) of the last received message, as measured
/// by the receiver.
/// \return SNR of the last received message in dB
int lastSNR();
/// Sets the transmitter power output level
/// Be a good neighbour and set the lowest power level you need.
/// Caution: legal power limits may apply in certain countries.
/// After init(), the power will be set to 13dBm
/// \param[in] power Transmitter power level in dBm. Max 20dBm.
void setTxPower(int32_t power);
/// Gets the currently set transmitter power output level
/// \return Current poer level in dbM
int32_t getTxPower();
/// Set the LoRa Spreading Factor
/// \param[in] sf The spreading factor. Valid values are 64, 128, 256, 512, 1024, 2048, 4096.
void setSpreadingFactor(int32_t sf);
/// Get the LoRa Spreading Factor
/// \return The current Spreading Factor
int32_t getSpreadingFactor();
/// Gets the RSSI of the last received packet
/// \return RSSI of the last received packet
int32_t getRSSI();
/// Gets the Signal To Noise (SNR) of the last received packet
/// \return SNR of the last received packet
int32_t getSNR();
/// Set the receiver Low Noise Amplifier (LNA) gain
/// \param[in] lna LNA gain in dBm
void setLNA(int32_t lna);
/// Get the current LNA gain
/// \return The current LNA gain in dBm
int32_t getLNA();
/// Set the LNA Automatic Gain Control (AGC) enabled
/// \param[in] lnaagc 1 to enable LNA AGC, 0 to disable it
void setLNAAGC(int32_t lnaagc);
/// Set the transmitter and receiver modulation bandwidth
/// \param[in] bw Modulation bandwidth in Hz. Valid values are 7800, 10400, 15600, 20800,
/// 312500, 41700, 62500, 125000, 250000, 500000.
void setBW(int32_t bw);
/// Get the transmitter and receiver modulation bandwidth
/// \return Modulation bandwidth in Hz
int32_t getBW();
/// Enable Cyclic Redundancy Check (CRC) in the transmitter and receiver. If enabled,
/// the transmitter will always appenda CRC to every packet, and the receiver will
/// always check the CRC on received packets, ignoring packets with incorrect CRC
/// \param[in] crc 1 to enable CRC generation and detection, 0 to disable it
void setCRC(uint32_t crc);
protected:
/// Set the current radio state, one of LORA_STATE_*
void setState(uint32_t state);
/// Get the current radio state
uint32_t getState();
private:
// The name of the Unix filesystm port for the Lora SX1278 compatible radio
// typically /dev/loraSPI0.0 or similar
const char* _port;
/// Unix file system device number of the LoRa device port. -1 if not open
int _fd;
/// Last measured SNR, dB
int8_t _lastSNR;
};
/// @example lorafileops_client.cpp
/// @example lorafileops_server.cpp
#endif
#endif

View File

@ -769,7 +769,7 @@ float RH_RF24::get_gpio_voltage(uint8_t gpio)
uint8_t RH_RF24::frr_read(uint8_t reg)
{
uint8_t ret;
uint8_t ret = 0; // Init to prevent compiler warnings
// Do not wait for CTS
ATOMIC_BLOCK_START;

View File

@ -194,9 +194,9 @@ bool RH_RF69::init()
}
// C++ level interrupt handler for this instance
// RH_RF69 is unusual in Mthat it has several interrupt lines, and not a single, combined one.
// RH_RF69 is unusual in that it has several interrupt lines, and not a single, combined one.
// On Moteino, only one of the several interrupt lines (DI0) from the RH_RF69 is connnected to the processor.
// We use this to get PACKETSDENT and PAYLOADRADY interrupts.
// We use the single interrupt line to get PACKETSENT and PAYLOADREADY interrupts.
void RH_RF69::handleInterrupt()
{
// Get the interrupt cause
@ -208,6 +208,7 @@ void RH_RF69::handleInterrupt()
_txGood++;
// Serial.println("PACKETSENT");
}
// Must look for PAYLOADREADY, not CRCOK, since only PAYLOADREADY occurs _after_ AES decryption
// has been done
if (_mode == RHModeRx && (irqflags2 & RH_RF69_IRQFLAGS2_PAYLOADREADY))

View File

@ -43,7 +43,7 @@ RH_RF95::RH_RF95(uint8_t slaveSelectPin, uint8_t interruptPin, RHGenericSPI& spi
{
_interruptPin = interruptPin;
_myInterruptIndex = 0xff; // Not allocated yet
_enableCRC = false;
_enableCRC = true;
_useRFO = false;
}
@ -141,9 +141,9 @@ bool RH_RF95::init()
// setModemConfig(Bw125Cr48Sf4096); // slow and reliable?
setPreambleLength(8); // Default is 8
// An innocuous ISM frequency, same as RF22's
setFrequency(433.775);
setFrequency(434.0);
// Lowish power
setTxPower(20);
setTxPower(13);
return true;
}
@ -185,7 +185,9 @@ void RH_RF95::handleInterrupt()
// timeout
// bad CRC
// CRC is required but it is not present
if (_mode == RHModeRx && ((irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR)) || (_enableCRC && !(hop_channel & RH_RF95_RX_PAYLOAD_CRC_IS_ON)) ))
if (_mode == RHModeRx
&& ( (irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR))
|| (_enableCRC && !(hop_channel & RH_RF95_RX_PAYLOAD_CRC_IS_ON)) ))
// if (_mode == RHModeRx && irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR))
{
// Serial.println("E");
@ -352,6 +354,35 @@ bool RH_RF95::recv(uint8_t* buf, uint8_t* len)
return true;
}
bool RH_RF95::sendAPRS(const uint8_t* data, uint8_t len)
{
if (len > RH_RF95_MAX_MESSAGE_LEN)
return false;
waitPacketSent(); // Make sure we dont interrupt an outgoing message
setModeIdle();
if (!waitCAD())
return false; // Check channel activity
// Position at the beginning of the FIFO
spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, 0);
// The headers for APRS
spiWrite(RH_RF95_REG_00_FIFO, '<');
spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFrom);
spiWrite(RH_RF95_REG_00_FIFO, 0x1 );
//spiWrite(BG_RF95_REG_00_FIFO, _txHeaderFlags);
// The message data
spiBurstWrite(RH_RF95_REG_00_FIFO, data, len);
spiWrite(RH_RF95_REG_22_PAYLOAD_LENGTH, len + RH_RF95_HEADER_LEN -1 ); // only 3 Byte header BG
RH_MUTEX_LOCK(lock); // Multithreading support
setModeTx(); // Start the transmitter
RH_MUTEX_UNLOCK(lock);
// when Tx is done, interruptHandler will fire and radio mode will return to STANDBY
return true;
}
bool RH_RF95::send(const uint8_t* data, uint8_t len)
{
if (len > RH_RF95_MAX_MESSAGE_LEN)
@ -382,35 +413,6 @@ bool RH_RF95::send(const uint8_t* data, uint8_t len)
return true;
}
bool RH_RF95::sendAPRS(const uint8_t* data, uint8_t len)
{
if (len > RH_RF95_MAX_MESSAGE_LEN)
return false;
waitPacketSent(); // Make sure we dont interrupt an outgoing message
setModeIdle();
if (!waitCAD())
return false; // Check channel activity
// Position at the beginning of the FIFO
spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, 0);
// The headers for APRS
spiWrite(RH_RF95_REG_00_FIFO, '<');
spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFrom);
spiWrite(RH_RF95_REG_00_FIFO, 0x1 );
//spiWrite(BG_RF95_REG_00_FIFO, _txHeaderFlags);
// The message data
spiBurstWrite(RH_RF95_REG_00_FIFO, data, len);
spiWrite(RH_RF95_REG_22_PAYLOAD_LENGTH, len + RH_RF95_HEADER_LEN -1 ); // only 3 Byte header BG
RH_MUTEX_LOCK(lock); // Multithreading support
setModeTx(); // Start the transmitter
RH_MUTEX_UNLOCK(lock);
// when Tx is done, interruptHandler will fire and radio mode will return to STANDBY
return true;
}
bool RH_RF95::printRegisters()
{
#ifdef RH_HAVE_SERIAL

View File

@ -10,7 +10,7 @@ It provides a complete object-oriented library for sending and receiving packeti
via a variety of common data radios and other transports on a range of embedded microprocessors.
The version of the package that this documentation refers to can be downloaded
from http://www.airspayce.com/mikem/arduino/RadioHead/RadioHead-1.119.zip
from http://www.airspayce.com/mikem/arduino/RadioHead/RadioHead-1.120.zip
You can find the latest version of the documentation at http://www.airspayce.com/mikem/arduino/RadioHead
You can also find online help and discussion at
@ -106,6 +106,13 @@ and HopeRF RFM95/96/97/98 and other similar LoRa capable radios.
Supports Long Range (LoRa) with spread spectrum frequency hopping, large payloads etc.
FSK/GFSK/OOK modes are not (yet) supported.
Also works with the same chips on Linux using LoRa-file-ops Linux driver ioctls to
transmit and receive RadioHead compatible messages.
Requires a modified version of LoRa-file-ops driver to be installed,
and a compatible radio to be connected appropriately:
https://github.com/starnight/LoRa/tree/file-ops
See the RH_LoRaFileOps class documentation for more information.
- RH_MRF89
Works with Microchip MRF89XA and compatible transceivers.
and modules such as MRF89XAM9A and MRF89XAM8A.
@ -255,7 +262,9 @@ Including Diecimila, Uno, Mega, Leonardo, Yun, Due, Zero etc. http://arduino.cc/
- ESP32 built using Arduino IDE 1.8.9 or later using the ESP32 toolchain installed per
https://github.com/espressif/arduino-esp32
The internal 2.4GHz radio is not yet supported. Tested with RFM22 using SPI interface
The internal 2.4GHz radio is not yet supported. Tested with RFM22 using SPI interface.
Uses the VSPI SPI bus by default.
You can enable use of the alternative HSPI bus for SPI by defining RH_ESP32_USE_HSPI in RadioHead.h.
- Raspberry Pi
Uses BCM2835 library for GPIO http://www.airspayce.com/mikem/bcm2835/
@ -276,6 +285,11 @@ Including Diecimila, Uno, Mega, Leonardo, Yun, Due, Zero etc. http://arduino.cc/
- muRata cmwx1zzabz module, which includes an STM32L0 processor,
a SX1276 LoRa radio and an antenna switch.
- Raspberry Pi Pico, on Arduino, using either the Raspberry Pi Pico/RP2040 core by Earle F. Philhower, version 1.93,
installed per https://arduino-pico.readthedocs.io/_/downloads/en/latest/pdf/
or the alternative Arduino MBED OS RP2040 core version 2.4.1. At this stage support is partial: RH_ASK works
but radio drivers that use interrupts do not (yet) work in either core.
Other platforms are partially supported, such as Generic AVR 8 bit processors, MSP430.
We welcome contributions that will expand the range of supported platforms.
@ -1134,6 +1148,28 @@ k Fix SPI bus speed errors on 8MHz Arduinos.
Changes to RadioHead.h RHHardwareSPI.cpp and RHSPIDriver.cpp for ESP32 support
provided by Juliano Perotto.
\version 1.120 2021-11-13
Added intial support for Raspberry Pi Pico, using the Raspberry Pi Pico/RP2040 core by Earle F. Philhower, version 1.93,
installed per https://arduino-pico.readthedocs.io/_/downloads/en/latest/pdf/
RH_ASK works with default pins (11 and 12). But it seems that interrupt callbacks (needed by most SPI based
radio drivers in RadioHead) are not working (yet) in this core, at least when compiled on our Linux platform.
It appears
there is some bug in std::map that causes the interrupt handling to not work correctly
(which I have been able to reproduce in a simple sketch that only uses std::map). This is very strange.
Interrupts _are_ supported and work in the alternative Arduino MBED OS RP2040 core version 2.4.1,
but there are other mysterious crashes RadioHead when a radio driver reads SPI inside an interrupt.
But RH_ASK also works with this core.
So the result (so far) is partial support of RadioHead using either core (so far).
We will revisit this when either core is updated<br>
On ESP32, added support for using the non-default HSPI bus by defining RH_ESP32_USE_HSPI.<br>
Improved the reading of RSSI for the last packet in RH_CC110: seems that it was being read too soon. Now
read it from the end received packet with RH_CC110_APPEND_STATUS.<br>
Added RH_LoRaFileOps, driver on RPi+Linux and using LoRa-file-ops Linux driver ioctls to
transmit and receive RadioHead compatible messages via SX1276/77/78/79
and compatible radios. Requires a modified version of LoRa-file-ops driver to be installed,
and a compatible radio to be connected appropriately:
https://github.com/starnight/LoRa/tree/file-ops <br>
\author Mike McCauley. DO NOT CONTACT THE AUTHOR DIRECTLY. USE THE GOOGLE GROUP GIVEN ABOVE
*/
@ -1381,7 +1417,7 @@ these examples and explanations and extend them to suit your needs.
// Official version numbers are maintained automatically by Makefile:
#define RH_VERSION_MAJOR 1
#define RH_VERSION_MINOR 119
#define RH_VERSION_MINOR 120
// Symbolic names for currently supported platform types
#define RH_PLATFORM_ARDUINO 1
@ -1404,6 +1440,7 @@ these examples and explanations and extend them to suit your needs.
// Spencer Kondes megaTinyCore:
#define RH_PLATFORM_ATTINY_MEGA 18
#define RH_PLATFORM_STM32L0 19
#define RH_PLATFORM_RASPI_PICO 20
////////////////////////////////////////////////////
// Select platform automatically, if possible
@ -1446,6 +1483,7 @@ these examples and explanations and extend them to suit your needs.
#define RH_PLATFORM RH_PLATFORM_UNIX
#elif defined(__APPLE__) // OSX
#define RH_PLATFORM RH_PLATFORM_UNIX
#else
#error Platform not defined!
#endif
@ -1454,6 +1492,7 @@ these examples and explanations and extend them to suit your needs.
////////////////////////////////////////////////////
// Platform specific headers:
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
#if (ARDUINO >= 100)
#include <Arduino.h>
#else
@ -1467,6 +1506,10 @@ these examples and explanations and extend them to suit your needs.
// There seems to be no way to output text to the USB connection
#undef Serial
#define Serial Serial2
#elif defined(ARDUINO_ARCH_RP2040)
// Raspi Pico
#define RH_ASK_PICO_ALARM_IRQ TIMER_IRQ_1
#define RH_ASK_PICO_ALARM_NUM 1
#endif
#elif (RH_PLATFORM == RH_PLATFORM_ATTINY)
#include <Arduino.h>
@ -1499,6 +1542,19 @@ these examples and explanations and extend them to suit your needs.
#define RH_HAVE_HARDWARE_SPI
#define RH_HAVE_SERIAL
#define RH_MISSING_SPIUSINGINTERRUPT
// ESP32 has 2 user SPI buses: VSPI and HSPI. They are essentially identical, but use different pins.
// The usual, default bus VSPI (available as SPI object in Arduino) uses pins:
// SCLK: 18
// MISO: 19
// MOSI: 23
// SS: 5
// The other HSPI bus uses pins
// SCLK: 14
// MISO: 12
// MOSI: 12
// SS: 15
// By default RadioHead uses VSPI, but you can make it use HSPI by defining this:
//#define RH_ESP32_USE_HSPI
#elif (RH_PLATFORM == RH_PLATFORM_MONGOOSE_OS) // Mongoose OS platform
#include <mgos.h>
@ -1644,8 +1700,15 @@ these examples and explanations and extend them to suit your needs.
#else
#include <util/atomic.h>
#endif
#if defined(ARDUINO_ARCH_MBED_RP2040)
// Standard arduino ATOMIC block crashes on MBED version of Pico as at 2021-08-12
#define ATOMIC_BLOCK_START {
#define ATOMIC_BLOCK_END }
#else
// Most Arduinos
#define ATOMIC_BLOCK_START ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
#define ATOMIC_BLOCK_END }
#endif
#elif (RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE)
// UsingChipKIT Core on Arduino IDE
#define ATOMIC_BLOCK_START unsigned int __status = disableInterrupts(); {

View File

@ -0,0 +1,81 @@
// lorafileops_client.cpp
// -*- mode: C++ -*-
//
// Example program demonstrating how to use
// RH_LoRaFileOps driver on RPi+Linux with a radio supported by
// the LoRa-file-ops Linix driver, such as the SX1278.
// See See https://github.com/starnight/LoRa/tree/file-ops
//
// You can build this in the top level RadioHead directory with something like:
// cd RadioHead
// g++ -o lorafileops_client -I . RH_LoRaFileOps.cpp RHGenericDriver.cpp tools/simMain.cpp examples/lorafileops/lorafileops_client/lorafileops_client.cpp
//
// And run with
// sudo ./lorafileops_client
// (root needed to open /dev/loraSPI0.0
//
// Ensure a RadioHead LoRa compatible receiver is running
// with modem config RH_RF95::Bw125Cr45Sf2048
// eg examples/rf95/rf95_server/rf95_server.pde
// or
// examples/lorafileops/lorafileops_server/lorafileops_server.cpp
#include <RH_LoRaFileOps.h>
// An instance of the RadioHead LoraFileOps driver
RH_LoRaFileOps lora("/dev/loraSPI0.0");
void setup()
{
if (!lora.init())
Serial.println("init failed");
// Defaults after init are:
// Centre frequency 434.0 MHz
// 13dBm transmit power
// Bandwidth 125kHz
// Spreading Factor 2045
// CRC on
// But you can change them all:
//lora.setFrequency(434000000);
lora.setTxPower(17);
//lora.setSpreadingFactor(1024);
//lora.setLNA(10);
//lora.setBW(250000);
}
void loop()
{
Serial.println("Sending to server");
uint8_t data[] = "Hello World!";
lora.send(data, sizeof(data));
lora.waitPacketSent();
// Now wait for a reply
uint8_t buf[RH_LORAFILEOPS_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
if (lora.waitAvailableTimeout(6000, 100))
{
// Should be a reply message for us now
if (lora.recv(buf, &len))
{
Serial.print("got reply: ");
Serial.println((char*)buf);
Serial.print("RSSI: ");
Serial.println(lora.lastRssi(), DEC);
Serial.print("SNR: ");
Serial.println(lora.lastSNR(), DEC);
}
else
{
Serial.println("recv failed");
}
}
else
{
Serial.println("No reply, is server running?");
}
delay(1000);
}

View File

@ -0,0 +1,73 @@
// lorafileops_server.cpp
// -*- mode: C++ -*-
//
// Example program demonstrating how to use
// RH_LoRaFileOps driver on RPi+Linux with a radio supported by
// the LoRa-file-ops Linix driver, such as the SX1278.
// See See https://github.com/starnight/LoRa/tree/file-ops
//
// You can build this in the top level RadioHead directory with something like:
// cd RadioHead
// g++ -o lorafileops_server -I . RH_LoRaFileOps.cpp RHGenericDriver.cpp tools/simMain.cpp examples/lorafileops/lorafileops_server/lorafileops_server.cpp
//
// And run with
// sudo ./lorafileops_server
// (root needed to open /dev/loraSPI0.0
//
// Ensure a RadioHead LoRa compatible transmitter is running
// with modem config RH_RF95::Bw125Cr45Sf2048
// eg examples/rf95/rf95_client/rf95_client.pde
// or
// examples/lorafileops/lorafileops_client/lorafileops_client.cpp
#include <RH_LoRaFileOps.h>
// An instance of the RadioHead LoraFileOps driver
RH_LoRaFileOps lora("/dev/loraSPI0.0");
void setup()
{
if (!lora.init())
Serial.println("init failed");
// Defaults after init are:
// Centre frequency 434.0 MHz
// 13dBm transmit power
// Bandwidth 125kHz
// Spreading Factor 2045
// CRC on
// But you can change them all:
//lora.setFrequency(434000000);
lora.setTxPower(17);
//lora.setSpreadingFactor(1024);
//lora.setLNA(10);
//lora.setBW(250000);
}
void loop()
{
lora.waitAvailable(100);
// Should be a message for us now
uint8_t buf[RH_LORAFILEOPS_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
if (lora.recv(buf, &len))
{
RH_LoRaFileOps::printBuffer("request: ", buf, len);
Serial.print("got request: ");
Serial.println((char*)buf);
Serial.print("RSSI: ");
Serial.println(lora.lastRssi(), DEC);
// Send a reply
uint8_t data[] = "And hello back to you";
lora.send(data, sizeof(data)); // Returns when packet fully transmitted
Serial.println("Sent a reply");
}
else
{
Serial.println("recv failed");
}
}

View File

@ -1,5 +1,5 @@
name=RadioHead
version=1.119
version=1.120
author=Mike McCauley <mikem@airspayce.com>
maintainer=Mike McCauley <mikem@airspayce.com>
sentence= Packet Radio library for embedded microprocessors

Binary file not shown.

Binary file not shown.

View File

@ -24,17 +24,18 @@ lib_deps =
Adafruit SSD1306
Adafruit GFX Library
Adafruit Unified Sensor
adafruit/Adafruit BMP280 Library @ 2.4.0
Adafruit BusIO @ !=1.9.7 # due to https://github.com/adafruit/Adafruit_BusIO/pull/70
https://github.com/SQ9MDD/AXP202X_Library.git
SparkFun u-blox Arduino Library
bblanchon/ArduinoJson
build_flags =
-Wl,--gc-sections,--relax
-D 'KISS_PROTOCOL' ; leave enabled
-D 'CALLSIGN="N0CALL-0"' ; can be set from www interface
-D 'CALLSIGN="N0CALL"' ; can be set from www interface
-D 'DIGI_PATH="WIDE1-1"' ; can be set from www interface
-D 'FIXED_BEACON_EN' ; can be set from www interface
-D 'LATIDUDE_PRESET="0000.00N"' ; can be set from www interface
; -D 'FIXED_BEACON_EN' ; can be set from www interface
-D 'SB_ALGO_KENWOOD' ; Kenwood skales better on lower speed.
-D 'LATITUDE_PRESET="0000.00N"' ; can be set from www interface
-D 'LONGITUDE_PRESET="00000.00E"' ; can be set from www interface
-D 'APRS_SYMBOL_TABLE="/"' ; can be set from www interface
-D 'APRS_SYMBOL="["' ; can be set from www interface
@ -44,33 +45,34 @@ build_flags =
-D 'SHOW_RX_PACKET' ; can be set from www interface
-D 'SHOW_RX_TIME=10000' ; can be set from www interface
-D 'TXFREQ=433.775' ; set operating frequency
-D 'SPEED_1200' ; comment out to set 300baud
; -D 'LORA_SPEED_1200' ; comment out to set 300baud
; -D 'TXDISABLE' ; can be set from www interface
-D 'TXdbmW=20' ; set power
-D 'ENABLE_OLED' ; can be set from www interface
-D 'ENABLE_LED_SIGNALING' ; can be set from www interface
-D 'NETWORK_TNC_PORT=8001' ; default KISS TCP port
-D 'MAX_TIME_TO_NEXT_TX=120000L' ; can be set from www interface
-D 'MAX_TIME_TO_NEXT_TX=120000L' ; can be set from www interface -- not implemented
-D 'FIX_BEACON_INTERVAL=1800000L' ; can be set from www interface
-D 'NETWORK_GPS_PORT=10110' ; GPS NMEA Port
-D 'ENABLE_TNC_SELF_TELEMETRY' ; can be set from www interface
-D 'TNC_SELF_TELEMETRY_INTERVAL=3600L' ; can be set from www interface (seconds)
-D 'SHOW_OLED_TIME=15000' ; OLED Timeout
-D 'SHOW_OLED_TIME=15000' ; can be set from www interface (OLED Timeout)
; -D 'TX_RX_LNA' ; Set external pins for 1W modules
; -D 'USE_BME280' ; BMP280 Temp sensor for simple WX station
; -D 'HEIGTH_PRESET=34'
; -D 'RSSI_SNR_REPORT' ; Experimental show RSSI/SNR in KISS packet (OE Mode)
[env:ttgo-t-beam-v1.0]
platform = espressif32 @ 3.4.0
platform = espressif32 @ 3.5.0
board = ttgo-t-beam
build_flags =
${env.build_flags}
-D T_BEAM_V1_0
-D ENABLE_WIFI
; -D ENABLE_BLUETOOTH
-D ENABLE_BLUETOOTH
[env:ttgo-t-beam-v0.7]
platform = espressif32 @ 3.4.0
platform = espressif32 @ 3.5.0
board = ttgo-t-beam
build_flags =
${env.build_flags}
@ -79,7 +81,7 @@ build_flags =
; -D ENABLE_BLUETOOTH
[env:ttgo-lora32-v2.1]
platform = espressif32 @ 3.4.0
platform = espressif32 @ 3.5.0
board = ttgo-lora32-v21
build_flags =
${env.build_flags}
@ -88,7 +90,7 @@ build_flags =
; -D ENABLE_BLUETOOTH
[env:ttgo-lora32-v2]
platform = espressif32 @ 3.4.0
platform = espressif32 @ 3.5.0
board = ttgo-lora32-v2
build_flags =
${env.build_flags}
@ -97,7 +99,7 @@ build_flags =
; -D ENABLE_BLUETOOTH
[env:ttgo-lora32-v1]
platform = espressif32 @ 3.4.0
platform = espressif32 @ 3.5.0
board = ttgo-lora32-v1
build_flags =
${env.build_flags}
@ -106,7 +108,7 @@ build_flags =
; -D ENABLE_BLUETOOTH
[env:Heltec-WiFi-v1]
platform = espressif32 @ 3.4.0
platform = espressif32 @ 3.5.0
board = heltec_wifi_kit_32
build_flags =
${env.build_flags}
@ -115,7 +117,7 @@ build_flags =
; -D ENABLE_BLUETOOTH
[env:Heltec-WiFi-v2]
platform = espressif32 @ 3.4.0
platform = espressif32 @ 3.5.0
board = heltec_wifi_kit_32_v2
build_flags =
${env.build_flags}
@ -124,7 +126,7 @@ build_flags =
; -D ENABLE_BLUETOOTH
[env:Esp32-Dev-v1]
platform = espressif32 @ 3.4.0
platform = espressif32 @ 3.5.0
board = esp32dev
; change MCU frequency
;board_build.f_cpu = 80000000L
@ -135,7 +137,7 @@ build_flags =
; -D ENABLE_BLUETOOTH
[env:ttgo-t-beam-v1.0-development]
platform = espressif32 @ 3.4.0
platform = espressif32 @ 3.5.0
board = ttgo-t-beam
build_flags =
${env.build_flags}

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,20 @@ extern String infoApName;
extern String infoApPass;
extern String infoApAddr;
// For APRS-IS connection
extern String to_aprsis_data;
extern boolean aprsis_enabled;
extern String aprsis_host;
extern uint16_t aprsis_port;
extern String aprsis_filter;
extern String aprsis_callsign;
extern String aprsis_password;
extern boolean aprsis_data_allow_inet_to_rf;
extern double lora_freq_rx_curr;
extern boolean lora_tx_enabled;
QueueHandle_t webListReceivedQueue = nullptr;
std::list <tReceivedPacketData*> receivedPackets;
const int MAX_RECEIVED_LIST_SIZE = 50;
@ -34,6 +48,19 @@ String apSSID = "";
String apPassword;
String defApPassword = "xxxxxxxxxx";
// needed for aprsis igate functionality
String aprsis_status = "Disconnected";
// aprsis 3rd party traffic encoding
String generate_third_party_packet(String, String);
#ifdef KISS_PROTOCOL
extern void sendToTNC(const String &);
#endif
extern uint8_t txPower;
extern double lora_freq;
extern ulong lora_speed;
extern void loraSend(byte, float, ulong, const String &);
WebServer server(80);
#ifdef KISS_PROTOCOL
WiFiServer tncServer(NETWORK_TNC_PORT);
@ -77,7 +104,7 @@ String jsonLineFromPreferenceInt(const char *preferenceName, bool last=false){
return String("\"") + preferenceName + "\":" + (preferences.getInt(preferenceName)) + (last ? + R"()" : + R"(,)");
}
String jsonLineFromPreferenceDouble(const char *preferenceName, bool last=false){
return String("\"") + preferenceName + "\":" + String(preferences.getDouble(preferenceName),3) + (last ? + R"()" : + R"(,)");
return String("\"") + preferenceName + "\":" + String(preferences.getDouble(preferenceName),4) + (last ? + R"()" : + R"(,)");
}
String jsonLineFromString(const char *name, const char *value, bool last=false){
return String("\"") + name + "\":\"" + jsonEscape(value) + "\"" + (last ? + R"()" : + R"(,)");
@ -85,6 +112,9 @@ String jsonLineFromString(const char *name, const char *value, bool last=false){
String jsonLineFromInt(const char *name, const int value, bool last=false){
return String("\"") + name + "\":" + String(value) + (last ? + R"()" : + R"(,)");
}
String jsonLineFromDouble(const char *name, const double value, bool last=false){
return String("\"") + name + "\":" + String(value, 4) + (last ? + R"()" : + R"(,)");
}
void handle_NotFound(){
sendCacheHeader();
@ -195,7 +225,18 @@ void handle_Cfg() {
jsonData += jsonLineFromPreferenceString(PREF_WIFI_SSID);
jsonData += jsonLineFromPreferenceDouble(PREF_LORA_FREQ_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_SPEED_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_LORA_TX_ENABLE);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_TX_POWER);
jsonData += jsonLineFromPreferenceBool(PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_APRS_DIGIPEATING_MODE_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET);
jsonData += jsonLineFromPreferenceDouble(PREF_LORA_FREQ_CROSSDIGI_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_SPEED_CROSSDIGI_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_TX_POWER_CROSSDIGI_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_RX_ON_FREQUENCIES_PRESET);
jsonData += jsonLineFromPreferenceString(PREF_APRS_CALLSIGN);
jsonData += jsonLineFromPreferenceString(PREF_APRS_RELAY_PATH);
jsonData += jsonLineFromPreferenceString(PREF_APRS_SYMBOL_TABLE);
@ -209,21 +250,35 @@ void handle_Cfg() {
jsonData += jsonLineFromPreferenceInt(PREF_APRS_SB_MIN_SPEED_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_APRS_SB_MAX_SPEED_PRESET);
jsonData += jsonLineFromPreferenceDouble(PREF_APRS_SB_ANGLE_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_APRS_SB_TURN_SLOPE_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_APRS_SB_TURN_TIME_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_SHOW_BATTERY);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_FIXED_BEACON_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_SHOW_ALTITUDE);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_GPS_EN);
jsonData += jsonLineFromPreferenceBool(PREF_ACCEPT_OWN_POSITION_REPORTS_VIA_KISS);
jsonData += jsonLineFromPreferenceBool(PREF_GPS_ALLOW_SLEEP_WHILE_KISS);
jsonData += jsonLineFromPreferenceBool(PREF_ENABLE_TNC_SELF_TELEMETRY);
jsonData += jsonLineFromPreferenceInt(PREF_TNC_SELF_TELEMETRY_INTERVAL);
jsonData += jsonLineFromPreferenceInt(PREF_TNC_SELF_TELEMETRY_MIC);
jsonData += jsonLineFromPreferenceString(PREF_TNC_SELF_TELEMETRY_PATH);
jsonData += jsonLineFromPreferenceBool(PREF_DEV_OL_EN);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_SHOW_CMT);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_COMMENT_RATELIMIT_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_DEV_BT_EN);
jsonData += jsonLineFromPreferenceInt(PREF_DEV_SHOW_RX_TIME);
jsonData += jsonLineFromPreferenceBool(PREF_DEV_AUTO_SHUT);
jsonData += jsonLineFromPreferenceInt(PREF_DEV_AUTO_SHUT_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_DEV_SHOW_OLED_TIME);
jsonData += jsonLineFromPreferenceBool(PREF_APRSIS_EN);
jsonData += jsonLineFromPreferenceString(PREF_APRSIS_SERVER_NAME);
jsonData += jsonLineFromPreferenceInt(PREF_APRSIS_SERVER_PORT);
jsonData += jsonLineFromPreferenceString(PREF_APRSIS_FILTER);
jsonData += jsonLineFromPreferenceString(PREF_APRSIS_CALLSIGN);
jsonData += jsonLineFromPreferenceString(PREF_APRSIS_PASSWORD);
jsonData += jsonLineFromPreferenceBool(PREF_APRSIS_ALLOW_INET_TO_RF);
jsonData += jsonLineFromDouble("lora_freq_rx_curr", lora_freq_rx_curr);
jsonData += jsonLineFromString("aprsis_status", aprsis_status.c_str());
jsonData += jsonLineFromInt("FreeHeap", ESP.getFreeHeap());
jsonData += jsonLineFromInt("HeapSize", ESP.getHeapSize());
jsonData += jsonLineFromInt("FreeSketchSpace", ESP.getFreeSketchSpace());
@ -241,12 +296,12 @@ void handle_ReceivedList() {
auto received = root.createNestedArray("received");
for (auto element: receivedPackets){
char buf[64];
strftime(buf, 64, "%Y.%m.%d %H:%M:%S", &element->rxTime);
strftime(buf, 64, "%Y-%m-%d %H:%M:%S", &element->rxTime);
auto packet_data = received.createNestedObject();
packet_data["time"] = String(buf);
packet_data["packet"] = element->packet->c_str();
packet_data["rssi"] = element->RSSI;
packet_data["snr"] = element->SNR / 10.0f;
packet_data["snr"] = element->SNR;
}
server.send(200,"application/json", doc.as<String>());
@ -261,9 +316,37 @@ void handle_SaveAPRSCfg() {
if (server.hasArg(PREF_LORA_SPEED_PRESET)){
preferences.putInt(PREF_LORA_SPEED_PRESET, server.arg(PREF_LORA_SPEED_PRESET).toInt());
}
preferences.putBool(PREF_LORA_TX_ENABLE, server.hasArg(PREF_LORA_TX_ENABLE));
if (server.hasArg(PREF_LORA_TX_POWER)) {
preferences.putInt(PREF_LORA_TX_POWER, server.arg(PREF_LORA_TX_POWER).toInt());
}
preferences.putBool(PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET, server.hasArg(PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET));
if (server.hasArg(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET)){
preferences.putInt(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET, server.arg(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET).toInt());
}
preferences.putBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET, server.hasArg(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET));
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_CROSS_DIGIPEATING_MODE_PRESET)){
preferences.putInt(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET, server.arg(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET).toInt());
}
if (server.hasArg(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET)) {
preferences.putInt(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET, server.arg(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET).toInt());
}
if (server.hasArg(PREF_LORA_FREQ_CROSSDIGI_PRESET)){
preferences.putDouble(PREF_LORA_FREQ_CROSSDIGI_PRESET, server.arg(PREF_LORA_FREQ_CROSSDIGI_PRESET).toDouble());
Serial.printf("FREQ crossdigi saved:\t%f\n", server.arg(PREF_LORA_FREQ_CROSSDIGI_PRESET).toDouble());
}
if (server.hasArg(PREF_LORA_SPEED_CROSSDIGI_PRESET)){
preferences.putInt(PREF_LORA_SPEED_CROSSDIGI_PRESET, server.arg(PREF_LORA_SPEED_CROSSDIGI_PRESET).toInt());
}
if (server.hasArg(PREF_LORA_TX_POWER_CROSSDIGI_PRESET)) {
preferences.putInt(PREF_LORA_TX_POWER_CROSSDIGI_PRESET, server.arg(PREF_LORA_TX_POWER_CROSSDIGI_PRESET).toInt());
}
if (server.hasArg(PREF_LORA_RX_ON_FREQUENCIES_PRESET)) {
preferences.putInt(PREF_LORA_RX_ON_FREQUENCIES_PRESET, server.arg(PREF_LORA_RX_ON_FREQUENCIES_PRESET).toInt());
}
// APRS station settings
if (server.hasArg(PREF_APRS_CALLSIGN) && !server.arg(PREF_APRS_CALLSIGN).isEmpty()){
preferences.putString(PREF_APRS_CALLSIGN, server.arg(PREF_APRS_CALLSIGN));
@ -304,17 +387,47 @@ void handle_SaveAPRSCfg() {
preferences.putInt(PREF_APRS_SB_MIN_INTERVAL_PRESET, server.arg(PREF_APRS_SB_MIN_INTERVAL_PRESET).toInt());
}
if (server.hasArg(PREF_APRS_SB_MAX_INTERVAL_PRESET)){
preferences.putInt(PREF_APRS_SB_MAX_INTERVAL_PRESET, server.arg(PREF_APRS_SB_MAX_INTERVAL_PRESET).toInt());
//preferences.putInt(PREF_APRS_SB_MAX_INTERVAL_PRESET, server.arg(PREF_APRS_SB_MAX_INTERVAL_PRESET).toInt());
int i = server.arg(PREF_APRS_SB_MAX_INTERVAL_PRESET).toInt();
if (i <= preferences.getInt(PREF_APRS_SB_MIN_INTERVAL_PRESET)) i = preferences.getInt(PREF_APRS_SB_MIN_INTERVAL_PRESET) +1;
preferences.putInt(PREF_APRS_SB_MAX_INTERVAL_PRESET, i);
}
if (server.hasArg(PREF_APRS_SB_MIN_SPEED_PRESET)){
preferences.putInt(PREF_APRS_SB_MIN_SPEED_PRESET, server.arg(PREF_APRS_SB_MIN_SPEED_PRESET).toInt());
}
if (server.hasArg(PREF_APRS_SB_MAX_SPEED_PRESET)){
preferences.putInt(PREF_APRS_SB_MAX_SPEED_PRESET, server.arg(PREF_APRS_SB_MAX_SPEED_PRESET).toInt());
//preferences.putInt(PREF_APRS_SB_MAX_SPEED_PRESET, server.arg(PREF_APRS_SB_MAX_SPEED_PRESET).toInt());
int i = server.arg(PREF_APRS_SB_MAX_SPEED_PRESET).toInt();
if (i <= preferences.getInt(PREF_APRS_SB_MIN_SPEED_PRESET)) i = preferences.getInt(PREF_APRS_SB_MIN_SPEED_PRESET) +1;
preferences.putInt(PREF_APRS_SB_MAX_SPEED_PRESET, i);
}
if (server.hasArg(PREF_APRS_SB_ANGLE_PRESET)){
preferences.putDouble(PREF_APRS_SB_ANGLE_PRESET, server.arg(PREF_APRS_SB_ANGLE_PRESET).toDouble());
}
if (server.hasArg(PREF_APRS_SB_TURN_SLOPE_PRESET)){
preferences.putInt(PREF_APRS_SB_TURN_SLOPE_PRESET, server.arg(PREF_APRS_SB_TURN_SLOPE_PRESET).toInt());
}
if (server.hasArg(PREF_APRS_SB_TURN_TIME_PRESET)){
preferences.putInt(PREF_APRS_SB_TURN_TIME_PRESET, server.arg(PREF_APRS_SB_TURN_TIME_PRESET).toInt());
}
preferences.putBool(PREF_APRSIS_EN, server.hasArg(PREF_APRSIS_EN));
if (server.hasArg(PREF_APRSIS_SERVER_NAME)){
preferences.putString(PREF_APRSIS_SERVER_NAME, server.arg(PREF_APRSIS_SERVER_NAME));
}
if (server.hasArg(PREF_APRSIS_SERVER_PORT)){
preferences.putInt(PREF_APRSIS_SERVER_PORT, server.arg(PREF_APRSIS_SERVER_PORT).toInt());
}
if (server.hasArg(PREF_APRSIS_FILTER)){
preferences.putString(PREF_APRSIS_FILTER, server.arg(PREF_APRSIS_FILTER));
}
if (server.hasArg(PREF_APRSIS_CALLSIGN)){
preferences.putString(PREF_APRSIS_CALLSIGN, server.arg(PREF_APRSIS_CALLSIGN));
}
if (server.hasArg(PREF_APRSIS_PASSWORD)){
preferences.putString(PREF_APRSIS_PASSWORD, server.arg(PREF_APRSIS_PASSWORD));
}
preferences.putBool(PREF_APRSIS_ALLOW_INET_TO_RF, server.hasArg(PREF_APRSIS_ALLOW_INET_TO_RF));
preferences.putBool(PREF_APRS_SHOW_BATTERY, server.hasArg(PREF_APRS_SHOW_BATTERY));
@ -322,10 +435,14 @@ void handle_SaveAPRSCfg() {
preferences.putBool(PREF_APRS_SHOW_ALTITUDE, server.hasArg(PREF_APRS_SHOW_ALTITUDE));
preferences.putBool(PREF_APRS_FIXED_BEACON_PRESET, server.hasArg(PREF_APRS_FIXED_BEACON_PRESET));
preferences.putBool(PREF_APRS_GPS_EN, server.hasArg(PREF_APRS_GPS_EN));
preferences.putBool(PREF_ACCEPT_OWN_POSITION_REPORTS_VIA_KISS, server.hasArg(PREF_ACCEPT_OWN_POSITION_REPORTS_VIA_KISS));
preferences.putBool(PREF_GPS_ALLOW_SLEEP_WHILE_KISS, server.hasArg(PREF_GPS_ALLOW_SLEEP_WHILE_KISS));
preferences.putBool(PREF_APRS_SHOW_CMT, server.hasArg(PREF_APRS_SHOW_CMT));
preferences.putBool(PREF_APRS_COMMENT_RATELIMIT_PRESET, server.hasArg(PREF_APRS_COMMENT_RATELIMIT_PRESET));
server.sendHeader("Location", "/");
server.send(302,"text/html", "");
}
void handle_saveDeviceCfg(){
@ -413,7 +530,7 @@ void handle_saveDeviceCfg(){
Serial.println("Connecting to " + wifi_ssid);
// Set power to minimum (max 20)
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html
esp_wifi_set_max_tx_power(8);
esp_wifi_set_max_tx_power(80);
while (WiFi.status() != WL_CONNECTED) {
Serial.print("Not connected: ");
Serial.println((int)WiFi.status());
@ -430,7 +547,7 @@ void handle_saveDeviceCfg(){
Serial.print(" Password: ");
Serial.println(apPassword.c_str());
// Set power to minimum (max 20)
esp_wifi_set_max_tx_power(8);
esp_wifi_set_max_tx_power(80);
break;
}
}
@ -493,6 +610,38 @@ void handle_saveDeviceCfg(){
tReceivedPacketData *receivedPacketData = nullptr;
WiFiClient aprs_is_client;
uint32_t t_connect_apprsis_again = 0L;
String aprs_callsign = webServerCfg->callsign;
aprsis_host.trim();
aprsis_filter.trim();
aprsis_password.trim();
if (aprsis_callsign.length() < 3 || aprsis_callsign.length() > 9)
aprsis_callsign = "";
if (aprsis_callsign.isEmpty()) aprsis_callsign = aprs_callsign;
if (aprsis_callsign.length() < 3 || aprsis_callsign.length() > 9)
aprsis_callsign = "";
aprsis_callsign.toUpperCase(); aprsis_callsign.trim();
// sanity check
if (aprsis_enabled) {
if (aprsis_callsign.isEmpty() || aprsis_host.isEmpty() || aprsis_port == 0) {
aprsis_enabled = false;
} else {
const char *p = aprsis_callsign.c_str();
for (; *p && aprsis_enabled; p++) {
if (*p == '-') {
int len = strlen(p+1);
if (len < 1 || len > 2)
aprsis_enabled = false;
} else if ( !((*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')) )
aprsis_enabled = false;
}
}
}
while (true){
server.handleClient();
if (xQueueReceive(webListReceivedQueue, &receivedPacketData, (1 / portTICK_PERIOD_MS)) == pdPASS) {
@ -513,6 +662,164 @@ void handle_saveDeviceCfg(){
delete receivedPacketData;
}
if (aprsis_enabled) {
boolean err = true;
if (WiFi.getMode() == 1) {
if (WiFi.status() != WL_CONNECTED) { aprsis_status = "Error: no internet"; goto on_err; } else { if (aprsis_status == "Error: no internet") aprsis_status = "Internet available"; }
if (!aprs_is_client.connected() && t_connect_apprsis_again < millis()) {
aprsis_status = "Connecting";
aprs_is_client.connect(aprsis_host.c_str(), aprsis_port);
if (!aprs_is_client.connected()) { aprsis_status = "Error: connect failed"; goto on_err; }
aprsis_status = "Connected. Waiting for greeting.";
uint32_t t_start = millis();
while (!aprs_is_client.available() && (millis()-t_start) < 25000) delay(100);
if (aprs_is_client.available()) {
// check
String s = aprs_is_client.readStringUntil('\n');
if (s.isEmpty() || !s.startsWith("#")) { aprsis_status = "Error: unexpected greeting"; goto on_err; }
} else { aprsis_status = "Error: No response"; goto on_err; }
aprsis_status = "Login";
char buffer[1024];
sprintf(buffer, "user %s pass %s TTGO-T-Beam-LoRa-APRS 0.1%s%s\r\n", aprsis_callsign.c_str(), aprsis_password.c_str(), aprsis_filter.isEmpty() ? " filter " : "", aprsis_filter.isEmpty() ? aprsis_filter.c_str() : "");
aprs_is_client.print(String(buffer));
t_start = millis();
while (!aprs_is_client.available() && (millis()-t_start) < 25000) delay(100);
aprsis_status = "Logged in";
if (aprs_is_client.available()) {
// check
String s = aprs_is_client.readStringUntil('\n');
if (s.isEmpty() || !s.startsWith("#")) { aprsis_status = "Error: unexpected reponse on login"; goto on_err; }
if (s.indexOf(" logresp") == -1) { aprsis_status = "Error: Login denied: " + s; aprsis_status.trim(); goto on_err; }
if (s.indexOf(" verified") == -1) { aprsis_status = "Notice: server responsed not verified: " + s; aprsis_status.trim(); }
} else { aprsis_status = "Error: No response"; goto on_err; }
}
if (!aprs_is_client.connected())
goto on_err;
//aprsis_status = "OK";
if (aprs_is_client.available()) {
//aprsis_status = "OK, reading";
String s = aprs_is_client.readStringUntil('\n');
if (!s) goto on_err;
//aprsis_status = "OK";
s.trim();
if (s.isEmpty()) goto on_err;
if (*(s.c_str()) == '#' || !isalnum(*(s.c_str()))) goto do_not_send;
char *header_end = strchr(s.c_str(), ':');
if (!header_end) goto do_not_send;
char *src_call_end = strchr(s.c_str(), '>');
if (!src_call_end) goto do_not_send;
if (src_call_end > header_end-2) goto do_not_send;
char *q = strchr(s.c_str(), '-');
if (q && q < src_call_end) {
// len callsign > 6?
if (q-s.c_str() > 6) goto do_not_send;
// SSID optional, only 0..15
if (q[2] == '>') {
if (q[1] < '0' || q[1] > '9') goto do_not_send;
} else if (q[3] == '>') {
if (q[1] != '1' || q[2] < '0' || q[2] > '5') goto do_not_send;
} else goto do_not_send;
} else {
if (src_call_end-s.c_str() > 6) goto do_not_send;
}
// do not interprete packets coming back from aprs-is net (either our source call, or if we have repeated it with one of our calls
// sender is our call?
if (s.startsWith(aprs_callsign + '>') || s.startsWith(aprsis_callsign + '>')) goto do_not_send;
// packet has our call in i.E. ...,qAR,OURCALL:...
q = strstr(s.c_str(), (',' + aprsis_callsign + ':').c_str());
if (q && q < header_end) goto do_not_send;
for (int i = 0; i < 2; i++) {
String call = (i == 0 ? aprs_callsign : aprsis_callsign);
// digipeated frames look like "..,DL9SAU,DL1AAA,DL1BBB*,...", or "..,DL9SAU*,DL1AAA,DL1BBB,.."
if (((q = strstr(s.c_str(), (',' + call + '*').c_str())) || (q = strstr(s.c_str(), (',' + call + ',').c_str()))) && q < header_end) {
char *digipeatedflag = strchr(q, '*');
if (digipeatedflag && digipeatedflag < header_end && digipeatedflag > q)
goto do_not_send;
}
}
// 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()) {
#ifdef KISS_PROTOCOL
sendToTNC(third_party_packet);
#endif
if (lora_tx_enabled && aprsis_data_allow_inet_to_rf) {
// not query or aprs-message addressed to our call (check both, aprs_callsign and aprsis_callsign)=
// Format: "..::DL9SAU-15:..."
// check is in this code part, because we may like to see those packets via kiss (sent above)
q = header_end + 1;
if (*q == ':' && strlen(q) > 10 && q[10] == ':' &&
((!strncmp(q+1, aprs_callsign.c_str(), aprs_callsign.length()) && (aprs_callsign.length() == 9 || q[9] == ' ')) ||
(!strncmp(q+1, aprsis_callsign.c_str(), aprsis_callsign.length()) && (aprsis_callsign.length() == 9 || q[9] == ' ')) ))
goto do_not_send;
loraSend(txPower, lora_freq, lora_speed, third_party_packet);
}
}
}
do_not_send:
if (to_aprsis_data) {
// copy(). We are threaded..
String data = String(to_aprsis_data);
// clear queue
to_aprsis_data = "";
data.trim();
char *p = strchr(data.c_str(), '>');
char *q;
// some plausibility checks.
if (p && p > data.c_str() && (q = strchr(p+1, ':'))) {
// Due to http://www.aprs-is.net/IGateDetails.aspx , never gate third-party traffic contining TCPIP or TCPXX
// IGATECALL>APRS,GATEPATH:}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data
char *r; char *s;
if (!(q[1] == '}' && (r = strchr(q+2, '>')) && ((s = strstr(r+1, ",TCPIP,")) || (s = strstr(r+1, ",TCPXX,"))) && strstr(s+6, "*:"))) {
char buf[256];
int len = (q-data.c_str());
if (len > 0 && len < sizeof(buf)) {
strncpy(buf, data.c_str(), len);
buf[len] = 0;
String s_data = String(buf) + (lora_tx_enabled ? ",qAR," : ",qAO,") + aprsis_callsign + q + "\r\n";
aprsis_status = "OK, sending: " + s_data; aprsis_status.trim();
aprs_is_client.print(s_data);
}
}
}
//aprsis_status = "OK";
}
err = false;
if (err) {
on_err:
aprs_is_client.stop();
if (!aprsis_status.startsWith("Error: "))
aprsis_status = "Disconnected";
if (t_connect_apprsis_again <= millis())
t_connect_apprsis_again = millis() + 60000;
to_aprsis_data = "";
}
}
}
vTaskDelay(5/portTICK_PERIOD_MS);
}
}
String generate_third_party_packet(String callsign, String packet_in)
{
String packet_out = "";
const char *s = packet_in.c_str();
char *p = strchr(s, '>');
char *q = strchr(s, ',');
char *r = strchr(s, ':');
char fromtodest[20]; // room for max (due to spec) 'DL9SAU-15>APRSXX-NN' + \0
if (p > s && p < q && q < r && (q-s) < sizeof(fromtodest)) {
r++;
strncpy(fromtodest, s, q-s);
fromtodest[(q-s)] = 0;
packet_out = callsign + ">APRS:}" + fromtodest + ",TCPIP," + callsign + "*:" + r;
// ^ 3rd party traffic should be addressed directly (-> not to WIDE2-1 or so)
}
return packet_out;
}

View File

@ -1,6 +1,6 @@
FILENAME_BUILDNO = '.pio/versioning'
FILENAME_VERSION_H = 'include/version.h'
version = 'v0.4.6.'
version = 'v0.4.7.'
import datetime
from subprocess import *