Merge branch 'dl9rdz:devel' into displayvolts

pull/153/head
eben80 2021-09-09 09:40:24 +02:00 zatwierdzone przez GitHub
commit ec660beeb1
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
38 zmienionych plików z 7792 dodań i 438 usunięć

Wyświetl plik

@ -1,7 +1,7 @@
language: c
env:
global:
- ESP32TOOLS=/home/travis/.arduino15/packages/esp32/hardware/esp32/1.0.5/tools
- ESP32TOOLS=/home/travis/.arduino15/packages/esp32/hardware/esp32/1.0.6/tools
- MKSPIFFS=/home/travis/.arduino15/packages/esp32/tools/mkspiffs/0.2.3/mkspiffs
before_install:
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16"
@ -20,9 +20,9 @@ before_install:
- rm master.zip
- sudo mv AsyncTCP-master /usr/local/share/arduino/libraries/AsyncTCP
- wget https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/download/1.0/ESP32FS-1.0.zip
- wget https://github.com/lewisxhe/AXP202X_Library/archive/v1.0.zip
- unzip v1.0.zip
- sudo mv AXP202X_Library-1.0 /usr/local/share/arduino/libraries/
- wget https://github.com/lewisxhe/AXP202X_Library/archive/refs/tags/V1.1.3.zip
- unzip V1.1.3.zip
- sudo mv AXP202X_Library-1.1.3 /usr/local/share/arduino/libraries/
- wget https://github.com/dx168b/async-mqtt-client/archive/master.zip
- unzip master.zip
@ -45,10 +45,11 @@ install:
- arduino --pref "custom_FlashFreq=ttgo-lora32-v1_80" --save-prefs
- mkdir -p $PWD/build
- arduino --pref "build.path=$PWD/build" --save-prefs
- arduino --install-boards esp32:esp32 --save-prefs
- arduino --install-boards esp32:esp32:1.0.6 --save-prefs
- ln -s $PWD/libraries/SondeLib /usr/local/share/arduino/libraries/SondeLib
- arduino --install-library "U8g2"
- arduino --install-library "MicroNMEA"
- arduino --install-library "GFX Library for Arduino"
script:
- arduino --board esp32:esp32:t-beam --verify $PWD/RX_FSK/RX_FSK.ino
- find build

Wyświetl plik

@ -59,8 +59,10 @@ commit_website_files() {
git add ${BRANCH}/${VERSION}-full.bin
cp ${MYPATH}/build/RX_FSK.ino.bin ${BRANCH}/update.ino.bin
git add ${BRANCH}/update.ino.bin
echo "${TRAVIS_COMMIT_MESSAGE}" > ${BRANCH}/${VERSION}-changelog.txt
echo "${TRAVIS_COMMIT_MESSAGE}" >> ${BRANCH}/${VERSION}-changelog.txt
git add ${BRANCH}/${VERSION}-changelog.txt
echo "<html><body><p>${VERSION}</p></body></html>" > ${BRANCH}/update-info.html
git add ${BRANCH}/update-info.html
git commit --message "Travis build: $TRAVIS_BUILD_NUMBER"
}
upload_files() {

Wyświetl plik

@ -1,11 +1,66 @@
RDZ_TTGO_SONDE
==============
rdzTTGOsonde
============
This a decoder for radiosonde RS41, RS92, DFM06/09/17, M10/M20, and MP3H
based on a TTGO LoRa ESP32 board.
It supports OLED displays (SSD1306, SH1106) and TFT displays (ILI9225).
It also supports feeding data to external applications using WiFi (NOT bluetooth):
- Arduino app by dl9rdz (see https://github.com/dl9rdz/rdzwx-go for apk download)
- AXUDP (for aprsmap application by oe5dxl, among others)
- KISS TNC (aprs format, mainly useful for APRSdroid app)
- MQTT
- SondeHub tracker (experimental)
This a simple, experimental decoder for radiosonde RS41, RS92, DFM06/09/17 and M10/M20 on
a TTGO LoRa ESP32 board with either a OLED or extern TFT display.
Please consult the Wiki at https://github.com/dl9rdz/rdz_ttgo_sonde/wiki/Supported-boards
for details on supported boardsi, and additional setup instructions.
for details on supported boards, and additional setup instructions.
### Radiosonde Support Matrix
Manufacturer | Model | Position | Temperature | Humidity | Pressure
-------------|-------|----------|-------------|----------|----------
Vaisala | RS92-SGP/NGP | :heavy_check_mark: | :heavy_check_mark: | :x: | :x:
Vaisala | RS41-SG/SGP/SGM | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x:
Graw | DFM06/09/17 | :heavy_check_mark: | :x: | :x: | :x:
Meteomodem | M10 | :heavy_check_mark: | :x: | :x: | Not Sent
Meteomodem | M20 | :heavy_check_mark: | :x: | :x: | Not Sent
Meteo-Radiy | MP3-H1 (MRZ-H1) | :heavy_check_mark: | :x: | :x: | :x:
SondeHub integration has mainly been tested with RS41 and DFM.
Support for other radiosondes that use AFSK modulation is not feasible with the TTGO hardware.
In particular, decoding iMet-1/iMet-4 radiosondes is not practical (iMet-5x seems to use FSK,
so should be feasible to implement).
Adding support for LMS6 (see issue #48) and ims100 (see branch ims100) could be feasible,
but currently I don't have plans to do add this myself. Well-tested pull requests will of
course be considered for inclusion :-).
## Installation
You can download the latest binary automated build for the development and testing branches [here](http://rdzsonde.mooo.com/download.html), the binary includes everything including configuration files so any existing settings will be reset.
To update an existing installatiom to the latest development or master version you can use the [OTA](https://github.com/dl9rdz/rdz_ttgo_sonde/wiki/Other-features#over-the-air-updates) update feature.
The downloaded .bin file can be flashed to your ESP32 board using [esptool](https://github.com/espressif/esptool) or [ESP32 Download Tool](https://www.espressif.com/en/support/download/other-tools)
### esptool
You can run the following command replacing `<filename.bin>` with the path to the downloaded .bin file.
If you encounter errors with the device COM not automatically being detected replace `/dev/cu.SLAB_USBtoUART` with `COM<X>`.
```
esptool --chip esp32 --port /dev/cu.SLAB_USBtoUART --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0x1000 <filename.bin>
```
### ESP32 Download Tool
The binary file can also be installed using the GUI application with the [following](http://rdzsonde.mooo.com/) settings.
## Button commands

Plik diff jest za duży Load Diff

Wyświetl plik

@ -25,11 +25,9 @@
#tft_rs=2
#tft_cs=0
tft_orient=1
#tft_modeflip=0
#tft_spifreq=40000000
#gps_rxd=-1
#gps_txd=-1
# Show AFC value (for RS41 and M10/M20, maybe also DFM, but not useful for RS92)
showafc=1
# Frequency correction, in Hz
# freqofs=0
#-------------------------------#
@ -42,8 +40,8 @@ wifi=3
# TCP/IP KISS TNC in port 14590 for APRSdroid (0=disabled, 1=enabled)
kisstnc.active = 1
# which screens file to use (0: screens.txt, i>0: screens${i}.txt
# 0: old version; 1: for OLED, 2: for TFT; 3: for TFT (portrait mode)
# which screens file to use (0: automated selection based on display type and orientation, i>0: screens${i}.txt
# predefined: 1: for OLED, 2: for ILI9225; 3: for ILI9225 (portrait mode); 4: for ILI9431; 5: for ILI9431 (portrait mode)
# screenfile=2
# display configuration. List of "displays"
# first entry: "Scanner" display
@ -113,11 +111,24 @@ tcp.idformat=0
# data not sanitized / quality checked, outliers not filtered out
mqtt.active=0
mqtt.id=rdz_sonde_server
mqtt.host=
mqtt.ip=192.168.1.5
mqtt.port=1883
mqtt.username=
mqtt.password=
mqtt.prefix=rdz_sonde_server/
#-------------------------------#
# Sondehub v2 settings
#-------------------------------#
# Sondehub v2 DB settings
sondehub.active=0
sondehub.chase=0
sondehub.host=api.v2.sondehub.org
sondehub.callsign=CHANGEME_RDZTTGO
sondehub.lat=
sondehub.lon=
sondehub.alt=
sondehub.antenna=
sondehub.email=
#-------------------------------#
# EOF
#-------------------------------#

17
RX_FSK/data/index.html 100644 → 100755
Wyświetl plik

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>rdzTTGOsonde Server</title>
<title>rdzTTGOSonde Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<link rel="icon" href="data:,">
@ -9,12 +9,14 @@
</head>
<body>
<div class="wrapper"><div class="header">
<h1>RDZSonde Server</h1>
<h2>rdzTTGOSonde Server</h2>
<div class="tab">
<button class="tablinks" onclick="selTab(event,'QRG')" id="defaultTab">QRG</button>
<button class="tablinks" onclick="selTab(event,'WiFi')">WiFi</button>
<button class="tablinks" onclick="selTab(event,'Data')">Data</button>
<button class="tablinks" onclick="document.location.href='livemap.html'">LiveMap</button>
<button class="tablinks" onclick="selTab(event,'Map')">Map</button>
<button class="tablinks" onclick="selTab(event,'Config')">Config</button>
<button class="tablinks" onclick="selTab(event,'Control')">Control</button>
<button class="tablinks" onclick="selTab(event,'About')">About</button>
@ -33,6 +35,10 @@
<iframe class="tci" src="" ></iframe>
</div>
<div id="Map" class="tabcontent" data-src="map.html">
<iframe class="tci" src="" ></iframe>
</div>
<div id="Config" class="tabcontent" data-src="config.html">
<iframe class="tci" src="" ></iframe>
</div>
@ -46,7 +52,12 @@
%VERSION_NAME%<br>
Copyright &copy; 2019-2021 by Hansi Reiser, DL9RDZ<br>
(version %VERSION_ID%)<br><br>
with contributions by Vigor and Xavier (M20 support), <a href="https://www.dl2mf.de/" target="_blank">Meinhard Guenther, DL2MF</a>,
<a href="/update.html">Check for update (requires TTGO internet connection via WiFi)</a><br><br>
with contributions by Vigor and Xavier (M20 support),
<a href="https://github.com/LukePrior">Luke Prior</a> and <a href="https://github.com/oh3bsg">OH3BSG</a> (SondeHub support),
<a href="https://www.dl2mf.de/" target="_blank">Meinhard Guenther, DL2MF</a>,
<a href="https://github.com/bazjo">Johannes</a>, <a href="http://www.p1337.synology.me/dokuwiki/doku.php?id=public:wettersonden">Robert Stefanowicz</a>,
<a href="https://github.com/puspis">Josema</a>, and probably some more people I forgot to mention here.

Wyświetl plik

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>rdzTTGOSonde Server LiveMap</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet.marker.slideto@0.2.0/Leaflet.Marker.SlideTo.js"></script>
<script src="livemap.js"></script>
</head>
<body>
<div id="map"></div>
</body>
</html>

Wyświetl plik

@ -0,0 +1,486 @@
try {
var check = $(document);
} catch (e) {
document.addEventListener("DOMContentLoaded", function(event) {
document.getElementById('map').innerHTML = '<br /><br />In order to use this functionality, there must be an internet connection.<br /><br/><a href="livemap.html">retry</a><br /><br/><a href="index.html">go back</a>';
});
}
$(document).ready(function(){
var map = L.map('map', { attributionControl: false, zoomControl: false });
map.on('mousedown touchstart',function () { follow=false; });
L.control.scale().addTo(map);
L.control.attribution({prefix:false}).addTo(map);
var osm = L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
attribution: '<div><a href="https://leafletjs.com/">Leaflet</a> &middot; Map: <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a></div>',
minZoom: 1,
maxZoom: 19
});
var esri = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: '<div><a href="https://leafletjs.com/">Leaflet</a> &middot; Map: <a href="https://www.esri.com/">Esri</a> &middot; Earthstar Geographics</div>',
minZoom: 1,
maxZoom: 20
});
var basemap = 'osm';
osm.addTo(map);
basemap_change = function () {
if (basemap == 'osm') {
map.removeLayer(osm);
map.addLayer(esri);
basemap = 'esri';
} else {
map.removeLayer(esri);
map.addLayer(osm);
basemap = 'osm';
}
};
map.setView([51.163361,10.447683], 5); // Mitte DE
var reddot = '<span class="ldot rbg"></span>';
var yellowdot = '<span class="ldot ybg"></span>';
var greendot = '<span class="ldot gbg"></span>';
$('#map .leaflet-control-container').append(L.DomUtil.create('div', 'leaflet-top leaflet-center leaflet-header'));
var header = '';
header += '<div id="sonde_main"><b>rdzTTGOSonde LiveMap</b><br />🎈 <b><span id="sonde_id"></span> - <span id="sonde_freq"></span> MHz - <span id="sonde_type"></span></b></div>';
header += '<div id="sonde_detail"><span id="sonde_alt"></span>m | <span id="sonde_climb"></span>m/s | <span id="sonde_speed"></span>km/h</div>';
header += '<div id="sonde_status"><span id="sonde_statbar"></span></div>';
header += '<div id="settings"><br /><b>Prediction-Settings</b><br />';
header += '<label for="burst">Burst at:</label><input type="text" id="burst" maxlength="5" value="..." /> m<br />';
header += '<label for="overwrite_descend">Descending:</label><input type="text" id="overwrite_descend" maxlength="2" value="..." /> m/s<br />';
header += '<label for="overwrite_descend_till">Use this descending until:</label><input type="text" id="overwrite_descend_till" maxlength="5" value="..." /> m<br />';
header += '<small>after the transmitted descend will be used</small>';
header += '<div id="submit"><input type="button" value="save" onclick="settings_save();"/>&nbsp;&nbsp;&nbsp;<input type="button" id="submit" value="reset" onclick="settings_reset();"/></div>';
header += '</div>';
$('.leaflet-header').append(header);
$('#map .leaflet-control-container').append(L.DomUtil.create('div', 'leaflet-bottom leaflet-center leaflet-footer'));
var footer = '';
footer += '<div id="gps_main"><b>Direction: </b><span class="gps_dir">...</span>°<br /><b>Distance: </b><span class="gps_dist">...</span>m</div>';
$('.leaflet-footer').append(footer);
var statbar = '';
headtxt = function(data,stat) {
var staticon = (stat == '1')?greendot:yellowdot;
statbar = staticon + statbar;
if ((statbar.length) > 10*greendot.length) { statbar = statbar.substring(0,10*greendot.length); }
if (data.lat == '0.000000') { return false; }
if (data.id) {
$('#sonde_id').html(data.id);
$('#sonde_alt').html(data.alt);
$('#sonde_climb').html(data.climb);
$('#sonde_speed').html( mr(data.speed * 3.6 * 10) / 10 );
$('#sonde_detail').show();
} else {
$('#sonde_id').html(data.launchsite.trim());
$('#sonde_detail').hide();
}
$('#sonde_freq').html(data.freq);
$('#sonde_type').html(data.type);
$('#sonde_statbar').html('&nbsp;'+statbar);
};
map.addControl(new L.Control.Button([ { position: 'topleft', text: '🔙', href: 'index.html' } ]));
L.control.zoom({ position:'topleft' }).addTo(map);
map.addControl(new L.Control.Button([ { position: 'topleft', text: '🗺️', href: 'javascript:basemap_change();' } ]));
map.addControl(new L.Control.Button([ { position: 'topright', id: "status", text: '', href: 'javascript:get_data();' } ]));
map.addControl(new L.Control.Button([
{ position:'topright', text: '🎈', href: 'javascript:show(marker,\'marker\');' },
{ text: '〰️', href: 'javascript:show_line();' },
{ text: '💥', href: 'javascript:show(marker_burst,\'burst\');' },
{ text: '🎯', href: 'javascript:show(marker_landing,\'landing\');' }
]));
map.addControl(new L.Control.Button([ { position:'topright', text: '⚙️', href: 'javascript:show_settings();' } ]));
show = function(e,p) {
if (p == 'landing') { get_predict(last_data); }
if (e) {
map.closePopup();
map.setView(map._layers[e._leaflet_id].getLatLng());
map._layers[e._leaflet_id].openPopup();
follow = p;
}
};
getTwoBounds = function (a,b) {
var sW = new L.LatLng((a._southWest.lat > b._southWest.lat)?b._southWest.lat:a._southWest.lat, (a._southWest.lng > b._southWest.lng)?b._southWest.lng:a._southWest.lng);
var nE = new L.LatLng((a._northEast.lat < b._northEast.lat)?b._northEast.lat:a._northEast.lat, (a._northEast.lng < b._northEast.lng)?b._northEast.lng:a._northEast.lng);
return new L.LatLngBounds(sW, nE);
};
show_line = function() {
$('.i_position, .i_landing').remove();
map.closePopup();
if (line._latlngs.length != 0 && line_predict._latlngs.length != 0) {
map.fitBounds(getTwoBounds(line.getBounds(),line_predict.getBounds()));
} else if (line._latlngs.length != 0) {
map.fitBounds(line.getBounds());
} else if (line_predict._latlngs.length != 0) {
map.fitBounds(line_predict.getBounds());
}
};
last_data = false;
follow = 'marker';
marker_landing = false;
icon_landing = L.divIcon({className: 'leaflet-landing'});
dots_predict = [];
line_predict = L.polyline(dots_predict,{color: 'yellow'}).addTo(map);
marker_burst = false;
icon_burst = L.divIcon({className: 'leaflet-burst'});
marker = false;
dots = [];
line = L.polyline(dots).addTo(map);
draw = function(data) {
var stat;
if (data.id) {
if ((data.lat != '0.000000' && data.lon != '0.000000') && (JSON.stringify(data) != JSON.stringify(last_data)) ) {
var location = [data.lat,data.lon,data.alt];
if (!marker) {
map.setView(location, 14);
marker = L.marker(location).addTo(map)
.bindPopup(poptxt('position',data),{closeOnClick:false, autoPan:false}).openPopup();
get_predict(data);
} else {
marker.slideTo(location, {
duration: 500,
keepAtCenter: (follow=='marker')?true:false
})
.setPopupContent(poptxt('position',data));
if (last_data.id != data.id) {
storage_remove();
dots = [];
get_predict(data);
}
}
dots.push(location);
line.setLatLngs(dots);
storage_write(data);
$('#status').html(greendot);
stat = 1;
} else {
$('#status').html(yellowdot);
stat = 0;
}
headtxt(data,stat);
last_data = data;
} else {
$('#status').html(yellowdot);
headtxt(data,0);
}
};
marker_gps = false;
icon_gps = L.divIcon({className: 'leaflet-gps'});
circ_gps = false;
gps = function(e) {
gps_location = [e.lat/1000000,e.lon/1000000];
gps_accuracy = e.hdop*2;
if (last_data && last_data.lat != '0.000000') {
if ($('.leaflet-footer').css('display') == 'none') { $('.leaflet-footer').show(); }
var distance = Math.round(map.distance(gps_location,[last_data.lat, last_data.lon]));
distance = (distance > 1000)?(distance / 1000) + 'k':distance;
$('.leaflet-footer .gps_dist').html(distance);
$('.leaflet-footer .gps_dir').html( bearing(gps_location,[last_data.lat, last_data.lon]) );
}
if (!marker_gps) {
map.addControl(new L.Control.Button([{ position: 'topleft', text: '🛰️', href: 'javascript:show(marker_gps,\'gps\');' }]));
marker_gps = L.marker(gps_location,{icon:icon_gps}).addTo(map)
.bindPopup(poptxt('gps',e),{closeOnClick:false, autoPan:false});
circ_gps = L.circle(gps_location, gps_accuracy).addTo(map);
} else {
marker_gps.slideTo(gps_location, {
duration: 500,
keepAtCenter: (follow=='gps')?true:false
})
.setPopupContent(poptxt('gps',e));
circ_gps.slideTo(gps_location, { duration: 500 });
circ_gps.setRadius(gps_accuracy);
}
};
get_data = function() {
$('#status').html(reddot);
$.ajax({url: 'live.json', success: (function( data ) {
if (typeof data != "object") { data = $.parseJSON(data); }
if (data.sonde) {
draw(data.sonde);
} else {
setTimeout(function() {$('#status').html(yellowdot);},100);
}
if (data.gps) {
gps(data.gps);
}
}),
timeout: 1000}
);
};
storage = (typeof(Storage) !== "undefined")?true:false;
settings_std = {
burst: 32500,
overwrite_descend: 6,
overwrite_descend_till: 12000
};
settings_read = function() {
if (storage) {
if (sessionStorage.settings) {
return JSON.parse(sessionStorage.settings);
} else {
settings_write(settings_std);
return settings_std;
}
} else {
return settings_std;
}
return false;
};
settings_write = function (data) {
if (storage) {
sessionStorage.settings = JSON.stringify(data);
settings = data;
}
};
settings = settings_read();
settings_save = function() {
settings.burst = parseInt($('#settings #burst').val());
settings.overwrite_descend = parseInt($('#settings #overwrite_descend').val());
settings.overwrite_descend_till = parseInt($('#settings #overwrite_descend_till').val());
if (Number.isInteger(settings.burst) && Number.isInteger(settings.overwrite_descend) && Number.isInteger(settings.overwrite_descend_till)) {
settings_write(settings);
$("#settings").slideUp();
get_predict(last_data);
} else {
alert('Error: only numeric values allowed!');
}
};
settings_reset = function() {
if (confirm('Reset to default?')) {
settings_write(settings_std);
show_settings();
}
};
show_settings = function() {
$('#settings #burst').val(settings.burst);
$('#settings #overwrite_descend').val(settings.overwrite_descend);
$('#settings #overwrite_descend_till').val(settings.overwrite_descend_till);
$("#settings").slideToggle();
};
predictor = false;
get_predict = function(data) {
if (!data) { return; }
var ascent = (data.climb > 0)? data.climb : 15;
var descent = (data.climb > 0)? settings.overwrite_descend : data.climb * -1;
var burst;
if (data.climb > 0) {
burst = (data.alt > settings.burst )?data.alt + 100 : settings.burst;
} else {
burst = parseInt(data.alt) + 7;
if (data.alt > settings.overwrite_descend_till ) { descent = settings.overwrite_descend; }
}
var m = new Date();
var datetime = m.getUTCFullYear() + "-" + az(m.getUTCMonth()+1) + "-" + az(m.getUTCDate()) + "T" +
az(m.getUTCHours()) + ":" + az(m.getUTCMinutes()) + ":" + az(m.getUTCSeconds()) + "Z";
var url = 'https://predict.cusf.co.uk/api/v1/';
url += '?launch_latitude='+data.lat + '&launch_longitude='+fix_lon(data.lon);
url += '&launch_altitude='+data.alt + '&launch_datetime='+datetime;
url += '&ascent_rate='+ascent + '&burst_altitude=' + burst + '&descent_rate='+descent;
$.getJSON(url, function( prediction ) {
draw_predict(prediction,data);
});
};
draw_predict = function(prediction,data) {
var ascending = prediction.prediction[0].trajectory;
var highest = ascending[ascending.length-1];
var highest_location = [highest.latitude,fix_lon(highest.longitude)];
var descending = prediction.prediction[1].trajectory;
var landing = descending[descending.length-1];
var landing_location = [landing.latitude,fix_lon(landing.longitude)];
if (!marker_landing) {
marker_landing = L.marker(landing_location,{icon: icon_landing}).addTo(map)
.bindPopup(poptxt('landing',landing),{closeOnClick:false, autoPan:false});
} else {
marker_landing.slideTo(landing_location, {
duration: 500,
keepAtCenter: (follow=='landing')?true:false
})
.setPopupContent(poptxt('landing',landing));
}
dots_predict=[];
if (data.climb > 0) {
ascending.forEach(p => dots_predict.push([p.latitude,fix_lon(p.longitude)]));
if (!marker_burst) {
marker_burst = L.marker(highest_location,{icon:icon_burst}).addTo(map).bindPopup(poptxt('burst',highest),{closeOnClick:false, autoPan:false});
} else {
marker_burst.slideTo(highest_location, {
duration: 500,
keepAtCenter: (follow=='burst')?true:false
}).setPopupContent(poptxt('burst',highest));
}
}
descending.forEach(p => dots_predict.push([p.latitude,fix_lon(p.longitude)]));
line_predict.setLatLngs(dots_predict);
if (data.climb > 0) {
predictor_time = 5 * 60; // ascending, every 5 min
} else if (data.climb < 0 && data.alt > 5000) {
predictor_time = 2 * 60; // descending, above 5km, every 2 min
} else {
predictor_time = 30; // descending, below 5km, every 30 sec
}
clearTimeout(predictor);
predictor = setTimeout(function() {get_predict(last_data);}, predictor_time*1000);
};
fix_lon = function(lon) {
if (lon > 180) { return lon - 360; }
if (lon < 0) { return lon + 360; }
return lon;
};
poptxt = function(t,i) {
var lat_input = (i.id)?i.lat:i.latitude;
var lon_input = (i.id)?i.lon:i.longitude;
var lat = Math.round(lat_input * 1000000) / 1000000;
var lon = Math.round(lon_input * 1000000) / 1000000;
var add =
'<br /><b>Position:</b> '+lat+', '+lon+'<br />'+
'<b>Open:</b> <a href="https://www.google.de/maps/?q='+lat+', '+lon+'" target="_blank">GMaps</a> | <a href="https://www.openstreetmap.org/?mlat='+lat+'&mlon='+lon+'&zoom=15" target="_blank">OSM</a> | <a href="mapsme://map?ll='+lat+','+lon+'">Maps.me</a>';
if (t == 'position') { return '<div class="i_position"><b>🎈 '+i.id+'</b>'+add+'</div>'; }
if (t == 'burst') { return '<div class="i_burst"><b>💥 Predicted Burst:</b><br />'+fd(i.datetime)+' in '+mr(i.altitude)+'m'+add+'</div>'; }
if (t == 'highest') { return '<div class="i_burst"><b>💥 Burst:</b> '+mr(i.altitude)+'m'+add+'</div>';}
if (t == 'landing') { return '<div class="i_landing"><b>🎯 Predicted Landing:</b><br />'+fd(i.datetime)+' at '+mr(i.altitude)+'m'+add+'</div>'; }
if (t == 'gps') { return '<div class="i_gps">Position: '+(i.lat/1000000)+','+(i.lon/1000000)+'<br />Altitude: '+mr(i.alt/1000)+'m<br />Speed: '+mr(i.speed/1000 * 1.852 * 10)/10+'km/h '+mr(i.dir/1000)+'°<br />Sat: '+i.sat+' Hdop:'+(i.hdop/10)+'</div>'; }
};
fd = function(date) {
var d = new Date(Date.parse(date));
return az(d.getUTCHours()) +':'+ az(d.getUTCMinutes())+' UTC';
};
az = function(n) { return (n<10)?'0'+n:n; };
mr = function(n) { return Math.round(n); };
storage = (typeof(Storage) !== "undefined")?true:false;
storage_write = function (data) {
if (storage) {
if (sessionStorage.sonde) {
storage_data = JSON.parse(sessionStorage.sonde);
} else {
storage_data = [];
}
if (JSON.stringify(data) != JSON.stringify(storage_data[storage_data.length - 1])) {
storage_data.push(data);
sessionStorage.sonde = JSON.stringify(storage_data);
}
}
};
storage_read = function() {
if (storage) {
if (sessionStorage.sonde) {
storage_data = JSON.parse(sessionStorage.sonde);
return storage_data;
}
}
return false;
};
storage_remove = function() {
sessionStorage.removeItem('sonde');
};
session_storage = storage_read();
if (session_storage) {
session_storage.forEach(function(d) {
dots.push([d.lat,d.lon,d.alt]);
session_storage_last = d;
});
draw(session_storage_last);
}
setInterval(get_data,1000);
});
L.Control.Button = L.Control.extend({
onAdd: function (map) {
var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
options = this.options;
Object.keys(options).forEach(function(key) {
this.link = L.DomUtil.create('a', '', container);
this.link.text = options[key].text;
this.link.href = options[key].href;
this.link.id = options[key].id;
});
this.options.position = this.options[0].position;
return container;
}
});
// https://github.com/makinacorpus/Leaflet.GeometryUtil/blob/master/src/leaflet.geometryutil.js#L682
// modified to fit
function bearing(latlng1, latlng2) {
var rad = Math.PI / 180,
lat1 = latlng1[0] * rad,
lat2 = latlng2[0] * rad,
lon1 = latlng1[1] * rad,
lon2 = latlng2[1] * rad,
y = Math.sin(lon2 - lon1) * Math.cos(lat2),
x = Math.cos(lat1) * Math.sin(lat2) -
Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
var bearing = ((Math.atan2(y, x) * 180 / Math.PI) + 360) % 360;
bearing = bearing < 0 ? bearing-360 : bearing;
return Math.round(bearing);
}

Wyświetl plik

@ -6,6 +6,7 @@ stypes.set('6', 'DFM6 (old)');
stypes.set('D', 'DFM');
stypes.set('M', 'M10');
stypes.set('2', 'M20');
stypes.set('3', 'MP3H');
/* Used by qrg.html in RX_FSK.ino */
function prep() {

Wyświetl plik

@ -109,7 +109,7 @@
@Scanner
timer=-1,0,0
key1action=D,#,F,W
key2action=#,#,#,#
key2action=D,#,#,#
timeaction=#,D,+
0,0=XScan
0,5=S#:

Wyświetl plik

@ -103,7 +103,7 @@
@ScannerTFT
timer=-1,0,0
key1action=D,#,F,W
key2action=#,#,#,#
key2action=D,#,#,#
timeaction=#,D,+
fonts=5,6
0,0=XScan
@ -200,7 +200,7 @@ timeaction=#,#,#
@Scan.TFT.Bazjo
timer=-1,0,0
key1action=D,#,F,W
key2action=#,#,#,#
key2action=D,#,#,#
timeaction=#,D,+
scale=11,10
fonts=0,2
@ -337,7 +337,7 @@ color=99001F
@Scanner.Puspis
timer=-1,0,4
key1action=D,#,F,W
key2action=#,#,#,#
key2action=D,#,#,#
timeaction=#,D,+
scale=13,10
fonts=0,1

Wyświetl plik

@ -0,0 +1,574 @@
## screens2.txt: TFT display (landscape)
# Definition of display content and action behaviour
#
# Timer: (view timer, rx timer, norx timer)
# - value -1: timer is disabled; value>=0: timer fires after (value) seconds
# - view timer: time since current view (display mode and sonde) was started
# - rx timer: time since when sonde data has been received continuously (trigger immediatly after RX)
# - norx timer: time since when no sonde data has been received continuously
# (rx and norx timer is started after tuning a new frequency and receiving a signal or not receiving
# anything for a 1s period)
#
# Actions:
# - W: activate WiFi scan
# - F: activate frequency spectrum display
# - 0: activate "Scan:" display (this is basically just display mode 0)
# - x: (1..N): activate display mode x [deprecated]
# - >: activate next display mode
# - D: activate default receiver display (display mode specified in config)
# - +: advance to next active sonde from QRG config
# - #: no action
#
# Display content (lower/upper case: small/large font)
# line,column=content
# for ILI9225 its also possible to indicate
# line,column,width=content for text within a box of width 'width'
# line,column,-width=content for right-justified text
#
# XText : Text
# F(suffix): frequency (with suffix, e.g., " MHz")
# L latitade
# O lOngitute
# A altitude
# Hm(suffix) hor. speed m/s (suffix: e.g. "m/s"; no suffix=>m/s as 16x8 bitmap for SSD1306 display only)
# Hk(suffix) hor. speed km/h (suffix: e.g. "km/h"; no suffix=>km/h as 16x8 bitmap for SSD1306 display only)
# V(suffix) vert. speef (suffix: e.g. "m/s"; no suffix=>m/s as 16x8 bitmap for SSD1306 display only)
# Ix sonde ID (default/d: dxlaprs; s: short id, n: real serial number)
# RS41,RS92: all identical R1234567
# DFMx: ID M12345678; short ID and serial 12345678
# M10: ID ME95231F0; short ID: M95231F0; serial 9062104592
# Q signal quality statistics bar
# T type string (RS41/DFM9/DFM6/RS92)
# C afC value
# N ip address (only tiny font)
# S scan list entry info: l/empty: launch site name, #=entry nr, t=total entries, a=active entries, /: #/t
# K RS41 kill timer values: Kl launch timer, Kb burst timer, Kc kill countdown
# format: K_4: h:mm k_6: h:mm:ss k_s: sssss, nothing shown for other sonde
# Mx telemetry value x (t temp p preassure h hyg) [not yet implemented, maybe some day in future]
# Gx GPS-related data
# raw data from GPS: GA, GO, GH, GC: LAtitude, lOngitude, Altutide(Height), Course over ground
# relative to sonde: GD, GI, GB: Distance, dIrection (absolute), relative Bearing
# G0 GPS circle diagram e.g. 3,5=g0NCS,50,ff0000,000033,5,ffff00,4,ffffff
# "N" (what is on top: N=north C=course)
# "C" (where does the arrow point to: C=course, S=sonde)
# "S" (what is shown by the bullet: C=course, S=sonde)
# 50: circle radius, followed by fg and bg color
# 5: bullet radius, followed by fg color
# 4: arrow width, followed by fg color
# R RSSI
# B battery(T-Beam 1.0) S=status V=Batt.Volt C=charge current D=discharge current
# U=USB volt I=USB current T=IC temp
#
# fonts=x,y can be used to select font (x=small, y=large) for all items below
# for SSD1306, x and y can be used to select one of those fonts:
# (y should be a 1x2 font (1,5,6,7), x a small font)
# u8x8_font_chroma48medium8_r, // 0 ** default small
# u8x8_font_7x14_1x2_f, // 1 ** default large
# u8x8_font_amstrad_cpc_extended_f, // 2
# u8x8_font_5x7_f, // 3
# u8x8_font_5x8_f, // 4
# u8x8_font_8x13_1x2_f, // 5
# u8x8_font_8x13B_1x2_f, // 6
# u8x8_font_7x14B_1x2_f, // 7
# u8x8_font_artossans8_r, // 8
# u8x8_font_artosserif8_r, // 9
# u8x8_font_torussansbold8_r, // 10
# u8x8_font_victoriabold8_r, // 11
# u8x8_font_victoriamedium8_r, // 12
# u8x8_font_pressstart2p_f, // 13
# u8x8_font_pcsenior_f, // 14
# u8x8_font_pxplusibmcgathin_f, // 15
# u8x8_font_pxplusibmcga_f, // 16
# u8x8_font_pxplustandynewtv_f, // 17
#
# for ILI9225, these fonts are available:
# Terminal6x8 // 0
# Terminal11x16 // 1
# Terminal12x16 // 2
# FreeMono9pt7b, // 3
# FreeMono12pt7b, // 4
# FreeSans9pt7b, // 5
# FreeSans12pt7b, // 6
# Picopixel, // 7
#
# color=rrggbb,rrggbb can be used to select color (foreground, background)
# see https://github.com/Nkawu/TFT_22_ILI9225/wiki#color-reference for example (use without "#"-sign)
#
# for TFT display, coordinates and width are multiplied by xscale,yscale and later used in pixels
# with scale=1,1 you can directly use pixel coordinates. (default: xscale=13,yscale=22 => 8 lines, 16 columns)
###########
############
# Scan display for large 2" TFT dispaly
@ScannerTFT
scale=30,18
timer=-1,0,0
key1action=D,#,F,W
key2action=D,#,#,#
timeaction=#,D,+
fonts=5,6
0,0=XScan
0,5,-3=S#:
0,9,5.5=T
3,0=F MHz
5,0,16=S
7,5=n
############
@MainTFT
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,0
color=FFD700
0,0,10.5=Is
color=0000FF
0,11,-5.5=f
1,0,4=t
1,10.5,-6=c
color=00ff00
2,0,7=L
4,0,7=O
color=FFA500
2,9.5,-7=A
3,9.5,-7=vm/s
color=AA5522
4,9.5,-7=hkkm/h
color=FFFFFF
6,2=r
6.3,10=Q4
7,0=xd=
7,2,6=gD
7,12=gI
############
@PeilungTFT
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
color=ffff00,000033
color=bbbbbb,000000
0,2=xN Top:
0,8=xCourse Top:
color=ffff00,000033
1,0=g0NCS,48,ffff00,000044,6,33ff33,5,eeaa00
1,8=g0CCS,48,ffff00,000044,6,55ff55,5,eeaa00
color=ffffff,000000
6,0=xDirection:
6,8,4=gI
7,0=xCOG:
7,4,4=gC
7,8=xturn:
7,12,4=gB
############
@GPSdataTFT
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
0,0=xOn-board GPS:
1,0,8=gA
2,0,8=gO
3,0,8=gH
4,0,8=gC
5,0=xGPS vs Sonde:
6,0,8=gD
7,0,8=gI
7,8,8=gB
############
@BatteryTFT
timer=-1,-1,-1
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
0,0=xBattery status:
0,14=bS
1,0=xBatt:
1,5,5=bVV
2,0,16=bCmA(charging)
3,0,16=bDmA(discharging)
4.4,0=xUSB:
4.4,5,5=bUV
5.4,0,10=bImA
6.4,0=xTemp:
6.4,5,5=bT C
### Alternative display layouts based on https://gist.github.com/bazjo
# Scan display for large 2" TFT dispaly
@Scan.TFT.Bazjo
timer=-1,0,0
key1action=D,#,F,W
key2action=D,#,#,#
timeaction=#,D,+
scale=11,10
fonts=0,2
color=e0e0e0
#Row 1
0.5,0=XScanning...
#Row 2
3,0=xIndex
4,0,8=S/
3,9=xSite
4,9=S
#Row 3
6,0=xType
7,0,6=T
6,9=xFrequency
7,9=F
#Row 4
9,0=xWeb UI IP
10,0=N
#Row 5
#Footer
color=6C757D
15,0=xScan Mode
15,18=bVV
############
@Decode/General.TFT.Bazjo
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,0
scale=11,10
fonts=0,2
#Row 1
color=996A06
0,0=xSerial
0,5=t
color=FFB10B
1,0=Is
color=996A06
0,11=xFreq.
0,16=c
color=FFB10B
1,11=F
#Row 2
color=3C5C99
3,0=xLatitude
color=639AFF
4,0=L
color=3C5C99
3,11=xLongitude
color=639AFF
4,11=O
#Row 3
color=3C5C99
6,0=xHoriz. Speed
color=639AFF
7,0=Hkkm/h
color=3C5C99
6,11=xVert. Speed
color=639AFF
7,11=Vm/s
#Row 4
color=99004A
9,0=xAltitude
color=FF007B
10,0=A
color=99004A
9,11=xBearing
color=FF007B
10,11=GB
#Row 5
color=06998E
12,0=xRSSI
color=0AFFEF
13,0=R
color=06998E
12,11=xHistory
color=0AFFEF
13.5,11=Q4
#Footer
color=6C757D
15,0=xDecode Mode / General View
15,18=bVV
############
@Decode/Battery.TFT.Bazjo
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
scale=11,10
fonts=0,2
#Row 1
color=99001F
0,0=xBattery Status
0,11=xBattery Voltage
color=FF0035
1,0=BS
1,11=BVV
#Row 2
color=99001F
3,0=xCharge Current
3,11=xDischarge Current
color=FF0035
4,0=BCmA
4,11=BDmA
#Row 3
color=99001F
6,0=xUSB Voltage
6,11=xUSB Current
color=FF0035
7,0=BUV
7,11=BImA
#Row 4
color=99001F
9,0=xIC Temperature
#9,11=xKey
color=FF0035
10,0=BTC
#10,11=XValue
#Row 5
#12,0=xKey
#12,11=xKey
#13,0=XValue
#13,11=XValue
#Footer
color=99001F
15,0=xDecode Mode/Battery View
15,18=bVV
# based on https://github.com/puspis/rdz_ttgo_sonde
##########
@Scanner.Puspis
timer=-1,0,4
key1action=D,#,F,W
key2action=D,#,#,#
timeaction=#,D,+
scale=13,10
fonts=0,1
#Row 1
color=90EE90
0.5,3=XFREQUENCY SCAN
#Row 2
color=00FF00
3,0=xMEMORY
3,9=xLAUNCH SITE
color=639AFF
4,0,9=S/
4,9=S
#Row 3
color=00FF00
6,0=xTYPE
6,9=xFREQUENCY
color=639AFF
7,0,9=T
7,9=F MHz
#Row 4
fonts=0,5
color=285454
11.5,0=xIP ADDRESS:
10.9,7,-15=N
#Footer
color=FF0000
12.7,18=bVV
############
@Main.Puspis
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,0
scale=11,10
fonts=0,2
#Row 1
color=00FF00
0,0=xSONDE ID
0,11=xFREQUENCY
color=90EE90
0,7.4=t
1,0=Is
1,11=F
#Row 2
fonts=0,1
color=00FF00
3,0=xLATITUDE
3,11=xLONGITUDE
color=FF007B
4,0=L
4,11=O
#Row 3
color=00FF00
6,0=xWIND SPEED
6,11=xCLIMB RATE
color=639AFF
7,0=Hkkm/h
7,11=Vm/s
#Row 4
color=00FF00
9,0=xRX ALTITUDE
9,11=xSONDE ALTITUDE
color=639AFF
10,0=GH
10,11=A
#Row 5
color=00FF00
12,0=xDISTANCE
12,11=xFRAMES
color=FFFFFF
13,0=GD
13.5,11=Q4
#Footer
color=FF0000
15,0.2=xIP:
15,2.5=n
15,18=bVV
############
@JotaEme.Puspis
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,0
scale=11,10
fonts=0,1
#Row 1
color=90EE90
0,0=Is
0.7,10.5=t
0,14=F
#Row 2
color=00FF00
2,1.2=xWIND SPEED
2,14=xCLIMB RATE
color=639AFF
3,1=Hkkm/h
3,13=Vm/s
#Row 3
color=00FF00
5,1=xRX ALTITUDE
5,12.5=xSONDE ALTITUDE
color=639AFF
6,2=GH
6,14=A
#Row 4
color=00FF00
8,0=xSONDE POSITION
color=FF007B
9,2=l
10,2=o
#Row 5
color=00FF00
11.4,2=xDISTANCE
color=FFFFFF
12.4,1.5=GD
#Circle
color=EEAA00,000033
8,13.8=g0CCS,28,FFFF00,000033,5,9ACD32,5,EEAA00
#Footer
color=FF0000
15,0=n
color=FFFFFF,000000
15,8.7=Q4
color=FF0000
15,18.4=bVV
############
@CompassTFT.Puspis
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
scale=13,10
fonts=0,1
#Row 1
color=90EE90
0.5,1.5=XCOMPASS
#Row 2
color=00FF00
4,2=xRX HEADING
color=639AFF
5,3.8=GC
#Row 3
color=00FF00
9.5,3=xDISTANCE
9.5,13.5=xBEARING
color=639AFF
10.5,3.4=GD
10.5,14.3=GI
#Circle
color=EEAA00,000033
0.2,10=g0CCS,52,FFFF00,000033,10,9ACD32,6,EEAA00
#Footer
color=FF0000,000000
12.7,1=Q4
12.7,18=bVV
############
@GPSdataTFT.Puspis
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
scale=13,10
fonts=0,1
#Row 1
color=90EE90
0.5,0.5=XGPS RECEIVER STATION
#Row 2
color=00FF00
3,0=xRX LATITUDE
3,12=xRX LONGITUDE
color=639AFF
4,0=GA
4,12=GO
#Row 3
color=00FF00
6,0=xRX ALTITUDE
6,12=xRX HEADING
color=639AFF
7,0=GH
7,12=GC
#Row 4
color=00FF00
9,0=xDISTANCE
9,12=xBEARING
color=639AFF
10,0=GD
10,12=GB
#Footer
color=FF0000,000000
12.7,0.4=Q4
12.7,18=bVV
############
@BatteryTFT.Puspis
timer=-1,-1,-1
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
scale=13,10
fonts=0,1
#Row 1
color=90EE90
0.5,4=XBATTERY STATUS
#Row 2
color=00FF00
3,0=x(C)HARGE/(B)ATT
3,11.5=xBATTERY VOLTAGE
color=639AFF
4,4=BS
4,11.5=BVV
#Row 3
color=00FF00
6,0=xCHARGE CURRENT
6,11.5=xDISCHG. CURRENT
color=639AFF
7,0=BCmA
7,11.5=BDmA
#Row 3
color=00FF00
9,0=xIC TEMPERATURE
9,11.5=xFREQ. OFFSET
color=639AFF
10,0=BTC
10,10=C
#Footer
color=FF0000,000000
12.7,0.4=Q4
12.7,18=bVV

Wyświetl plik

@ -0,0 +1,217 @@
## screens3.txt: TFT display (portrait)
## based on http://www.p1337.synology.me/dokuwiki/doku.php?id=public:wettersonden
# Definition of display content and action behaviour
#
# Timer: (view timer, rx timer, norx timer)
# - value -1: timer is disabled; value>=0: timer fires after (value) seconds
# - view timer: time since current view (display mode and sonde) was started
# - rx timer: time since when sonde data has been received continuously (trigger immediatly after RX)
# - norx timer: time since when no sonde data has been received continuously
# (rx and norx timer is started after tuning a new frequency and receiving a signal or not receiving
# anything for a 1s period)
#
# Actions:
# - W: activate WiFi scan
# - F: activate frequency spectrum display
# - 0: activate "Scan:" display (this is basically just display mode 0)
# - x: (1..N): activate display mode x [deprecated]
# - >: activate next display mode
# - D: activate default receiver display (display mode specified in config)
# - +: advance to next active sonde from QRG config
# - #: no action
#
# Display content (lower/upper case: small/large font)
# line,column=content
# for ILI9225 its also possible to indicate
# line,column,width=content for text within a box of width 'width'
# line,column,-width=content for right-justified text
#
# XText : Text
# F(suffix): frequency (with suffix, e.g., " MHz")
# L latitade
# O lOngitute
# A altitude
# Hm(suffix) hor. speed m/s (suffix: e.g. "m/s"; no suffix=>m/s as 16x8 bitmap for SSD1306 display only)
# Hk(suffix) hor. speed km/h (suffix: e.g. "km/h"; no suffix=>km/h as 16x8 bitmap for SSD1306 display only)
# V(suffix) vert. speef (suffix: e.g. "m/s"; no suffix=>m/s as 16x8 bitmap for SSD1306 display only)
# Ix sonde ID (default/d: dxlaprs; s: short id, n: real serial number)
# RS41,RS92: all identical R1234567
# DFMx: ID M12345678; short ID and serial 12345678
# M10: ID ME95231F0; short ID: M95231F0; serial 9062104592
# Q signal quality statistics bar
# T type string (RS41/DFM9/DFM6/RS92)
# C afC value
# N ip address (only tiny font)
# S scan list entry info: l/empty: launch site name, #=entry nr, t=total entries, a=active entries, /: #/t
# K RS41 kill timer values: Kl launch timer, Kb burst timer, Kc kill countdown
# format: K_4: h:mm k_6: h:mm:ss k_s: sssss, nothing shown for other sonde
# Mx telemetry value x (t temp p preassure h hyg) [not yet implemented, maybe some day in future]
# Gx GPS-related data
# raw data from GPS: GA, GO, GH, GC: LAtitude, lOngitude, Altutide(Height), Course over ground
# relative to sonde: GD, GI, GB: Distance, dIrection (absolute), relative Bearing
# G0 GPS circle diagram e.g. 3,5=g0NCS,50,ff0000,000033,5,ffff00,4,ffffff
# "N" (what is on top: N=north C=course)
# "C" (where does the arrow point to: C=course, S=sonde)
# "S" (what is shown by the bullet: C=course, S=sonde)
# 50: circle radius, followed by fg and bg color
# 5: bullet radius, followed by fg color
# 4: arrow width, followed by fg color
# R RSSI
# B battery(T-Beam 1.0) S=status V=Batt.Volt C=charge current D=discharge current
# U=USB volt I=USB current T=IC temp
#
# fonts=x,y can be used to select font (x=small, y=large) for all items below
# for SSD1306, x and y can be used to select one of those fonts:
# (y should be a 1x2 font (1,5,6,7), x a small font)
# u8x8_font_chroma48medium8_r, // 0 ** default small
# u8x8_font_7x14_1x2_f, // 1 ** default large
# u8x8_font_amstrad_cpc_extended_f, // 2
# u8x8_font_5x7_f, // 3
# u8x8_font_5x8_f, // 4
# u8x8_font_8x13_1x2_f, // 5
# u8x8_font_8x13B_1x2_f, // 6
# u8x8_font_7x14B_1x2_f, // 7
# u8x8_font_artossans8_r, // 8
# u8x8_font_artosserif8_r, // 9
# u8x8_font_torussansbold8_r, // 10
# u8x8_font_victoriabold8_r, // 11
# u8x8_font_victoriamedium8_r, // 12
# u8x8_font_pressstart2p_f, // 13
# u8x8_font_pcsenior_f, // 14
# u8x8_font_pxplusibmcgathin_f, // 15
# u8x8_font_pxplusibmcga_f, // 16
# u8x8_font_pxplustandynewtv_f, // 17
#
# for ILI9225, these fonts are available:
# Terminal6x8 // 0
# Terminal11x16 // 1
# Terminal12x16 // 2
# FreeMono9pt7b, // 3
# FreeMono12pt7b, // 4
# FreeSans9pt7b, // 5
# FreeSans12pt7b, // 6
# Picopixel, // 7
#
# color=rrggbb,rrggbb can be used to select color (foreground, background)
# see https://github.com/Nkawu/TFT_22_ILI9225/wiki#color-reference for example (use without "#"-sign)
#
# for TFT display, coordinates and width are multiplied by xscale,yscale and later used in pixels
# with scale=1,1 you can directly use pixel coordinates. (default: xscale=13,yscale=22 => 8 lines, 16 columns)
###########
#
# Default configuration for "Scanner" display:
# - view timer disabled; rx timer=0; norx timer = 0
# => after 1 second immediately an action is triggered
# (norx: go to next sonde; rx: go to default receiver display)
# - key1 actions: D,0,F,W
# => Button press activates default receiver view, double press does nothing
# Mid press activates Spectrum display, long press activates Wifi scan
# - key2 has no function
@ScannerPortrait
timer=-1,0,0
key1action=D,#,F,W
key2action=>,#,#,#
timeaction=#,D,+
0,0=XScan
0,5=S#:
0,9,4.5=T
6,0=XHoehe
6,5=GH
color=ffff00
2,0=F MHz
4,0=S
color=00ff00,444444
7,5=n
7,0=bV
############
# Default configuration for "Legacy" display:
# - view timer=-1, rx timer=-1 (disabled); norx timer=20 (or -1 for "old" behaviour)
# => norx timer fires after not receiving a singla for 20 seconds
# - key1 actions: +,0,F,W
# => Button1 press: next sonde; double press => @Scanner display
# => Mid press activates Spectrum display, long press activates Wifi scan
# - key2 actions: 2,#,#,#
# => BUtton2 activates display 2 (@Field)
# - timer actions: #,#,0
# (norx timer: if no signal for >20 seconds: go back to scanner mode)
#
@LegacyPortrait
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,0
9,10=f
9,0=r
9,4=Q
5,0=g0NCS,35,ffff00,000044,6,33ff33,5,eeaa00
5,7=g0CCS,35,ffff00,000044,6,55ff55,5,eeaa00
0,0=s
0,9=is
2,0=L
3,0=O
color=FFFF00
1,6=Hk km/h
color=FF0000
1,0=GD
color=FFFFFF
4,9=GH
3,9=V
4,0=A
############
@PeilungTFTPortrait
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
color=ffff00,000033
color=bbbbbb,000000
0,2=xN Top:
0,8=xCourse Top:
color=ffff00,000033
1,0=g0NCS,35,ffff00,000044,6,33ff33,5,eeaa00
1,7=g0CCS,35,ffff00,000044,6,55ff55,5,eeaa00
color=ffffff,000000
6,0=xDirection:
6,8,4=gI
7,0=xCOG:
7,4,4=gC
7,8=xturn:
7,12,4=gB
############
@GPSdataTFTPortrait
timer=-1,-1,N
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
0,0=xOn-board GPS:
1,0,8=gA
2,0,8=gO
3,0,8=gH
4,0,8=gC
5,0=xGPS vs Sonde:
6,0,8=gD
7,0,8=gI
7,8,8=gB
############
@BatteryTFTPortrait
timer=-1,-1,-1
key1action=+,0,F,W
key2action=>,#,#,#
timeaction=#,#,#
0,0=xBattery status:
0,14=bS
1,0=xBatt:
1,5,5=bVV
2,0,16=bCmA(charging)
3,0,16=bDmA(discharging)
4.4,0=xUSB:
4.4,5,5=bUV
5.4,0,10=bImA
6.4,0=xTemp:
6.4,5,5=bT C

146
RX_FSK/data/style.css 100644 → 100755
Wyświetl plik

@ -44,7 +44,7 @@ td#sfreq {
outline: none;
cursor: pointer;
padding: 10px 10px;
width: 16vw;
width: 12vw;
transition: 0.3s;
}
@ -128,3 +128,147 @@ p{
margin: 0;
display: block;
}
#map {
height: 100%;
}
.leaflet-popup-content table, .leaflet-popup-content table td {
border:0;
background-color: white;
}
.leaflet-popup-content table td:nth-child(2),.leaflet-popup-content table td:nth-child(5) {
text-align: right;
padding-left: 3px;
}
.leaflet-popup-content table td:nth-child(3),.leaflet-popup-content table td:nth-child(6) {
text-align: left;
padding-right: 10px;
}
.leaflet-gps{animation:fading 1s infinite}@keyframes fading{0%{opacity:0.7}50%{opacity:1}100%{opacity:0.7}}
.leaflet-gps::after {
content: '🔵';
}
.leaflet-gps {
margin-left: -7px !important;
margin-top: -9px !important;
}
.leaflet-burst::after {
content: '💥';
}
.leaflet-burst {
margin-left: -20px !important;
margin-top: -22px !important;
font-weight: bold;
font-size: 30px;
}
.leaflet-landing::after {
content: '×';
}
.leaflet-landing {
margin-left: -13px !important;
margin-top: -30px !important;
font-weight: bold;
font-size: 40px;
}
.leaflet-header {
text-align: center;
width: 250px;
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
pointer-events: auto !important;
}
.leaflet-header #settings {
display: none;
}
.leaflet-header label {
display: block;
margin-top: 5px;
}
.leaflet-header input {
width: 80px;
margin: 0 auto;
}
.leaflet-header #submit {
margin: 3px auto;
}
.leaflet-footer {
display:none;
text-align: center;
width: 180px;
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-center {
left:0;
right:0;
margin: 0 auto;
padding: 5px;
background: #fff;
background: rgba(255, 255, 255, 0.8);
}
.leaflet-header #sonde_detail {
display:none;
}
@media screen and (max-width: 600px) {
.leaflet-control-attribution {
-moz-transform: rotate(-90deg) translateX(100%);
-ms-transform: rotate(-90deg) translateX(100%);
-o-transform: rotate(-90deg) translateX(100%);;
-webkit-transform: rotate(-90deg) translateX(100%);
transform: rotate(-90deg) translateX(100%);
-webkit-transform-origin: 100% 100%;
-moz-transform-origin: 100% 100%;
-ms-transform-origin: 100% 100%;
-o-transform-origin: 100% 100%;
transform-origin: 100% 100%;
}
}
.ldot {
height: 15px;
width: 15px;
margin-top: 8px;
margin-left: -1px;
border-radius: 50%;
display: inline-block;
}
.ybg {
background-color: orange;
background-image: -webkit-gradient(linear, left top, left bottom, from(yellow), to(orange));
background-image: linear-gradient(top, yellow, orange);
}
.gbg {
background-color: green;
background-image: -webkit-gradient(linear, left top, left bottom, from(lime), to(green));
background-image: linear-gradient(top, lime, green);
}
.rbg {
background-color: red;
background-image: -webkit-gradient(linear, left top, left bottom, from(orange), to(red));
background-image: linear-gradient(top, orange, red);
}
#sonde_statbar .ldot {
margin-right: 3px;
}

12
RX_FSK/features.h 100644
Wyświetl plik

@ -0,0 +1,12 @@
// Configuration flags for including/excluding fuctionality from the compiled binary
// set flag to 0 for exclude/1 for include
/* data feed to sondehubv2 */
/* needs about 4k4 code, 200b data, 200b stack, 200b heap */
#define FEATURE_SONDEHUB 1
#define FEATURE_MQTT 1
#define FEATURE_RS92 1

Wyświetl plik

@ -1,4 +1,4 @@
const char *version_name = "rdzTTGOsonde";
const char *version_id = "devel20210316";
const char *version_id = "devel20210907";
const int SPIFFS_MAJOR=2;
const int SPIFFS_MINOR=10;
const int SPIFFS_MINOR=14;

115
fontconverter 100755
Wyświetl plik

@ -0,0 +1,115 @@
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $fontname = "Terminal11x16";
## https://github.com/moononournation/Arduino_GFX/blob/master/src/Arduino_GFX.cpp: baseline = yadvance * 2 / 3
## so we have to set yadvance = (baseline * 3 / 2).
## baseofs is baseline offset from bottom, i.e. baseline = height - baseofs
## so lets try... (for Terminal11x16, baseline is 14 pixel from top, height is 16)
my $baseofs = 4;
my @data;
# read font bitmap
while(<>) {
my @dt = map { $_=~/(0x..)/ ? (hex($1)) : () } split(/,/,$_);
push @data, @dt;
}
my $width = shift @data;
my $height = shift @data;
my $first = shift @data;
my $count = shift @data;
my $nbrows = int(($height+7)/8);
my $charofs = $width * $nbrows + 1;
## process all glyphs
my @bitmap;
my @glyph;
my $bitofs = 0;
#$count=2;
sub optimize_glyph {
my $w = shift;
my $h = shift;
my $pix = shift;
my $xofs = 0;
my $yofs = 0;
# shrink 2D pixel array if possible
while(@$pix) {
if ( @{$pix->[0]} == grep { $_ == 0 } @{$pix->[0]} ) { shift(@$pix); $yofs++; $h--; }
else { last; }
}
while(@$pix) {
if ( @{$pix->[-1]} == grep { $_ == 0 } @{$pix->[-1]} ) { pop(@$pix); $h--; }
else { last; }
}
# transform 2D pixel array into compact byte sequence
my @all;
for(@$pix) { push @all, @$_; }
#print("pix size: ".(@all)."\n");
my @res;
while(@all) {
my @b = splice(@all,0,8);
while(@b<8) { push @b,0; }
my $res = 0;
while(@b) { $res = ($res*2) + (shift(@b)?1:0); }
push @res,$res;
}
return ($w, $h, \@res, $xofs, $yofs);
}
for my $c ($first .. $first+$count-1) {
# Convert char $c
my @gly = splice(@data,0,$charofs);
my $glywidth = shift @gly;
my @pixmap;
# print("Converting $c ($width x $height => nbrows=$nbrows)");
for my $x (0..$glywidth-1) {
for my $y (0..$height-1) {
my $g = $gly[$x * $nbrows + int($y/8)];
$pixmap[$y]->[$x] = (($g >> ($y%8))&1) ? 1 : 0;
#print("$x, $y : $pixmap[$y]->[$x]\n");
}
}
# pixmap is now my glyph
my ($w, $h, $d, $xofs, $yofs) = optimize_glyph($glywidth, $height, \@pixmap);
my $glyph = {
"offset" => $bitofs,
"width" => $w,
"height" => $h,
"xadv" => $w+1,
"xofs" => $xofs,
"yofs" => -$height + $yofs + $baseofs,
"c" => $c,
};
push @bitmap, @$d;
$bitofs += @$d;
push @glyph, $glyph;
#print(Dumper($glyph,$d));
}
#print(Dumper(\@glyph));
printf("const uint8_t %sBitmap[] = {\n", $fontname);
while(my @bm = splice(@bitmap,0,12)) { print( " ".join(", ", map { sprintf("0x%02X",$_) } @bm ).",\n" ); }
print("};\n\n");
printf("const GFXglyph %sGlyphs[] = {\n", $fontname);
for my $gl (@glyph) {
printf(" { %5d, %3d, %3d, %3d, %4d, %4d }, // 0x%02x '%c'\n ", $gl->{"offset"}, $gl->{"width"}, $gl->{"height"},
$gl->{"xadv"}, $gl->{"xofs"}, $gl->{"yofs"}, $gl->{"c"}, $gl->{"c"});
}
print("};\n");
printf("const GFXfont %sFont = {\n", $fontname);
printf(" (uint8_t *)%sBitmap,\n", $fontname);
printf(" (GFXglyph *)%sGlyphs,\n", $fontname);
printf(" 0x%02X, 0x%02X, %d };\n", $first, $first+$count-1, ($height-$baseofs)*3/2);

Wyświetl plik

@ -4,7 +4,7 @@
#include "SX1278FSK.h"
#include "Sonde.h"
#define DFM_DEBUG 1
#define DFM_DEBUG 0
#if DFM_DEBUG
#define DFM_DBG(x) x
@ -23,8 +23,11 @@ static struct st_dfmstat {
uint8_t start[50];
uint16_t dat[50*2];
uint8_t cnt[50*2];
uint16_t good;
uint16_t msec;
uint8_t nameregok;
uint8_t nameregtop;
uint8_t lastdat;
} dfmstate;
int DFM::setup(float frequency, int type)
@ -60,36 +63,8 @@ int DFM::setup(float frequency, int type)
return 1;
}
if(type == STYPE_DFM09_OLD || type == STYPE_DFM06_OLD) {
// packet mode, old version, misses some frames because chip enables rx too late after
// one frame was recevied. TODO: check if this can be fixed by changing parameters
// Enable auto-AFC, auto-AGC, RX Trigger by preamble
if(sx1278.setRxConf(0x1E)!=0) {
DFM_DBG(Serial.println("Setting RX Config FAILED"));
return 1;
}
// Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte
//char header[] = "0110.0101 0110.0110 1010.0101 1010.1010";
const char *SYNC=(stype==STYPE_DFM09_OLD)?"\x9A\x99\x5A\x55":"\x65\x66\xA5\xAA";
if(sx1278.setSyncConf(0x53, 4, (const uint8_t *)SYNC)!=0) {
DFM_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
//if(sx1278.setPreambleDetect(0xA8)!=0) {
if(sx1278.setPreambleDetect(0xAA)!=0) {
DFM_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
// Packet config 1: fixed len, mancecer, no crc, no address filter
// Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0)
if(sx1278.setPacketConfig(0x28, 0x40)!=0) {
DFM_DBG(Serial.println("Setting Packet config FAILED"));
return 1;
}
sx1278.setPayloadLength(33); // Expect 33 bytes (7+13+13 bytes)
} else {
// DFM OLD support has been removed
{
// continuous mode
// Enable auto-AFC, auto-AGC, RX Trigger by preamble ????
if(sx1278.setRxConf(0x1E)!=0) {
@ -269,6 +244,7 @@ void DFM::finddfname(uint8_t *b)
if(i==6) {
snprintf(sonde.si()->id, 10, "D%x ", id);
sonde.si()->validID = true;
sonde.si()->subtype = (st>>4)&0x0F;
strncpy(sonde.si()->typestr, typestr[ (st>>4)&0x0F ], 5);
return;
}
@ -319,6 +295,7 @@ void DFM::finddfname(uint8_t *b)
Serial.print("\nNEW AUTOID:");
Serial.println(sonde.si()->id);
sonde.si()->validID = true;
sonde.si()->subtype = (st>>4)&0x0F;
strncpy(sonde.si()->typestr, typestr[ (st>>4)&0x0F ], 5);
}
if(dfmstate.nameregok==i) {
@ -347,39 +324,10 @@ void DFM::finddfname(uint8_t *b)
void DFM::decodeCFG(uint8_t *cfg)
{
#if 1
// new ID
finddfname(cfg);
// new aprs ID (dxlaprs, autorx) is now "D" + serial (8 digits) by consensus
memcpy(sonde.si()->ser, sonde.si()->id+1, 9);
#else
// old ID
static int lowid, highid, idgood=0, type=0;
if((cfg[0]>>4)==0x06 && type==0) { // DFM-6 ID
lowid = ((cfg[0]&0x0F)<<20) | (cfg[1]<<12) | (cfg[2]<<4) | (cfg[3]&0x0f);
Serial.print("DFM-06 ID: "); Serial.print(lowid, HEX);
snprintf(sonde.si()->id, 10, "%x", lowid);
sonde.si()->validID = true;
}
if((cfg[0]>>4)==0x0A) { // DMF-9 ID
type=9;
if(cfg[3]==1) {
lowid = (cfg[1]<<8) | cfg[2];
idgood |= 1;
} else {
highid = (cfg[1]<<8) | cfg[2];
idgood |= 2;
}
if(idgood==3) {
uint32_t dfmid = (highid<<16) | lowid;
Serial.print("DFM-09 ID: "); Serial.print(dfmid);
snprintf(sonde.si()->ser, 10, "%d", dfmid);
// dxlAPRS sonde number (DF6 (why??) and 5 last digits of serial number as hex number
snprintf(sonde.si()->id, 9, "DF6%05X", dfmid&0xfffff);
sonde.si()->validID = true;
}
}
#endif
}
static int bitCount(int x) {
@ -390,23 +338,30 @@ static int bitCount(int x) {
return s1;
}
static uint16_t MON[]={0,0,31,59,90,120,151,181,212,243,273,304,334};
uint16_t MON[]={0,0,31,59,90,120,151,181,212,243,273,304,334};
void DFM::decodeDAT(uint8_t *dat)
{
SondeInfo *si = sonde.si();
Serial.print(" DAT["); Serial.print(dat[6]); Serial.print("]: ");
// We can have a 8 and 0 subframe in a single frame. So do the reset only for dat>0
if( !(dat[6]==0 && dfmstate.lastdat==8) ) { // if we have DAT8 + DAT0, don't reset before returing the 8 frame...
if(dat[6] < dfmstate.lastdat) dfmstate.good = 0; // next iteration detected
}
dfmstate.lastdat = dat[6];
dfmstate.good |= (1<<dat[6]);
switch(dat[6]) {
case 0:
Serial.print("Packet counter: "); Serial.print(dat[3]);
sonde.si()->frame = dat[3];
si->frame = dat[3];
break;
case 1:
{
int val = (((uint16_t)dat[4])<<8) + (uint16_t)dat[5];
Serial.print("UTC-msec: "); Serial.print(val);
sonde.si()->sec = val/1000;
dfmstate.msec = val;
uint32_t tmp = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + ((uint32_t)dat[3]);
sonde.si()->sats = bitCount(tmp); // maybe!?!?!?
si->sats = bitCount(tmp);
}
break;
case 2:
@ -416,9 +371,9 @@ void DFM::decodeDAT(uint8_t *dat)
vh = ((uint16_t)dat[4]<<8) + dat[5];
Serial.print("GPS-lat: "); Serial.print(lat*0.0000001);
Serial.print(", hor-V: "); Serial.print(vh*0.01);
sonde.si()->lat = lat*0.0000001;
sonde.si()->hs = vh*0.01;
sonde.si()->validPos |= 0x11;
si->lat = lat*0.0000001;
si->hs = vh*0.01;
si->validPos |= 0x11;
}
break;
case 3:
@ -428,9 +383,9 @@ void DFM::decodeDAT(uint8_t *dat)
dir = ((uint16_t)dat[4]<<8) + dat[5];
Serial.print("GPS-lon: "); Serial.print(lon*0.0000001);
Serial.print(", dir: "); Serial.print(dir*0.01);
sonde.si()->lon = lon*0.0000001;
sonde.si()->dir = dir*0.01;
sonde.si()->validPos |= 0x42;
si->lon = lon*0.0000001;
si->dir = dir*0.01;
si->validPos |= 0x42;
}
break;
case 4:
@ -440,9 +395,9 @@ void DFM::decodeDAT(uint8_t *dat)
vv = (int16_t)( ((int16_t)dat[4]<<8) | dat[5] );
Serial.print("GPS-height: "); Serial.print(alt*0.01);
Serial.print(", vv: "); Serial.print(vv*0.01);
sonde.si()->alt = alt*0.01;
sonde.si()->vs = vv*0.01;
sonde.si()->validPos |= 0x0C;
si->alt = alt*0.01;
si->vs = vv*0.01;
si->validPos |= 0x0C;
}
break;
case 8:
@ -459,7 +414,14 @@ void DFM::decodeDAT(uint8_t *dat)
int tt = (y-1970)*365 + (y-1969)/4; // days since 1970
if(m<=12) { tt += MON[m]; if((y%4)==0 && m>2) tt++; }
tt = (tt+d-1)*(60*60*24) + h*3600 + mi*60;
sonde.si()->time = tt;
si->time = tt + dfmstate.msec/1000;
// Lets be consistent with autorx: the timestamp uses the msec value truncated to seconds,
// whereas the virtual frame number for DFM uses the msec value rounded to full seconds.
// Actually, tt is real UTC, and the transformation to GPS seconds lacks adjusting for leap seconds
si->vframe = tt + (dfmstate.msec+500)/1000 - 315964800;
// maybe TODO: if we missed the type 0 frame, we still might caculate the right seconds from system time.
// but we only send time stamps to external servers (in particular to sondehub), if all
// required frame types have been correctly decoded, so this does not matter much.
}
break;
default:
@ -536,19 +498,11 @@ int DFM::processDFMdata(uint8_t dt) {
}
int DFM::receive() {
if( stype == STYPE_DFM ) {
return receiveNew();
} else {
return receiveOld();
}
}
int rxframes = 5; // UP TO 5 frames, stop at type 8 frame
int DFM::receiveNew() {
int rxframes = 4;
// tentative continuous RX version...
unsigned long t0 = millis();
while( ( millis() - t0 ) < 1000 ) {
while( ( millis() - t0 ) < 1300 ) {
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
if ( bitRead(value, 7) ) {
Serial.println("FIFO full");
@ -568,38 +522,24 @@ int DFM::receiveNew() {
processDFMdata(data);
value = sx1278.readRegister(REG_IRQ_FLAGS2);
} else {
#if 0
if(headerDetected) {
t0 = millis(); // restart timer... don't time out if header detected...
headerDetected = 0;
}
#endif
if(haveNewFrame) {
//Serial.printf("DFM::receive(): new frame complete after %ldms\n", millis()-t0);
Serial.printf("receive newframe: %d, good: %x\n", rxframes, dfmstate.good);
haveNewFrame = 0;
rxframes--;
if(rxframes==0) return RX_OK;
if(dfmstate.good & 0x100) {
if(dfmstate.good & 0x11E) {
dfmstate.good = 0; return RX_OK; // type 8 frame has been received
} else {
dfmstate.good = 0; return RX_ERROR;
}
}
if(rxframes==0) return RX_ERROR;
}
delay(2);
}
}
return RX_TIMEOUT;
}
int DFM::receiveOld() {
byte data[1000]; // pending data from previous mode may write more than 33 bytes. TODO.
for(int i=0; i<2; i++) {
sx1278.setPayloadLength(33); // Expect 33 bytes (7+13+13 bytes)
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
//int t = millis();
int e = sx1278.receivePacketTimeout(1000, data);
//Serial.printf("rxPTO done after %d ms", (int)(millis()-t));
if(e) { return RX_TIMEOUT; } //if timeout... return 1
if(!(stype==STYPE_DFM09_OLD)) { for(int i=0; i<33; i++) { data[i]^=0xFF; } }
decodeFrameDFM(data);
}
return RX_OK;
return rxframes == 5 ? RX_TIMEOUT : RX_OK;
}
int DFM::decodeFrameDFM(uint8_t *data) {

Wyświetl plik

@ -1,3 +1,4 @@
#include "../../RX_FSK/features.h"
#include <U8x8lib.h>
#include <U8g2lib.h>
#include <SPIFFS.h>
@ -16,6 +17,7 @@ extern const char *version_id;
#include <fonts/FreeSans9pt7b.h>
#include <fonts/FreeSans12pt7b.h>
#include <fonts/Picopixel.h>
#include <fonts/Terminal11x16.h>
extern Sonde sonde;
@ -23,13 +25,16 @@ extern AXP20X_Class axp;
extern bool axp192_found;
extern SemaphoreHandle_t axpSemaphore;
extern xSemaphoreHandle globalLock;
#define SPI_MUTEX_LOCK() \
do \
{ \
} while (xSemaphoreTake(globalLock, portMAX_DELAY) != pdPASS)
#define SPI_MUTEX_UNLOCK() xSemaphoreGive(globalLock)
struct GpsPos gpsPos;
SPIClass spiDisp(HSPI);
const char *sondeTypeStr[NSondeTypes] = { "DFM ", "DFM9", "RS41", "RS92", "M10 ", "M20 ", "DFM6" };
const char *sondeTypeLongStr[NSondeTypes] = { "DFM (all)", "DFM9 (old)", "RS41", "RS92", "M10 ", "M20 ", "DFM6 (old)" };
const char sondeTypeChar[NSondeTypes] = { 'D', '9', '4', 'R', 'M', '2', '6' };
//SPIClass spiDisp(HSPI);
byte myIP_tiles[8*11];
static uint8_t ap_tile[8]={0x00,0x04,0x22,0x92, 0x92, 0x22, 0x04, 0x00};
@ -187,7 +192,7 @@ DispInfo staticLayouts[5] = {
/////////////// Wrapper code for various display
// ALLFONTS requires 30k extra flash memory... for now there is still enough space :)
#define ALLFONTS 1
//#define ALLFONTS 1
static const uint8_t *fl[] = {
u8x8_font_chroma48medium8_r, // 0 ** default small
u8x8_font_7x14_1x2_f, // 1 ** default large
@ -311,8 +316,8 @@ void U8x8Display::drawQS(uint8_t x, uint8_t y, uint8_t len, uint8_t /*size*/, ui
const GFXfont *gfl[] = {
&FreeMono9pt7b, // 3
&FreeMono12pt7b, // 4
&Terminal11x16Font, // 3 (replacement for 1 or 2 with old library)
&Terminal11x16Font, // 4 (replacement for 1 or 2 with old library)
&FreeSans9pt7b, // 5
&FreeSans12pt7b, // 6
&Picopixel, // 7
@ -324,8 +329,8 @@ struct gfxoffset_t {
// first value: offset: max offset from font glyphs (last column * (-1)) (check /, \, `, $)`
// yclear:max height: max of (height in 3rd column) + (yofs + 6th column) (check j)
const struct gfxoffset_t gfxoffsets[]={
{ 11, 15 }, // 13+11-9 "j"
{ 15, 20 }, // 19+15-14
{ 16, 18},
{ 16, 18},
{ 13, 18 }, // 17+13-12 "j"
{ 17, 23 }, // 23+17-17
{ 4, 6}, // 6+4-4
@ -336,26 +341,49 @@ static int ngfx = sizeof(gfl)/sizeof(GFXfont *);
#define TFT_LED 0 // 0 if wired to +5V directly
#define TFT_BRIGHTNESS 100 // Initial brightness of TFT backlight (optional)
Arduino_DataBus *bus;
void ILI9225Display::begin() {
tft = new MY_ILI9225(sonde.config.oled_rst, sonde.config.tft_rs, sonde.config.tft_cs,
sonde.config.oled_sda, sonde.config.oled_scl, TFT_LED, TFT_BRIGHTNESS);
tft->setModeFlip(sonde.config.tft_modeflip);
tft->begin(spiDisp);
tft->setOrientation(sonde.config.tft_orient);
Serial.println("ILI9225/ILI9341 init");
// On the M5, the display and the Lora chip are on the same SPI interface (VSPI default pins),
// we must use the same SPI bus with correct locking
if(sonde.config.type == TYPE_M5_CORE2) {
bus = new Arduino_ESP32SPI( sonde.config.tft_rs, sonde.config.tft_cs,
sonde.config.oled_scl, sonde.config.oled_sda, 38, VSPI);
} else {
bus = new Arduino_ESP32SPI( sonde.config.tft_rs, sonde.config.tft_cs,
sonde.config.oled_scl, sonde.config.oled_sda, -1, HSPI);
}
if(_type == 3)
tft = new Arduino_ILI9341(bus, sonde.config.oled_rst);
else if(_type == 4)
tft = new Arduino_ILI9342(bus, sonde.config.oled_rst);
else
tft = new Arduino_ILI9225(bus, sonde.config.oled_rst);
Serial.println("ILI9225/ILI9341 init: done");
tft->begin(sonde.config.tft_spifreq);
tft->fillScreen(BLACK);
tft->setRotation(sonde.config.tft_orient);
tft->setTextWrap(false);
if(sonde.config.type == TYPE_M5_CORE2)
tft->invertDisplay(true);
}
void ILI9225Display::clear() {
tft->clear();
SPI_MUTEX_LOCK();
tft->fillScreen(BLACK);
SPI_MUTEX_UNLOCK();
}
// for now, 0=small=FreeSans9pt7b, 1=large=FreeSans18pt7b
void ILI9225Display::setFont(uint8_t fontindex) {
if(fontindex==1 || fontindex==2) { fontindex=3; }
findex = fontindex;
switch(fontindex) {
case 0: tft->setFont(Terminal6x8); break;
case 1: tft->setFont(Terminal11x16); break;
case 2: tft->setFont(Terminal12x16); break;
default: tft->setGFXFont(gfl[fontindex-3]);
case 0: tft->setFont(NULL); tft->setTextSize(1); break;
case 1: tft->setFont(NULL); tft->setTextSize(2); break;
case 2: tft->setFont(NULL); tft->setTextSize(2); break;
default: tft->setFont(gfl[fontindex-3]);
}
}
@ -377,11 +405,13 @@ void ILI9225Display::getDispSize(uint8_t *height, uint8_t *width, uint8_t *lines
break;
default: // get size from GFX Font
{
int16_t w,h,a;
tft->getGFXCharExtent('|',&w,&h,&a);
int16_t x, y;
uint16_t w, h;
tft->getTextBounds("|", 0, 0, &x, &y, &w, &h);
if(lineskip) *lineskip = h+2;
tft->getGFXCharExtent('A',&w,&h,&a);
if(colskip) *colskip = w+2; // just an approximation
tft->getTextBounds("A", 0, 0, &x, &y, &w, &h);
if(colskip) *colskip = w+2;
if(lineskip&&colskip) { Serial.printf("skip size from bounds: %d, %d\n", *lineskip, *colskip); }
}
}
}
@ -397,28 +427,49 @@ void ILI9225Display::drawString(uint8_t x, uint8_t y, const char *s, int16_t wid
}
// Standard font
if(findex<3) {
SPI_MUTEX_LOCK();
DebugPrintf(DEBUG_DISPLAY, "Simple Text %s at %d,%d [%d]\n", s, x, y, width);
tft->setBackgroundColor(bg);
int h = tft->getFont().height;
// for gpx fonts and new library, cursor is at baseline!!
int h = 6; if(findex>1) h=12;
if( alignright ) {
#if 1
//w = tft->getTextWidth(s);
/// TODO
if( width==WIDTH_AUTO ) { width = w; }
if( width > w ) {
tft->writeFillRect(x, y, width - w, h - 1, bg);
}
tft->setCursor(x + width - w, y);
tft->setTextColor(fg, bg);
tft->print(s);
#else
w = tft->getTextWidth(s);
if( width==WIDTH_AUTO ) { width = w; }
if( width > w ) {
tft->fillRectangle(x, y, x + width - w, y + h - 1, bg);
}
tft->drawText(x + width - w, y, s, fg);
#endif
} else {
int curx = tft->drawText(x, y, s, fg);
if( width==WIDTH_AUTO ) { return; }
if(curx < x + width) {
tft->fillRectangle(curx, y, x + width - 1, y + h - 1, bg);
}
tft->setCursor(x, y);
tft->setTextColor(fg, bg);
tft->print(s);
// curx???
//i//int curx = tft->drawText(x, y, s, fg);
//if( width==WIDTH_AUTO ) { return; }
//if(curx < x + width) {
// tft->fillRectangle(curx, y, x + width - 1, y + h - 1, bg);
//}
}
SPI_MUTEX_UNLOCK();
return;
}
// GFX font
if(width==WIDTH_AUTO || alignright) {
tft->getGFXTextExtent(s, x, y + gfxoffsets[findex-3].yofs, &w, &h);
SPI_MUTEX_LOCK();
int16_t x1, y1;
if(1||width==WIDTH_AUTO || alignright) {
tft->getTextBounds(s, x, y + gfxoffsets[findex-3].yofs, &x1, &y1, (uint16_t *)&w, (uint16_t *)&h);
w += x1 - x + 1;
if(width==WIDTH_AUTO) { width=w; }
if(alignright) {
if(w > width) {
@ -444,6 +495,22 @@ void ILI9225Display::drawString(uint8_t x, uint8_t y, const char *s, int16_t wid
}
#else
// Text by drawing bitmap.... => less "flicker"
#if 1
//TODO
tft->setCursor( alignright? x+width-w : x, y + gfxoffsets[findex-3].yofs);
tft->setTextColor( fg, bg );
tft->print(s);
uint16_t height = gfxoffsets[findex-3].yclear;
if(alignright) {
// fill with bg from x+w to width
if(width>w) tft->fillRect( x, y, width-w, height, bg);
DebugPrintf(DEBUG_DISPLAY,"rtext fill %d %d %d %d -- %d %d\n", x, y, width-w, height, x1, y1);
} else {
// fill with bg from x+w to width
if(width>w) tft->fillRect( x+w, y, width-w, height, bg);
DebugPrintf(DEBUG_DISPLAY,"ltext fill %d %d %d %d -- %d %d\n", x+w, y, width-w, height, x1, y1);
}
#else
uint16_t height = gfxoffsets[findex-3].yclear;
uint16_t *bitmap = (uint16_t *)malloc(sizeof(uint16_t) * width * height);
if(!bitmap) {
@ -465,10 +532,23 @@ void ILI9225Display::drawString(uint8_t x, uint8_t y, const char *s, int16_t wid
drawBitmap(x, y, bitmap, width, height);
free(bitmap);
#endif
#endif
SPI_MUTEX_UNLOCK();
}
void ILI9225Display::drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr) {
tft->drawTile(x, y, cnt, tile_ptr);
int i,j;
SPI_MUTEX_LOCK();
tft->startWrite();
for(i=0; i<cnt*8; i++) {
uint8_t v = tile_ptr[i];
for(j=0; j<8; j++) {
tft->writePixel(8*x+i, 8*y+j, (v&0x01) ? GREEN:BLUE);
v >>= 1;
}
}
tft->endWrite();
SPI_MUTEX_UNLOCK();
#if 0
int i,j;
tft->startWrite();
@ -486,18 +566,24 @@ void ILI9225Display::drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_p
}
void ILI9225Display::drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color, boolean fill) {
SPI_MUTEX_LOCK();
if(fill)
tft->fillTriangle(x1, y1, x2, y2, x3, y3, color);
else
tft->drawTriangle(x1, y1, x2, y2, x3, y3, color);
SPI_MUTEX_UNLOCK();
}
void ILI9225Display::drawBitmap(uint16_t x1, uint16_t y1, const uint16_t* bitmap, int16_t w, int16_t h) {
tft->drawBitmap(x1, y1, bitmap, w, h);
SPI_MUTEX_LOCK();
tft->draw16bitRGBBitmap(x1, y1, bitmap, w, h);
SPI_MUTEX_UNLOCK();
}
void ILI9225Display::welcome() {
tft->clear();
SPI_MUTEX_LOCK();
tft->fillScreen(0);
SPI_MUTEX_UNLOCK();
setFont(6);
drawString(0, 0*22, version_name, WIDTH_AUTO, 0xff00);
setFont(5);
@ -544,6 +630,10 @@ void ILI9225Display::drawQS(uint8_t x, uint8_t y, uint8_t len, uint8_t size, uin
#include <pgmspace.h>
#define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr))
#if 1
#else
// TO BE REMOVED
void MY_ILI9225::drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr) {
int i,j;
startWrite();
@ -592,6 +682,7 @@ uint16_t MY_ILI9225::drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t
return (uint16_t)xa;
}
#endif
///////////////
@ -603,8 +694,8 @@ RawDisplay *Display::rdis = NULL;
//TODO: maybe merge with initFromFile later?
void Display::init() {
Serial.printf("disptype is %d\n",sonde.config.disptype);
if(sonde.config.disptype==1) {
rdis = new ILI9225Display();
if(sonde.config.disptype==1 || sonde.config.disptype==3 || sonde.config.disptype==4 ) {
rdis = new ILI9225Display(sonde.config.disptype);
} else {
rdis = new U8x8Display(sonde.config.disptype);
}
@ -844,17 +935,33 @@ int Display::countEntries(File f) {
return n;
}
int Display::getScreenIndex(int index) {
if(index!=0) return index;
switch(sonde.config.disptype) {
case 1: // ILI9225
index = 2; // landscape mode (orient=1/3)
if( (sonde.config.tft_orient&0x01)==0 ) index++; // portrait mode (0/2)
break;
case 3: // ILI9341
case 4: // ILI9342
index = 4; // landscape mode (orient=1/3)
if( (sonde.config.tft_orient&0x01)==0 ) index++; // portrait mode (0/2)
break;
case 0: case 2: // small OLED display (SD1306/SH1106)
default:
index = 1; break;
}
return index;
}
void Display::initFromFile(int index) {
File d;
if(index>0) {
char file[20];
snprintf(file, 20, "/screens%d.txt", index);
Serial.printf("Reading %s\n", file);
d = SPIFFS.open(file, "r");
if(!d || d.available()==0 ) { Serial.printf("%s not found, using /screens.txt\n", file); }
}
if(!d || d.available()==0 ) d = SPIFFS.open("/screens.txt", "r");
if(!d) return;
char file[20];
index = getScreenIndex(index); // auto selection for index==0
snprintf(file, 20, "/screens%d.txt", index);
Serial.printf("Reading %s\n", file);
d = SPIFFS.open(file, "r");
if(!d || d.available()==0 ) { Serial.printf("%s not found\n", file); return; }
DispInfo *newlayouts = (DispInfo *)malloc(MAXSCREENS * sizeof(DispInfo));
if(!newlayouts) {
@ -958,7 +1065,7 @@ void Display::initFromFile(int index) {
char text[61];
n=sscanf(s, "%f,%f,%f", &y, &x, &w);
sscanf(ptr+1, "%60[^\r\n]", text);
if(sonde.config.disptype==1) { x*=xscale; y*=yscale; w*=xscale; }
if(sonde.config.disptype==1 || sonde.config.disptype==3 || sonde.config.disptype==4 ) { x*=xscale; y*=yscale; w*=xscale; }
newlayouts[idx].de[what].x = x;
newlayouts[idx].de[what].y = y;
newlayouts[idx].de[what].width = n>2 ? w : WIDTH_AUTO;
@ -1116,6 +1223,7 @@ void Display::drawID(DispEntry *de) {
}
void Display::drawRSSI(DispEntry *de) {
rdis->setFont(de->fmt);
// TODO.... 3/4!!!!!
if(sonde.config.disptype!=1) {
snprintf(buf, 16, "-%d ", sonde.si()->rssi/2);
int len=strlen(buf)-3;
@ -1147,10 +1255,7 @@ void Display::drawFreq(DispEntry *de) {
drawString(de, buf);
}
void Display::drawAFC(DispEntry *de) {
if(!sonde.config.showafc) return;
rdis->setFont(de->fmt);
//if(sonde.si()->afc==0) { strcpy(buf, " "); }
//else
{ snprintf(buf, 15, " %+3.2fk", sonde.si()->afc*0.001); }
drawString(de, buf+strlen(buf)-8);
}
@ -1163,7 +1268,7 @@ void Display::drawSite(DispEntry *de) {
switch(de->extra[0]) {
case '#':
// currentSonde is index in array starting with 0;
// but we draw "1" for the first entrie and so on...
// but we draw "1" for the first entry and so on...
snprintf(buf, 3, "%2d", sonde.currentSonde+1);
buf[2]=0;
break;
@ -1233,6 +1338,15 @@ void Display::drawKilltimer(DispEntry *de) {
#define FAKEGPS 0
extern int lastCourse; // from RX_FSK.ino
float calcLatLonDist(float lat1, float lon1, float lat2, float lon2) {
float x = radians(lon1-lon2) * cos( radians((lat1+lat2)/2) );
float y = radians(lat2-lat1);
float d = sqrt(x*x+y*y)*EARTH_RADIUS;
return d;
}
void Display::calcGPS() {
// base data
#if 0
@ -1264,12 +1378,7 @@ static int tmpc=0;
#endif
// distance
if( gpsPos.valid && (sonde.si()->validPos&0x03)==0x03 && (layout->usegps&GPSUSE_DIST)) {
float lat1 = gpsPos.lat;
float lat2 = sonde.si()->lat;
float x = radians(gpsPos.lon-sonde.si()->lon) * cos( radians((lat1+lat2)/2) );
float y = radians(lat2-lat1);
float d = sqrt(x*x+y*y)*EARTH_RADIUS;
gpsDist = (int)d;
gpsDist = (int)calcLatLonDist(gpsPos.lat, gpsPos.lon, sonde.si()->lat, sonde.si()->lon);
} else {
gpsDist = -1;
}
@ -1410,7 +1519,11 @@ void Display::drawGPS(DispEntry *de) {
}
Serial.printf("GPS0: %c%c%c N=%d, A=%d, B=%d\n", circinfo->top, circinfo->arr, circinfo->bul, angN, angA, angB);
// "N" in direction angN
#if 1
// TODO
#else
static_cast<ILI9225Display *>(rdis)->tft->drawGFXcharBM(x0 + circinfo->radius*sin(angN*PI/180)-6, y0 - circinfo->radius*cos(angN*PI/180)+7, 'N', 0xffff, bitmap, size, size);
#endif
// small circle in direction angB
if(validB) {
@ -1494,7 +1607,7 @@ void Display::drawBatt(DispEntry *de) {
snprintf(buf, 30, "%.2f%s", val, de->extra+1);
break;
case 'T':
val = axp.getTemp()-144.7; // WTF... library returns temperatur in K above -144.7°C!??
val = axp.getTemp(); // fixed in newer versions of libraray: -144.7 no longer needed here!
snprintf(buf, 30, "%.2f%s", val, de->extra+1);
break;
default:

Wyświetl plik

@ -1,4 +1,3 @@
#ifndef Display_h
#define Display_h
@ -6,7 +5,7 @@
#define FONT_SMALL 0
#include <SPI.h>
#include <TFT22_ILI9225.h>
#include <Arduino_GFX_Library.h>
#include <U8x8lib.h>
#include <SPIFFS.h>
@ -76,12 +75,12 @@ public:
class U8x8Display : public RawDisplay {
private:
U8X8 *u8x8 = NULL; // initialize later after reading config file
int _type;
uint8_t _type;
const uint8_t **fontlist;
int nfonts;
public:
U8x8Display(int type = 0) { _type = type; }
U8x8Display(uint8_t type = 0) { _type = type; }
void begin();
void clear();
void setFont(uint8_t fontindex);
@ -95,20 +94,17 @@ public:
void drawQS(uint8_t x, uint8_t y, uint8_t len, uint8_t size, uint8_t *stat, uint16_t fg=0xffff, uint16_t bg=0);
};
class MY_ILI9225 : public TFT22_ILI9225 {
using TFT22_ILI9225::TFT22_ILI9225;
public:
uint16_t drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color);
void drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr);
};
typedef Arduino_GFX MY_ILI9225;
class ILI9225Display : public RawDisplay {
private:
uint8_t yofs=0;
uint8_t findex=0;
uint8_t _type;
public:
MY_ILI9225 *tft = NULL; // initialize later after reading config file
ILI9225Display(int type = 1) { _type = type; }
void begin();
void clear();
void setFont(uint8_t fontindex);
@ -152,6 +148,7 @@ private:
return ret;
}
public:
static int getScreenIndex(int index);
void initFromFile(int index);
int layoutIdx;

Wyświetl plik

@ -276,12 +276,14 @@ int M10M20::decodeframeM10(uint8_t *data) {
}
Serial.println(crcok?"CRC OK":"CRC NOT OK");
Serial.printf(" repair: %d/%d\n", repl, repairstep);
if(!crcok) return 2;
if(data[1]==0x9F && data[2]==0x20) {
Serial.println("Decoding...");
SondeInfo *si = sonde.si();
// Its a M10
// getid...
char ids[11];
char ids[12];
ids[0] = 'M';
ids[1] = 'E';
ids[2] = hex(data[95]/16);
@ -297,29 +299,33 @@ int M10M20::decodeframeM10(uint8_t *data) {
ids[0] = hex(data[95]/16);
ids[1] = dez((data[95]&0x0f)/10);
ids[2] = dez((data[95]&0x0f));
ids[3] = dez(data[93]);
ids[4] = dez(id>>13);
ids[3] = '-';
ids[4] = dez(data[93]);
ids[5] = '-';
ids[6] = dez(id>>13);
id &= 0x1fff;
ids[5] = dez(id/1000);
ids[6] = dez((id/100)%10);
ids[7] = dez((id/10)%10);
ids[8] = dez(id%10);
strncpy(sonde.si()->ser, ids, 10);
sonde.si()->validID = true;
ids[7] = dez(id/1000);
ids[8] = dez((id/100)%10);
ids[9] = dez((id/10)%10);
ids[10] = dez(id%10);
ids[11] = 0;
strncpy(si->ser, ids, 12);
si->validID = true;
Serial.printf("ID is %s [%02x %02x %d]\n", ids, data[95], data[93], id);
// ID printed on sonde is ...-.-abbbb, with a=id>>13, bbbb=id&0x1fff in decimal
// position data
sonde.si()->lat = getint32(data+14) * DEGMUL;
sonde.si()->lon = getint32(data+18) * DEGMUL;
sonde.si()->alt = getint32(data+22) * 0.001;
si->lat = getint32(data+14) * DEGMUL;
si->lon = getint32(data+18) * DEGMUL;
si->alt = getint32(data+22) * 0.001;
float ve = getint16(data+4)*VMUL;
float vn = getint16(data+6)*VMUL;
sonde.si()->vs = getint16(data+8) * VMUL;
sonde.si()->hs = sqrt(ve*ve+vn*vn);
float dir = atan2(vn, ve)*(1.0/RAD);
si->vs = getint16(data+8) * VMUL;
si->hs = sqrt(ve*ve+vn*vn);
si->sats = data[30];
float dir = atan2(ve, vn)*(1.0/RAD);
if(dir<0) dir+=360;
sonde.si()->dir = dir;
sonde.si()->validPos = 0x3f;
si->dir = dir;
si->validPos = 0x3f;
uint32_t gpstime = getint32(data+10);
uint16_t gpsweek = getint16(data+32);
@ -328,13 +334,16 @@ int M10M20::decodeframeM10(uint8_t *data) {
// unix epoch starts jan 1st 1970 0:00
// gps time starts jan 6, 1980 0:00. thats 315964800 epoch seconds.
// subtracting 86400 yields 315878400UL
sonde.si()->time = (gpstime/1000) + 86382 + gpsweek*604800 + 315878400UL;
sonde.si()->validTime = true;
si->time = (gpstime/1000) + 86382 + gpsweek*604800 + 315878400UL;
// consistent with autorx, vframe is based on GPS time without the -18 seconds adjustment
// for the GPS time / UTC time difference (included in 86382 above)
si->vframe = si->time - 315964800 + 18;
si->validTime = true;
} else {
Serial.printf("data is %02x %02x %02x\n", data[0], data[1], data[2]);
return 0;
}
return crcok?1:2;
return 1;
}
static uint32_t rxdata;
@ -388,12 +397,14 @@ void M10M20::processM10data(uint8_t dt)
if(rxp==2 && dataptr[0]==0x45 && dataptr[1]==0x20) { isM20 = true; }
if(isM20) {
memcpy(sonde.si()->typestr, "M20 ", 5);
sonde.si()->subtype = 2;
if(rxp>=M20_FRAMELEN) {
rxsearching = true;
haveNewFrame = decodeframeM20(dataptr);
}
} else {
memcpy(sonde.si()->typestr, "M10 ", 5);
sonde.si()->subtype = 1;
if(rxp>=M10_FRAMELEN) {
rxsearching = true;
haveNewFrame = decodeframeM10(dataptr);
@ -475,6 +486,7 @@ int M10M20::decodeframeM20(uint8_t *data) {
int repl = 0;
bool crcok = false;
bool crcbok = false;
SondeInfo *si = sonde.si();
// error correction, inspired by oe5dxl's sondeudp
// check first block
uint8_t s[200];
@ -515,7 +527,7 @@ int M10M20::decodeframeM20(uint8_t *data) {
ids[0] = 'M';
ids[1] = 'E';
uint32_t id = getint16(data+18);
uint32_t id = data[18]; // getint16(data+18);
ids[2] = hex(id/16);
ids[3] = hex(id);
//
@ -525,39 +537,55 @@ int M10M20::decodeframeM20(uint8_t *data) {
ids[6] = (char)((id/100)%10+48);
ids[7] = (char)((id/10)%10+48);
ids[8] = (char)(id%10+48);
strncpy(si->id, ids, 10);
// Serial: AAB-C-DDEEE
char *ser = si->ser;
uint8_t tmp = data[18] & 0x7F;
ser[0] = (tmp/12) + '0';
ser[1] = ((tmp%12 + 1) / 10 ) + '0';
ser[2] = ((tmp%12 + 1) % 10 ) + '0';
ser[3] = '-';
ser[4] = (data[18]/128) + 1 + '0';
ser[5] = '-';
ser[6] = ids[4];
ser[7] = ids[5];
ser[8] = ids[6];
ser[9] = ids[7];
ser[10] = ids[8];
ser[11] = 0;
// TODO
strncpy(sonde.si()->ser, ids, 10);
if(crcok) {
sonde.si()->validID = true;
si->validID = true;
//Serial.printf("ID is %s [%02x %02x %d]\n", ids, data[95], data[93], id);
// ID printed on sonde is ...-.-abbbb, with a=id>>13, bbbb=id&0x1fff in decimal
// position data
// 0x1C 4 byte
sonde.si()->lat = getint32(data+28) * 1e-6;
si->lat = getint32(data+28) * 1e-6;
//0x20 4 byte
sonde.si()->lon = getint32(data+32) * 1e-6;
si->lon = getint32(data+32) * 1e-6;
//0x08 3 byte
sonde.si()->alt = getint24(data+8) * VMUL_M20;
si->alt = getint24(data+8) * VMUL_M20;
//0x0B 2 byte
//VMUL_M20 specific
float ve = getint16(data+11)*VMUL_M20;
//0x0D 2 byte
float vn = getint16(data+13)*VMUL_M20;
//0x18 2 byte
sonde.si()->vs = getint16(data+24) * VMUL_M20;
sonde.si()->hs = sqrt(ve*ve+vn*vn);
float dir = atan2(vn, ve)*(1.0/RAD);
si->vs = getint16(data+24) * VMUL_M20;
si->hs = sqrt(ve*ve+vn*vn);
float dir = atan2(ve, vn)*(1.0/RAD);
if(dir<0) dir+=360;
sonde.si()->dir = dir;
sonde.si()->validPos = 0x3f;
si->dir = dir;
si->validPos = 0x3f;
//0x0F 3 byte
uint32_t tow = getint24(data+15);
uint16_t week = getint16(data+26);
sonde.si()->time = (tow+week*604800+315964800)-18;
si->time = (tow+week*604800+315964800)-18;
si->vframe = sonde.si()->time - 315964800;
sonde.si()->validTime = true;
si->validTime = true;
}
return crcok?1:2;
}

Wyświetl plik

@ -0,0 +1,581 @@
/* MP3H decoder functions */
#include "MP3H.h"
#include "SX1278FSK.h"
#include "rsc.h"
#include "Sonde.h"
#include <SPIFFS.h>
#define MP3H_DEBUG 1
#if MP3H_DEBUG
#define MP3H_DBG(x) x
#else
#define MP3H_DBG(x)
#endif
static struct st_mp3hstate {
uint32_t id1, id2;
uint8_t idok;
uint32_t gpsdate;
uint32_t gpsdatetime;
bool dateok;
} mp3hstate;
static byte data1[512];
static byte *dataptr=data1;
static uint8_t rxbitc;
static uint16_t rxbyte;
static int rxp=0;
static int haveNewFrame = 0;
//static int lastFrame = 0;
static int headerDetected = 0;
extern uint16_t MON[];
int MP3H::setup(float frequency)
{
MP3H_DBG(Serial.println("Setup sx1278 for MP3H sonde"));;
if(sx1278.ON()!=0) {
MP3H_DBG(Serial.println("Setting SX1278 power on FAILED"));
return 1;
}
// setFSK: switches to FSK standby mode
if(sx1278.setFSK()!=0) {
MP3H_DBG(Serial.println("Setting FSK mode FAILED"));
return 1;
}
Serial.print("MP3H: setting RX frequency to ");
Serial.println(frequency);
int res = sx1278.setFrequency(frequency);
// Test: maybe fix issue after spectrum display?
sx1278.writeRegister(REG_PLL_HOP, 0);
if(sx1278.setAFCBandwidth(sonde.config.mp3h.agcbw)!=0) {
MP3H_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.mp3h.agcbw));
return 1;
}
if(sx1278.setRxBandwidth(sonde.config.mp3h.rxbw)!=0) {
MP3H_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.mp3h.rxbw));
return 1;
}
#if 0
/// TODO: Maybe do this conditionally? -- maybe skip if afc if agcbw set to 0 or -1?
//// Step 1: Tentative AFC mode
sx1278.clearIRQFlags();
// preamble detector + AFC + AGC on
// wait for preamble interrupt within 2sec
sx1278.setBitrate(4800);
// DetectorOn=1, Preamble detector size 01, preamble tol 0x0A (10)
sx1278.setPreambleDetect(0x80 | 0x20 | 0x0A);
// Manual start RX, Enable Auto-AFC, Auto-AGC, RX Trigger (AGC+AFC)by preamble
sx1278.setRxConf(0x20 | 0x10 | 0x08 | 0x06);
// Packet config 1: fixed len, no mancecer, no crc, no address filter
// Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0)
if(sx1278.setPacketConfig(0x08, 0x40)!=0) {
MP3H_DBG(Serial.println("Setting Packet config FAILED"));
return 1;
}
// enable RX
sx1278.setPayloadLength(0);
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
unsigned long t0 = millis();
MP3H_DBG(Serial.printf("MP3H::setup() AFC preamble search start at %ld\n",t0));
while( millis() - t0 < 1000 ) {
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS1);
if(value & 2) {
int32_t afc = sx1278.getAFC();
int16_t rssi = sx1278.getRSSI();
Serial.printf("MP3H::setup: preamble: AFC is %d, RSSI is %.1f\n", afc, rssi/2.0);
sonde.sondeList[rxtask.currentSonde].rssi = rssi;
sonde.sondeList[rxtask.currentSonde].afc = afc;
break;
}
yield();
}
if( millis() - t0 >= 1000) {
Serial.println("Preamble scan for AFC: TIMEOUT\n");
return 1; // no preamble, so we may fail fast....
}
#endif
//// Step 2: Real reception
// FSK standby mode, seems like otherweise baudrate cannot be changed?
sx1278.setFSK();
if(sx1278.setBitrate(2400)!=0) {
MP3H_DBG(Serial.println("Setting bitrate 2400bit/s FAILED"));
return 1;
}
MP3H_DBG(Serial.printf("Exact bitrate is %f\n", sx1278.getBitrate()));
// Probably not necessary, as this was set before
if(sx1278.setAFCBandwidth(sonde.config.mp3h.agcbw)!=0) {
MP3H_DBG(Serial.printf("Setting AFC bandwidth %d Hz FAILED", sonde.config.mp3h.agcbw));
return 1;
}
if(sx1278.setRxBandwidth(sonde.config.mp3h.rxbw)!=0) {
MP3H_DBG(Serial.printf("Setting RX bandwidth to %d Hz FAILED", sonde.config.mp3h.rxbw));
return 1;
}
///// Enable auto-AFC, auto-AGC, RX Trigger by preamble
//if(sx1278.setRxConf(0x1E)!=0) {
// Disable auto-AFC, auto-AGC, RX Trigger by preamble
if(sx1278.setRxConf(0x00)!=0) {
MP3H_DBG(Serial.println("Setting RX Config FAILED"));
return 1;
}
// version 1, working with continuous RX
const char *SYNC="\x66\x66";
if(sx1278.setSyncConf(0x70, 1, (const uint8_t *)SYNC)!=0) {
MP3H_DBG(Serial.println("Setting SYNC Config FAILED"));
return 1;
}
// Preamble detection off (+ size 1 byte, maximum tolerance; should not matter for "off"?)
if(sx1278.setPreambleDetect(0x00 | 0x00 | 0x1F)!=0) {
MP3H_DBG(Serial.println("Setting PreambleDetect FAILED"));
return 1;
}
// Packet config 1: fixed len, no mancecer, no crc, no address filter
// Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0)
if(sx1278.setPacketConfig(0x08, 0x40)!=0) {
MP3H_DBG(Serial.println("Setting Packet config FAILED"));
return 1;
}
// enable RX
sx1278.setPayloadLength(0); // infinite for now...
//sx1278.setRxConf(0x20);
uint16_t afc = sx1278.getRawAFC();
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
delay(50);
sx1278.setRawAFC(afc);
delay(50);
Serial.printf("after RX_MODE: AFC is %d\n", sx1278.getAFC());
memset((void *)&mp3hstate, 0, sizeof(mp3hstate));
#if MP3H_DEBUG
MP3H_DBG(Serial.println("Setting SX1278 config for MP3H finished\n"); Serial.println());
#endif
return res;
}
MP3H::MP3H() {
}
#define MP3H_FRAMELEN 49
// offsets from zilog
// https://github.com/rs1729/RS/blob/master/demod/mod/mp3h1mod.c
#define OFS -3
#define pos_CNT1 (OFS+ 3) // 1 nibble (0x80..0x8F ?)
#define pos_TIME (OFS+ 4) // 3*1 byte
#define pos_GPSecefX (OFS+ 8) // 4 byte
#define pos_GPSecefY (OFS+12) // 4 byte
#define pos_GPSecefZ (OFS+16) // 4 byte
#define pos_GPSecefV (OFS+20) // 3*2 byte
#define pos_GPSnSats (OFS+26) // 1 byte (num Sats ?)
#define pos_PTU1 (OFS+35) // 4 byte
#define pos_PTU2 (OFS+39) // 4 byte
#define pos_CNT2 (OFS+43) // 1 byte (0x01..0x10 ?)
#define pos_CFG (OFS+44) // 2/4 byte
#define pos_CRC (OFS+48) // 2 byte
#define crc16poly 0xA001
static bool checkMP3CRC(uint8_t *data)
{
int start = pos_CNT1;
int len = 45;
uint16_t rem = 0xffff;
for(int i=0; i<len; i++) {
rem ^= data[start+i];
for(int j=0; j<8; j++) {
if(rem&0x1) rem = (rem>>1) ^ crc16poly;
else rem = rem>>1;
}
}
uint16_t crcdat = data[pos_CRC] | (data[pos_CRC+1]<<8);
return rem == crcdat ? true : false;
}
void MP3H::printRaw(uint8_t *data, int len)
{
char buf[3];
int i;
for(i=0; i<len; i++) {
snprintf(buf, 3, "%02X ", data[i]);
Serial.print(buf);
}
Serial.println();
}
#ifndef PI
#define PI (3.1415926535897932384626433832795)
#endif
#define RAD (PI/180)
#define DEG (180/PI)
static uint32_t u4(uint8_t *d)
{
return d[0] | (d[1]<<8) | (d[2]<<16) | (d[3]<<24);
}
#define i4(d) ((int32_t)u4(d))
static uint16_t u2(uint8_t *d)
{
return d[0] | (d[1]<<8);
}
#define i2(d) ((int16_t)u2(d))
// defined in RS41.cpp
extern void wgs84r(double x, double y, double z, double * lat, double * long0, double * heig);
extern double atang2(double x, double y);
void calcgps(uint8_t *buf) {
SondeInfo *si = sonde.si();
double wx = i4(buf+pos_GPSecefX) * 0.01;
double wy = i4(buf+pos_GPSecefY) * 0.01;
double wz = i4(buf+pos_GPSecefZ) * 0.01;
double vx = i2(buf+pos_GPSecefV) * 0.01;
double vy = i2(buf+pos_GPSecefV+2) * 0.01;
double vz = i2(buf+pos_GPSecefV+4) * 0.01;
if(wx==0 && wy==0 && wz==0) { if(si->validPos&0x7f) { si->validPos |= 0x80; } return; }
// wgs84r
double lat, lng, alt;
wgs84r(wx, wy, wz, &lat, &lng, &alt);
if(alt<-1000 || alt>80000) { if(si->validPos&0x7f) { si->validPos |= 0x80; } return; }
si->lat = (float)(lat*DEG);
si->lon = (float)(lng*DEG);
si->alt = alt;
// speeddir
double sinlat = sin(lat);
double coslat = cos(lat);
double sinlng = sin(lng);
double coslng = cos(lng);
double vn = -vx*sinlat*coslng - vy*sinlat*sinlng + vz*coslat;
double ve = -vx*sinlng + vy*coslng;
double clb = vx*coslat*coslng + vy*coslat*sinlng + vz*sinlat;
double dir = atang2(vn, ve)/RAD;
if(dir<0.0) dir+=360.0;
si->dir = dir;
si->vs = clb;
si->hs = sqrt(vn*vn + ve*ve);
si->sats = buf[pos_GPSnSats];
Serial.printf("Pos: %f %f alt %f dir %f vs %f hs %f sats %d\n", si->lat, si->lon, si->alt, si->dir, si->vs, si->hs, si->sats);
si->validPos = 0x7f;
}
static uint32_t getgpstime(uint8_t *buf) {
return buf[pos_TIME] * 60*60 + buf[pos_TIME+1] * 60 + buf[pos_TIME+2];
}
// unix time stamp from date and time info in frame.
static void getmp3htime(uint8_t *buf) {
SondeInfo *si = sonde.si();
// gpsdate from CFG frame 15 (0 if not yet received)
uint32_t gpsdate = mp3hstate.gpsdate;
uint32_t gpstime = getgpstime(buf);
int tt = 0;
if(gpsdate) {
uint16_t year = (gpsdate%100)+2000;
gpsdate /= 100;
uint8_t month = gpsdate%100;
gpsdate /= 100;
uint8_t day = gpsdate % 100;
// year-month-day to unix time
tt = (year-1970)*365 + (year-1969)/4; // days since 1970
if(month<=12) { tt += MON[month]; if((year%4)==0 && month>2) tt++; }
tt = (tt+day-1)*(60*60*24);
if(gpstime < mp3hstate.gpsdatetime) tt += 60*60*24; // time wrapped since last date tx
Serial.printf("date: %04d-%02d-%02d t%d ", year, month, day, gpstime);
}
tt += gpstime;
si->time = tt;
si->vframe = tt - 315964800;
Serial.printf(" mp3h TIMESTAMP: %d\n", tt);
}
static uint8_t hex(uint32_t n) {
n = n % 16;
return (n<10) ? (n+'0') : (n-10+'A');
}
static void resetmp3h() {
mp3hstate.id1 = mp3hstate.id2 = 0;
mp3hstate.idok = 0;
mp3hstate.gpsdate = 0;
mp3hstate.dateok = 0;
}
// ret: 1=frame ok; 2=frame with errors; 0=ignored frame (m10dop-alternativ)
int MP3H::decodeframeMP3H(uint8_t *data) {
printRaw(data, MP3H_FRAMELEN);
//
if(!checkMP3CRC(data)) {
// maybe add repairing frames later...
return 2;
}
// data is a frame with correct CRC
SondeInfo *si = sonde.si();
uint8_t cnt = data[pos_CNT1] & 0x0F;
uint32_t cfg = u4(data+pos_CFG);
if(cnt==15) {
// date
mp3hstate.gpsdate = cfg;
mp3hstate.gpsdatetime = getgpstime(data);
mp3hstate.dateok = true;
} else if(cnt==13) {
// id2
if(mp3hstate.id2 > 0 && mp3hstate.id2 != cfg) { resetmp3h(); }
mp3hstate.id2 = cfg;
mp3hstate.idok |= 2;
} else if(cnt==12) {
// id1
if(mp3hstate.id1 > 0 && mp3hstate.id1 != cfg) { resetmp3h(); }
mp3hstate.id1 = cfg;
mp3hstate.idok |= 1;
}
// get id
if((mp3hstate.idok&3) == 3) {
//...
si->type = STYPE_MP3H;
uint32_t n = mp3hstate.id1*100000 + mp3hstate.id2;
si->id[0] = 'M';
si->id[1] = 'R';
si->id[2] = 'Z';
si->id[3] = hex(n/0x100000);
si->id[4] = hex(n/0x10000);
si->id[5] = hex(n/0x1000);
si->id[6] = hex(n/0x100);
si->id[7] = hex(n/0x10);
si->id[8] = hex(n);
si->id[9] = 0;
snprintf(si->ser, 12, "%d-%d", mp3hstate.id1, mp3hstate.id2);
si->validID = true;
}
// position
calcgps(data);
// time
getmp3htime(data);
return 1;
#if 0
int repairstep = 16;
int repl = 0;
bool crcok;
// error correction, inspired by oe5dxl's sondeudp
do {
crcok = checkMP3Hcrc(M10_CRCPOS, data);
if(crcok || repairstep==0) break;
repl = 0;
for(int i=0; i<M10_CRCPOS; i++) {
if( ((sondeudp_VARSET[i/32]&(1<<(i%32))) == 0) && (fixcnt[i]>=repairstep) ) {
repl++;
data[i] = fixbytes[i];
}
}
repairstep >>= 1;
} while(true);
if(crcok) {
for(int i=0; i<M10_CRCPOS; i++) {
if(fixbytes[i]==data[i] &&fixcnt[i]<255) fixcnt[i]++;
else { fixcnt[i]=0; fixbytes[i]=data[i]; }
}
}
Serial.println(crcok?"CRC OK":"CRC NOT OK");
Serial.printf(" repair: %d/%d\n", repl, repairstep);
if(data[1]==0x9F && data[2]==0x20) {
Serial.println("Decoding...");
// Its a M10
// getid...
char ids[11];
ids[0] = 'M';
ids[1] = 'E';
ids[2] = hex(data[95]/16);
ids[3] = hex(data[95]);
ids[4] = hex(data[93]);
uint32_t id = data[96] + data[97]*256;
ids[5] = hex(id/4096);
ids[6] = hex(id/256);
ids[7] = hex(id/16);
ids[8] = hex(id);
ids[9] = 0;
strncpy(sonde.si()->id, ids, 10);
ids[0] = hex(data[95]/16);
ids[1] = dez((data[95]&0x0f)/10);
ids[2] = dez((data[95]&0x0f));
ids[3] = dez(data[93]);
ids[4] = dez(id>>13);
id &= 0x1fff;
ids[5] = dez(id/1000);
ids[6] = dez((id/100)%10);
ids[7] = dez((id/10)%10);
ids[8] = dez(id%10);
strncpy(sonde.si()->ser, ids, 10);
sonde.si()->validID = true;
Serial.printf("ID is %s [%02x %02x %d]\n", ids, data[95], data[93], id);
// ID printed on sonde is ...-.-abbbb, with a=id>>13, bbbb=id&0x1fff in decimal
// position data
sonde.si()->lat = getint32(data+14) * DEGMUL;
sonde.si()->lon = getint32(data+18) * DEGMUL;
sonde.si()->alt = getint32(data+22) * 0.001;
float ve = getint16(data+4)*VMUL;
float vn = getint16(data+6)*VMUL;
sonde.si()->vs = getint16(data+8) * VMUL;
sonde.si()->hs = sqrt(ve*ve+vn*vn);
float dir = atan2(vn, ve)*(1.0/RAD);
if(dir<0) dir+=360;
sonde.si()->dir = dir;
sonde.si()->validPos = 0x3f;
uint32_t gpstime = getint32(data+10);
uint16_t gpsweek = getint16(data+32);
// UTC is GPSTIME - 18s (24*60*60-18 = 86382)
// one week = 7*24*60*60 = 604800 seconds
// unix epoch starts jan 1st 1970 0:00
// gps time starts jan 6, 1980 0:00. thats 315964800 epoch seconds.
// subtracting 86400 yields 315878400UL
sonde.si()->time = (gpstime/1000) + 86382 + gpsweek*604800 + 315878400UL;
sonde.si()->validTime = true;
} else {
Serial.printf("data is %02x %02x %02x\n", data[0], data[1], data[2]);
return 0;
}
return crcok?1:2;
#endif
return 0;
}
static uint32_t rxdata;
static bool rxsearching=true;
// search for
// 0xBF3H (or inverse)
void MP3H::processMP3Hdata(uint8_t dt)
{
for(int i=0; i<8; i++) {
uint8_t d = (dt&0x80)?1:0;
dt <<= 1;
rxdata = (rxdata<<1) | d;
if( (rxbitc&1)==0 ) {
// "bit1"
rxbyte = (rxbyte<<1) | d;
} else {
// "bit2" ==> 01 or 10 => 1, otherweise => 0
// rxbyte = rxbyte ^ d;
}
// BF3H => 1011 1111 0011 0101 => 10011010 10101010 01011010 01100110 => 9AAA5A66 // 6555a599
if(rxsearching) {
if( rxdata == 0x9AAA5A66 || rxdata == 0x6555a599 ) {
rxsearching = false;
rxbitc = 0;
rxp = 0;
headerDetected = 1;
Serial.print("SYNC\n");
int rssi=sx1278.getRSSI();
int fei=sx1278.getFEI();
int afc=sx1278.getAFC();
Serial.print("SYNC!!! Test: RSSI="); Serial.print(rssi);
Serial.print(" FEI="); Serial.print(fei);
Serial.print(" AFC="); Serial.println(afc);
sonde.si()->rssi = rssi;
sonde.si()->afc = afc;
}
} else {
rxbitc = (rxbitc+1)%16; // 16;
if(rxbitc == 0) { // got 8 data bit
dataptr[rxp++] = rxbyte&0xff; // (rxbyte>>1)&0xff;
//if(rxp==2 && dataptr[0]==0x45 && dataptr[1]==0x20) { isM20 = true; }
if(rxp>=MP3H_FRAMELEN) {
rxsearching = true;
haveNewFrame = decodeframeMP3H(dataptr);
}
}
}
}
}
#define MAXFRAMES 6
int MP3H::receive() {
// we wait for at most 6 frames or until a new seq nr.
uint8_t nFrames = MAXFRAMES; // MP3H sends every frame 6x
static uint32_t lastFrame = 0;
uint8_t retval = RX_TIMEOUT;
unsigned long t0 = millis();
Serial.printf("MP3H::receive() start at %ld\n",t0);
while( millis() - t0 < 1100 + (retval!=RX_TIMEOUT)?1000:0 ) {
uint8_t value = sx1278.readRegister(REG_IRQ_FLAGS2);
if ( bitRead(value, 7) ) {
Serial.println("FIFO full");
}
if ( bitRead(value, 4) ) {
Serial.println("FIFO overflow");
}
if ( bitRead(value, 2) == 1 ) {
Serial.println("FIFO: ready()");
sx1278.clearIRQFlags();
}
if(bitRead(value, 6) == 0) { // while FIFO not empty
byte data = sx1278.readRegister(REG_FIFO);
Serial.printf("%02x:",data);
processMP3Hdata(data);
value = sx1278.readRegister(REG_IRQ_FLAGS2);
} else {
if(headerDetected) {
t0 = millis(); // restart timer... don't time out if header detected...
headerDetected = 0;
}
if(haveNewFrame) {
Serial.printf("MP3H::receive(): new frame complete after %ldms\n", millis()-t0);
printRaw(dataptr, MP3H_FRAMELEN);
nFrames--;
// frame with CRC error: just skip and retry (unless we have waited for 6 frames alred)
if(haveNewFrame != 1) {
Serial.printf("hNF: %d (ERROR)\n", haveNewFrame);
retval = RX_ERROR;
} else if (sonde.si()->time == lastFrame) { // same frame number as seen before => skip
Serial.printf("Skipping frame with frame# %d\n", lastFrame);
// nothing, wait for next, "new" frame
} else { // good and new frame, return it.
Serial.println("Good frame");
haveNewFrame = 0;
lastFrame = sonde.si()->time;
return RX_OK;
}
haveNewFrame = 0;
#if 0
if(nFrames <= 0) {
// up to 6 old or erronous frames received => break out
Serial.printf("nFrames is %di, giving up\n", nFrames);
break;
}
#endif
}
delay(2);
}
}
int32_t afc = sx1278.getAFC();
int16_t rssi = sx1278.getRSSI();
Serial.printf("receive: AFC is %d, RSSI is %.1f\n", afc, rssi/2.0);
Serial.printf("MP3H::receive() timed out\n");
return retval;
}
int MP3H::waitRXcomplete() {
return 0;
}
MP3H mp3h = MP3H();

Wyświetl plik

@ -0,0 +1,35 @@
/*
* MP3H.h
* Functions for decoding MP3H radiosonde
* Copyright (C) 2021 Hansi Reiser, dl9rdz
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef MP3H_h
#define MP3H_h
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#ifndef inttypes_h
#include <inttypes.h>
#endif
/* Main class */
class MP3H
{
private:
void printRaw(uint8_t *data, int len);
void processMP3Hdata(uint8_t data);
int decodeframeMP3H(uint8_t *data);
public:
MP3H();
int setup(float frequency);
int receive();
int waitRXcomplete();
};
extern MP3H mp3h;
#endif

Wyświetl plik

@ -5,7 +5,7 @@
#include "rsc.h"
#include "Sonde.h"
#define RS41_DEBUG 0
#define RS41_DEBUG 1
#if RS41_DEBUG
#define RS41_DBG(x) x
@ -169,19 +169,18 @@ static uint32_t X2C_LSH(uint32_t a, int32_t length, int32_t n)
return (a >> -n) & m;
}
static double atang2(double x, double y)
double atang2(double x, double y)
{
double w;
if (fabs(x)>fabs(y)) {
w = (double)atan((float)(X2C_DIVL(y,x)));
w = (double)atan(X2C_DIVL(y,x));
if (x<0.0) {
if (y>0.0) w = 3.1415926535898+w;
else w = w-3.1415926535898;
}
}
else if (y!=0.0) {
w = (double)(1.5707963267949f-atan((float)(X2C_DIVL(x,
y))));
w = (double)(1.5707963267949f-atan(X2C_DIVL(x, y)));
if (y<0.0) w = w-3.1415926535898;
}
else w = 0.0;
@ -408,7 +407,8 @@ static int32_t getint16(const byte frame[], uint32_t frame_len,
return (int32_t)n;
} /* end getint16() */
static void wgs84r(double x, double y, double z,
// also used by MP3H.cpp
void wgs84r(double x, double y, double z,
double * lat, double * long0,
double * heig)
{
@ -421,20 +421,18 @@ static void wgs84r(double x, double y, double z,
double h;
h = x*x+y*y;
if (h>0.0) {
rh = (double)sqrt((float)h);
rh = sqrt(h);
xh = x+rh;
*long0 = atang2(xh, y)*2.0;
if (*long0>3.1415926535898) *long0 = *long0-6.2831853071796;
t = (double)atan((float)(X2C_DIVL(z*1.003364089821,
rh)));
st = (double)sin((float)t);
ct = (double)cos((float)t);
*lat = (double)atan((float)
(X2C_DIVL(z+4.2841311513312E+4*st*st*st,
t = atan(X2C_DIVL(z*1.003364089821, rh));
st = sin(t);
ct = cos(t);
*lat = atan((X2C_DIVL(z+4.2841311513312E+4*st*st*st,
rh-4.269767270718E+4*ct*ct*ct)));
sl = (double)sin((float)*lat);
*heig = X2C_DIVL(rh,(double)cos((float)*lat))-(double)(X2C_DIVR(6.378137E+6f,
sqrt((float)(1.0-6.6943799901413E-3*sl*sl))));
sl = sin(*lat);
*heig = X2C_DIVL(rh,cos(*lat))-(X2C_DIVR(6.378137E+6f,
sqrt((1.0-6.6943799901413E-3*sl*sl))));
}
else {
*lat = 0.0;
@ -464,6 +462,11 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p)
x = (double)getint32(b, b_len, p)*0.01;
y = (double)getint32(b, b_len, p+4UL)*0.01;
z = (double)getint32(b, b_len, p+8UL)*0.01;
if(x==0 && y==0 && z==0) {
// RS41 sometimes sends frame with all 0
if(sonde.si()->validPos) sonde.si()->validPos |= 0x80; // flag as old
return;
}
wgs84r(x, y, z, &lat, &long0, &heig);
Serial.print(" ");
sonde.si()->lat = (float)(X2C_DIVL(lat,1.7453292519943E-2));
@ -480,21 +483,14 @@ static void posrs41(const byte b[], uint32_t b_len, uint32_t p)
vx = (double)getint16(b, b_len, p+12UL)*0.01;
vy = (double)getint16(b, b_len, p+14UL)*0.01;
vz = (double)getint16(b, b_len, p+16UL)*0.01;
vn = (-(vx*(double)sin((float)lat)*(double)
cos((float)long0))-vy*(double)
sin((float)lat)*(double)sin((float)
long0))+vz*(double)cos((float)lat);
ve = -(vx*(double)sin((float)long0))+vy*(double)
cos((float)long0);
vu = vx*(double)cos((float)lat)*(double)
cos((float)long0)+vy*(double)
cos((float)lat)*(double)sin((float)
long0)+vz*(double)sin((float)lat);
vn = (-(vx*sin(lat)*cos(long0))-vy*sin(lat)*sin(long0))+vz*cos(lat);
ve = -(vx*sin(long0))+vy*cos(long0);
vu = vx*cos(lat)*cos(long0)+vy*cos(lat)*sin(long0)+vz*sin(lat);
dir = X2C_DIVL(atang2(vn, ve),1.7453292519943E-2);
if (dir<0.0) dir = 360.0+dir;
sonde.si()->dir = dir;
Serial.print(" ");
sonde.si()->hs = sqrt((float)(vn*vn+ve*ve));
sonde.si()->hs = sqrt(vn*vn+ve*ve);
Serial.print(sonde.si()->hs*3.6);
Serial.print("km/h ");
Serial.print(dir);
@ -690,7 +686,7 @@ int RS41::decode41(byte *data, int maxlen)
Serial.print("#");
uint16_t fnr = data[p]+(data[p+1]<<8);
Serial.print(fnr);
sonde.si()->frame = fnr;
sonde.si()->vframe = sonde.si()->frame = fnr;
Serial.print("; RS41 ID ");
snprintf(buf, 10, "%.8s ", data+p+2);
Serial.print(buf);

Wyświetl plik

@ -626,7 +626,7 @@ int RS92::waitRXcomplete() {
memcpy(si->id, gpx.id, 9);
memcpy(si->ser, gpx.id, 9);
si->validID = true;
si->frame = gpx.frnr;
si->vframe = si->frame = gpx.frnr;
si->sats = gpx.k;
si->time = (gpx.gpssec/1000) + 86382 + gpx.week*604800 + 315878400UL;
si->validTime = true;

Wyświetl plik

@ -14,13 +14,38 @@
#include <Sonde.h>
#include <Display.h>
SX1278FSK::SX1278FSK()
#define SPI_MUTEX_LOCK() \
do \
{ \
} while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS)
#define SPI_MUTEX_UNLOCK() xSemaphoreGive(_lock)
SX1278FSK::SX1278FSK() {}
void SX1278FSK::setup(xSemaphoreHandle lock)
{
// Initialize class variables
_lock = lock;
Serial.println("Setup sx1278");
if(_lock) SPI_MUTEX_LOCK();
digitalWrite(sonde.config.sx1278_ss, HIGH);
pinMode(sonde.config.sx1278_ss, OUTPUT);
Serial.printf("Configuing SX1278FSK SPI with miso=%d, mosi=%d, sck=%d, ss=%d\n", sonde.config.sx1278_miso,
sonde.config.sx1278_mosi, sonde.config.sx1278_sck, sonde.config.sx1278_ss);
SPI.begin(sonde.config.sx1278_sck, sonde.config.sx1278_miso, sonde.config.sx1278_mosi, -1); // no hardware CS
// was: SPI.begin();
//Set most significant bit first
SPI.setBitOrder(MSBFIRST);
//Divide the clock frequency
SPI.setClockDivider(SPI_CLOCK_DIV2);
//Set data mode
SPI.setDataMode(SPI_MODE0);
if(_lock) SPI_MUTEX_UNLOCK();
};
static SPISettings spiset = SPISettings(40000000L, MSBFIRST, SPI_MODE0);
static SPISettings spiset = SPISettings(10000000L, MSBFIRST, SPI_MODE0);
/*
Function: Turns the module ON.
@ -34,19 +59,6 @@ uint8_t SX1278FSK::ON()
Serial.println(F("Starting 'ON'"));
#endif
// Powering the module
pinMode(SX1278_SS, OUTPUT);
digitalWrite(SX1278_SS, HIGH);
//Configure the MISO, MOSI, CS, SPCR.
SPI.begin();
//Set most significant bit first
SPI.setBitOrder(MSBFIRST);
//Divide the clock frequency
SPI.setClockDivider(SPI_CLOCK_DIV2);
//Set data mode
SPI.setDataMode(SPI_MODE0);
// Set Maximum Over Current Protection
state = setMaxCurrent(0x1B);
if( state == 0 )
@ -60,7 +72,6 @@ uint8_t SX1278FSK::ON()
{
return 1;
}
// set FSK mode
state = setFSK();
return state;
@ -77,10 +88,12 @@ void SX1278FSK::OFF()
Serial.println(F("Starting 'OFF'"));
#endif
SPI.end();
//SPI.end();
#if 0
// Powering the module
pinMode(SX1278_SS,OUTPUT);
digitalWrite(SX1278_SS,LOW);
#endif
#if (SX1278FSK_debug_mode > 1)
Serial.println(F("## Setting OFF ##"));
@ -98,15 +111,16 @@ byte SX1278FSK::readRegister(byte address)
{
byte value = 0x00;
if(_lock) SPI_MUTEX_LOCK();
digitalWrite(sonde.config.sx1278_ss,LOW);
SPI.beginTransaction(spiset);
digitalWrite(SX1278_SS,LOW);
//delay(1);
bitClear(address, 7); // Bit 7 cleared to write in registers
SPI.transfer(address);
value = SPI.transfer(0x00);
digitalWrite(SX1278_SS,HIGH);
SPI.endTransaction();
digitalWrite(sonde.config.sx1278_ss,HIGH);
#if (SX1278FSK_debug_mode > 1)
if(address!=0x3F) {
@ -118,7 +132,7 @@ byte SX1278FSK::readRegister(byte address)
Serial.println();
}
#endif
if(_lock) SPI_MUTEX_UNLOCK();
return value;
}
@ -131,15 +145,16 @@ Parameters:
*/
void SX1278FSK::writeRegister(byte address, byte data)
{
if(_lock) SPI_MUTEX_LOCK();
digitalWrite(sonde.config.sx1278_ss,LOW);
SPI.beginTransaction(spiset);
digitalWrite(SX1278_SS,LOW);
//delay(1);
bitSet(address, 7); // Bit 7 set to read from registers
SPI.transfer(address);
SPI.transfer(data);
digitalWrite(SX1278_SS,HIGH);
SPI.endTransaction();
digitalWrite(sonde.config.sx1278_ss,HIGH);
#if (SX1278FSK_debug_mode > 1)
Serial.print(F("## Writing: ##\t"));
@ -150,7 +165,7 @@ void SX1278FSK::writeRegister(byte address, byte data)
Serial.print(data, HEX);
Serial.println();
#endif
if(_lock) SPI_MUTEX_UNLOCK();
}
/*
@ -867,4 +882,5 @@ void SX1278FSK::showRxRegisters()
}
#endif
xSemaphoreHandle globalLock =xSemaphoreCreateMutex();
SX1278FSK sx1278 = SX1278FSK();

Wyświetl plik

@ -35,8 +35,6 @@
#define SX1278FSK_debug_mode 0
#define SX1278_SS SS
//! MACROS //
#define bitRead(value, bit) (((value) >> (bit)) & 0x01) // read a bit
#define bitSet(value, bit) ((value) |= (1UL << (bit))) // set bit to '1'
@ -171,7 +169,9 @@ class SX1278FSK
{
public:
// class constructor
SX1278FSK();
SX1278FSK();
void setup(xSemaphoreHandle lock);
// Turn on SX1278 module (return 0 on sucess, 1 otherwise)
uint8_t ON();
@ -256,7 +256,7 @@ public:
// Receive a packet
uint8_t receivePacketTimeout(uint32_t wait, byte *data);
xSemaphoreHandle _lock = NULL;
#if 0
//! It gets the internal temperature of the module.

Wyświetl plik

@ -24,6 +24,7 @@ struct scancfg {
//struct scancfg scanLCD={ 121, 7, 120/6, 120/6/4, 6000.0/120.0/20.0, 20, 120*20, 1 };
struct scancfg scanLCD={ 121, 7, 120/6, 120/6/4, 6000.0/120.0/10.0, 10, 120*10, 2, 40 };
struct scancfg scanTFT={ 210, 16, 210/6, 210/6/5, 6000.0/210.0/10.0, 10, 210*10, 1, 0 };
struct scancfg scan9341={ 210, 16, 210/6, 210/6/5, 6000.0/210.0/10.0, 10, 210*10, 1, 0 };
struct scancfg &scanconfig = scanTFT;
@ -65,7 +66,7 @@ void Scanner::fillTiles(uint8_t *row, int value) {
///// unused???? uint8_t tiles[16] = { 0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0, 1, 3, 7, 15, 31, 63, 127, 255};
// type 0: lcd, 1: tft, 2: lcd(sh1106)
#define ISTFT (sonde.config.disptype==1)
#define ISTFT (sonde.config.disptype==1 || sonde.config.disptype==3)
void Scanner::plotResult()
{
int yofs = 0;

Wyświetl plik

@ -9,7 +9,6 @@
#ifndef inttypes_h
#include <inttypes.h>
#endif
class Scanner
{
private:

Wyświetl plik

@ -6,6 +6,7 @@
#include "RS92.h"
#include "DFM.h"
#include "M10M20.h"
#include "MP3H.h"
#include "SX1278FSK.h"
#include "Display.h"
#include <Wire.h>
@ -19,6 +20,12 @@ const char *evstring[]={"NONE", "KEY1S", "KEY1D", "KEY1M", "KEY1L", "KEY2S", "KE
const char *RXstr[]={"RX_OK", "RX_TIMEOUT", "RX_ERROR", "RX_UNKNOWN"};
// Dependency to enum SondeType
const char *sondeTypeStr[NSondeTypes] = { "DFM ", "RS41", "RS92", "M10 ", "M20 ", "MP3H" };
const char *sondeTypeLongStr[NSondeTypes] = { "DFM (all)", "RS41", "RS92", "M10 ", "M20 ", "MP3-H1" };
const char sondeTypeChar[NSondeTypes] = { 'D', '4', 'R', 'M', '2', '3' };
const char *manufacturer_string[]={"Graw", "Vaisala", "Vaisala", "Meteomodem", "Meteomodem", "Meteo-Radiy"};
int fingerprintValue[]={ 17, 31, 64, 4, 55, 48, 23, 128+23, 119, 128+119, -1 };
const char *fingerprintText[]={
"TTGO T-Beam (new version 1.0), I2C not working after powerup, assuming 0.9\" OLED@21,22",
@ -78,16 +85,21 @@ void Sonde::defaultConfig() {
config.power_pout = -1;
config.spectrum=10;
// Try autodetecting board type
config.type = TYPE_TTGO;
// Seems like on startup, GPIO4 is 1 on v1 boards, 0 on v2.1 boards?
config.gps_rxd = -1;
config.gps_txd = -1;
config.sx1278_ss = SS; // default SS pin, on all TTGOs
config.sx1278_miso = MISO;
config.sx1278_mosi = MOSI;
config.sx1278_sck = SCK;
config.oled_rst = 16;
config.disptype = 0;
config.tft_orient = 1;
config.button2_axp = 0;
config.norx_timeout = 20;
config.screenfile = 1;
config.tft_modeflip = 0;
config.tft_spifreq = SPI_DEFAULT_FREQ;
if(initlevels[16]==0) {
config.oled_sda = 4;
config.oled_scl = 15;
@ -99,32 +111,60 @@ void Sonde::defaultConfig() {
} else {
config.oled_sda = 21;
config.oled_scl = 22;
if(initlevels[17]==0) { // T-Beam
if(initlevels[17]==0) { // T-Beam or M5Stack Core2?
int tbeam=7;
if(initlevels[12]==0) {
tbeam = 10;
Serial.println("Autoconfig: looks like T-Beam 1.0 board");
Serial.println("Autoconfig: looks like T-Beam 1.0 or M5Stack Core2 board");
} else if ( initlevels[4]==1 && initlevels[12]==1 ) {
tbeam = 11;
Serial.println("Autoconfig: looks like T-Beam 1.1 board");
}
if(tbeam == 10 || tbeam == 11) { // T-Beam v1.0 or T-Beam v1.1
config.button_pin = 38;
config.button2_pin = 15 + 128; //T4 + 128; // T4 = GPIO13
// Maybe in future use as default only PWR as button2?
//config.button2_pin = 255;
config.button2_axp = 1;
config.gps_rxd = 34;
config.gps_txd = 12;
// Check for I2C-Display@21,22
#define SSD1306_ADDRESS 0x3c
Wire.begin(21, 22);
Wire.beginTransmission(SSD1306_ADDRESS);
byte err = Wire.endTransmission();
delay(100); // otherwise its too fast?!
Wire.beginTransmission(SSD1306_ADDRESS);
err = Wire.endTransmission();
if(err!=0 && fingerprint!=17) { // hmm. 17 after powerup with oled commected and no i2c answer!?!?
#define BM8563_ADDRESS 0x51
Wire.beginTransmission(BM8563_ADDRESS);
byte err = Wire.endTransmission();
if(err) { // try again
delay(400);
Wire.beginTransmission(BM8563_ADDRESS);
err = Wire.endTransmission();
}
if(err==0) {
Serial.println("M5stack Core2 board detected\n");
config.type = TYPE_M5_CORE2;
config.button_pin = 255;
config.button2_pin = 255;
config.button2_axp = 1;
config.disptype = 4; // ILI9342
config.oled_sda = 23;
config.oled_scl = 18;
config.oled_rst = -1;
config.tft_rs = 15;
config.tft_cs = 5;
config.screenfile = 4;
config.gps_rxd = 13;
config.gps_txd = -1; // 14
config.sx1278_ss = 33;
config.sx1278_miso = 38;
config.sx1278_mosi = 23; //MOSI;
config.sx1278_sck = 18; // SCK;
} else { // some t-beam...
config.button_pin = 38;
config.button2_pin = 15 + 128; //T4 + 128; // T4 = GPIO13
// Maybe in future use as default only PWR as button2?
//config.button2_pin = 255;
config.button2_axp = 1;
config.gps_rxd = 34;
config.gps_txd = 12;
// Check for I2C-Display@21,22
#define SSD1306_ADDRESS 0x3c
Wire.beginTransmission(SSD1306_ADDRESS);
err = Wire.endTransmission();
delay(100); // otherwise its too fast?!
Wire.beginTransmission(SSD1306_ADDRESS);
err = Wire.endTransmission();
if(err!=0 && fingerprint!=17) { // hmm. 17 after powerup with oled commected and no i2c answer!?!?
fingerprint |= 128;
Serial.println("no I2C display found, assuming large TFT display\n");
// CS=0, RST=14, RS=2, SDA=4, CLK=13
@ -137,10 +177,11 @@ void Sonde::defaultConfig() {
config.tft_cs = 0;
config.spectrum = -1; // no spectrum for now on large display
config.screenfile = 2;
} else {
} else {
// OLED display, pins 21,22 ok...
config.disptype = 0;
Serial.println("... with small OLED display\n");
}
}
} else {
Serial.println("Autoconfig: looks like T-Beam v0.7 board");
@ -181,7 +222,6 @@ void Sonde::defaultConfig() {
config.startfreq=400;
config.channelbw=10;
config.marker=0;
config.showafc=0;
config.freqofs=0;
config.rs41.agcbw=12500;
config.rs41.rxbw=6300;
@ -191,6 +231,8 @@ void Sonde::defaultConfig() {
config.dfm.rxbw=10400;
config.m10m20.agcbw=20800;
config.m10m20.rxbw=12500;
config.mp3h.agcbw=12500;
config.mp3h.rxbw=12500;
config.udpfeed.active = 1;
config.udpfeed.type = 0;
strcpy(config.udpfeed.host, "192.168.42.20");
@ -259,12 +301,20 @@ void Sonde::setConfig(const char *cfg) {
config.tft_cs = atoi(val);
} else if(strcmp(cfg,"tft_orient")==0) {
config.tft_orient = atoi(val);
} else if(strcmp(cfg,"tft_modeflip")==0) {
config.tft_modeflip = atoi(val);
} else if(strcmp(cfg,"tft_spifreq")==0) {
config.tft_spifreq = atoi(val);
} else if(strcmp(cfg,"gps_rxd")==0) {
config.gps_rxd = atoi(val);
} else if(strcmp(cfg,"gps_txd")==0) {
config.gps_txd = atoi(val);
} else if(strcmp(cfg,"sx1278_ss")==0) {
config.sx1278_ss = atoi(val);
} else if(strcmp(cfg,"sx1278_miso")==0) {
config.sx1278_miso = atoi(val);
} else if(strcmp(cfg,"sx1278_mosi")==0) {
config.sx1278_mosi = atoi(val);
} else if(strcmp(cfg,"sx1278_sck")==0) {
config.sx1278_sck = atoi(val);
} else if(strcmp(cfg,"maxsonde")==0) {
config.maxsonde = atoi(val);
if(config.maxsonde>MAXSONDE) config.maxsonde=MAXSONDE;
@ -299,8 +349,6 @@ void Sonde::setConfig(const char *cfg) {
config.spectrum = atoi(val);
} else if(strcmp(cfg,"marker")==0) {
config.marker = atoi(val);
} else if(strcmp(cfg,"showafc")==0) {
config.showafc = atoi(val);
} else if(strcmp(cfg,"freqofs")==0) {
config.freqofs = atoi(val);
} else if(strcmp(cfg,"rs41.agcbw")==0) {
@ -311,6 +359,10 @@ void Sonde::setConfig(const char *cfg) {
config.m10m20.agcbw = atoi(val);
} else if(strcmp(cfg,"m10m20.rxbw")==0) {
config.m10m20.rxbw = atoi(val);
} else if(strcmp(cfg,"mp3h.agcbw")==0) {
config.mp3h.agcbw = atoi(val);
} else if(strcmp(cfg,"mp3h.rxbw")==0) {
config.mp3h.rxbw = atoi(val);
} else if(strcmp(cfg,"dfm.agcbw")==0) {
config.dfm.agcbw = atoi(val);
} else if(strcmp(cfg,"dfm.rxbw")==0) {
@ -364,7 +416,26 @@ void Sonde::setConfig(const char *cfg) {
strncpy(config.mqtt.password, val, 63);
} else if(strcmp(cfg,"mqtt.prefix")==0) {
strncpy(config.mqtt.prefix, val, 63);
} else if(strcmp(cfg, "sondehub.active")==0) {
config.sondehub.active = atoi(val);
} else if(strcmp(cfg,"sondehub.chase")==0) {
config.sondehub.chase = atoi(val);
} else if(strcmp(cfg, "sondehub.host")==0) {
strncpy(config.sondehub.host, val, 63);
} else if(strcmp(cfg, "sondehub.callsign")==0) {
strncpy(config.sondehub.callsign, val, 63);
} else if(strcmp(cfg, "sondehub.lat")==0) {
config.sondehub.lat = *val==0 ? NAN : atof(val);
Serial.printf("lat is %f\n", config.sondehub.lat);
} else if(strcmp(cfg, "sondehub.lon")==0) {
config.sondehub.lon = *val==0 ? NAN : atof(val);
Serial.printf("lon is %f\n", config.sondehub.lon);
} else if(strcmp(cfg, "sondehub.alt")==0) {
strncpy(config.sondehub.alt, val, 19);
} else if(strcmp(cfg, "sondehub.antenna")==0) {
strncpy(config.sondehub.antenna, val, 63);
} else if(strcmp(cfg, "sondehub.email")==0) {
strncpy(config.sondehub.email, val, 63);
} else {
Serial.printf("Invalid config option '%s'=%s \n", cfg, val);
}
@ -384,6 +455,10 @@ void Sonde::addSonde(float frequency, SondeType type, int active, char *launchsi
return;
}
Serial.printf("Adding %f - %d - %d - %s\n", frequency, type, active, launchsite);
// reset all data if type or frequency has changed
if(type != sondeList[nSonde].type || frequency != sondeList[nSonde].freq) {
memset(&sondeList[nSonde], 0, sizeof(SondeInfo));
}
sondeList[nSonde].type = type;
sondeList[nSonde].typestr[0] = 0;
sondeList[nSonde].freq = frequency;
@ -454,8 +529,6 @@ void Sonde::setup() {
case STYPE_RS41:
rs41.setup(sondeList[rxtask.currentSonde].freq * 1000000);
break;
case STYPE_DFM06_OLD:
case STYPE_DFM09_OLD:
case STYPE_DFM:
dfm.setup( sondeList[rxtask.currentSonde].freq * 1000000, sondeList[rxtask.currentSonde].type );
break;
@ -466,6 +539,9 @@ void Sonde::setup() {
case STYPE_M20:
m10m20.setup( sondeList[rxtask.currentSonde].freq * 1000000);
break;
case STYPE_MP3H:
mp3h.setup( sondeList[rxtask.currentSonde].freq * 1000000);
break;
}
// debug
int freq = (int)sx1278.getFrequency();
@ -493,11 +569,12 @@ void Sonde::receive() {
case STYPE_M20:
res = m10m20.receive();
break;
case STYPE_DFM06_OLD:
case STYPE_DFM09_OLD:
case STYPE_DFM:
res = dfm.receive();
break;
case STYPE_MP3H:
res = mp3h.receive();
break;
}
// state information for RX_TIMER / NORX_TIMER events
@ -590,11 +667,12 @@ rxloop:
case STYPE_M20:
m10m20.waitRXcomplete();
break;
case STYPE_DFM06_OLD:
case STYPE_DFM09_OLD:
case STYPE_DFM:
dfm.waitRXcomplete();
break;
case STYPE_MP3H:
mp3h.waitRXcomplete();
break;
}
memmove(sonde.si()->rxStat+1, sonde.si()->rxStat, 17);
sonde.si()->rxStat[0] = res;

Wyświetl plik

@ -53,19 +53,21 @@ extern const char *RXstr[];
// 01000000 => goto sonde -1
// 01000001 => goto sonde +1
#define NSondeTypes 7
enum SondeType { STYPE_DFM, STYPE_DFM09_OLD, STYPE_RS41, STYPE_RS92, STYPE_M10, STYPE_M20, STYPE_DFM06_OLD };
#define NSondeTypes 6
enum SondeType { STYPE_DFM, STYPE_RS41, STYPE_RS92, STYPE_M10, STYPE_M20, STYPE_MP3H };
extern const char *sondeTypeStr[NSondeTypes];
extern const char *sondeTypeLongStr[NSondeTypes];
extern const char sondeTypeChar[NSondeTypes];
extern const char *manufacturer_string[NSondeTypes];
#define TYPE_IS_DFM(t) ( (t)==STYPE_DFM || (t)==STYPE_DFM09_OLD || (t)==STYPE_DFM06_OLD )
#define TYPE_IS_DFM(t) ( (t)==STYPE_DFM )
#define TYPE_IS_METEO(t) ( (t)==STYPE_M10 || (t)==STYPE_M20 )
typedef struct st_sondeinfo {
// receiver configuration
bool active;
SondeType type;
int8_t subtype; /* 0 for none/unknown, hex type for dfm, 1/2 for M10/M20 */
float freq;
// decoded ID
char typestr[5]; // decoded type (use type if *typestr==0)
@ -84,8 +86,8 @@ typedef struct st_sondeinfo {
uint8_t validPos; // bit pattern for validity of above 7 fields; 0x80: position is old
// decoded GPS time
uint32_t time;
uint16_t sec;
uint32_t frame;
uint32_t vframe; // vframe==frame if frame is unique/continous, otherweise vframe is derived from gps time
bool validTime;
// RSSI from receiver
int rssi; // signal strength
@ -143,6 +145,10 @@ struct st_m10m20config {
int agcbw;
int rxbw;
};
struct st_mp3hconfig {
int agcbw;
int rxbw;
};
enum IDTYPE { ID_DFMDXL, ID_DFMGRAW, ID_DFMAUTO };
@ -176,7 +182,23 @@ struct st_mqtt {
char prefix[64];
};
struct st_sondehub {
int active;
int chase;
char host[64];
char callsign[64];
double lat;
double lon;
char alt[20];
char antenna[64];
char email[64];
};
// to be extended
enum { TYPE_TTGO, TYPE_M5_CORE2 };
typedef struct st_rdzconfig {
int type; // autodetected type, TTGO or M5_CORE2
// hardware configuration
int button_pin; // PIN port number menu button (+128 for touch mode)
int button2_pin; // PIN port number menu button (+128 for touch mode)
@ -191,9 +213,13 @@ typedef struct st_rdzconfig {
int tft_rs; // TFT RS pin
int tft_cs; // TFT CS pin
int tft_orient; // TFT orientation (default: 1)
int tft_modeflip; // Hack for Joerg's strange display
int tft_spifreq; // SPI transfer speed (default 40M is out of spec for some TFT)
int gps_rxd; // GPS module RXD pin. We expect 9600 baud NMEA data.
int gps_txd; // GPS module TXD pin
int sx1278_ss; // SPI slave select for sx1278
int sx1278_miso; // SPI MISO for sx1278
int sx1278_mosi; // SPI MOSI for sx1278
int sx1278_sck; // SPI SCK for sx1278
// software configuration
int debug; // show port and config options after reboot
int wifi; // connect to known WLAN 0=skip
@ -209,12 +235,12 @@ typedef struct st_rdzconfig {
int noisefloor; // for spectrum display
char mdnsname[15]; // mDNS-Name, defaults to rdzsonde
// receiver configuration
int showafc; // show afc value in rx screen
int freqofs; // frequency offset (tuner config = rx frequency + freqofs) in Hz
struct st_rs41config rs41; // configuration options specific for RS41 receiver
struct st_rs92config rs92;
struct st_dfmconfig dfm;
struct st_m10m20config m10m20;
struct st_mp3hconfig mp3h;
char ephftp[40];
// data feed configuration
// for now, one feed for each type is enough, but might get extended to more?
@ -224,6 +250,7 @@ typedef struct st_rdzconfig {
struct st_feedinfo tcpfeed; // target for APRS-IS TCP connections
struct st_kisstnc kisstnc; // target for KISS TNC (via TCP, mainly for APRSdroid)
struct st_mqtt mqtt;
struct st_sondehub sondehub;
} RDZConfig;

Wyświetl plik

@ -215,6 +215,7 @@ extern int aprsstr_mon2kiss(const char *mon, char raw[], int raw_len)
if(len==0) return 0;
int idx=0;
raw[idx++] = '\xC0';
raw[idx++] = 0; // channel 0
for(int i=0; i<len-2; i++) { // -2: discard CRC, not used in KISS
if(tmp[i]=='\xC0') {
raw[idx++] = '\xDB';
@ -280,6 +281,7 @@ char *aprs_senddata(SondeInfo *s, const char *usercall, const char *sym) {
aprsstr_append(b, usercall);
aprsstr_append(b, ">");
const char *destcall="APZRDZ";
// const char *destcall="APRARX,SONDEGATE,TCPIP,qAR,oh3bsg";
aprsstr_append(b, destcall);
// uncompressed
aprsstr_append(b, ":;");
@ -290,7 +292,7 @@ char *aprs_senddata(SondeInfo *s, const char *usercall, const char *sym) {
// time
int i = strlen(b);
int sec = s->time % 86400;
snprintf(b+i, APRS_MAXLEN-1, "%02d%02d%02dz", sec/(60*60), (sec%(60*60))/60, sec%60);
snprintf(b+i, APRS_MAXLEN-1, "%02d%02d%02dh", sec/(60*60), (sec%(60*60))/60, sec%60);
i = strlen(b);
//aprsstr_append_data(time, ds);
int lati = abs((int)s->lat);

Wyświetl plik

@ -34,6 +34,16 @@ TTGO T-Team 1.0 with IL9225 TFT => fingerprint 23
0:1 1:0 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
0:1 1:1 2:0 3:1 4:0 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:1 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup)
M5Stack Core2
0:1 1:0 2:0 3:1 4:1 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:1 14:1 15:1 16:1 17:0 18:1 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
0:1 1:1 2:0 3:1 4:1 5:1 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:1 14:1 15:1 16:1 17:0 18:1 19:0 20:0 21:1 22:1 23:1 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup)
Board fingerprint is 87 (nach power on)
0:1 1:0 2:0 3:1 4:0 5:0 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:0 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0
0:1 1:1 2:0 3:1 4:0 5:0 6:0 7:1 8:0 9:1 10:1 11:1 12:0 13:0 14:1 15:0 16:1 17:0 18:0 19:0 20:0 21:1 22:1 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 (before setup)
Board fingerprint is 22 (nach reset)
Autoconfig: looks like T-Beam 1.0 board
vs 0010111
T-Beam 1.1 seems to be different: 1110111
GPIO4 = 1 (additional pullup) => +64

Wyświetl plik

@ -0,0 +1,221 @@
const uint8_t Terminal11x16Bitmap[] = {
0x0C, 0x18, 0x78, 0xF1, 0xE3, 0xC7, 0x86, 0x0C, 0x18, 0x00, 0x00, 0xC1,
0x80, 0x33, 0x33, 0x33, 0x33, 0x0C, 0xC1, 0x98, 0x33, 0x3F, 0xF1, 0x98,
0x33, 0x0C, 0xC1, 0x98, 0xFF, 0x8C, 0xC1, 0x98, 0x33, 0x00, 0x0C, 0x06,
0x0F, 0xCF, 0xF6, 0xC3, 0x61, 0xFC, 0x7F, 0x0D, 0x86, 0xDF, 0xE7, 0xE0,
0xC0, 0x60, 0x00, 0x2E, 0x0D, 0xC3, 0xB8, 0xE0, 0x38, 0x0E, 0x03, 0x80,
0xE0, 0x38, 0x0E, 0x3B, 0x87, 0x60, 0xE0, 0x0E, 0x06, 0xC3, 0x30, 0xCC,
0x36, 0x07, 0x03, 0xC1, 0xF0, 0x66, 0xD9, 0xE6, 0x31, 0xDE, 0x3C, 0xC0,
0x1C, 0x71, 0xC3, 0x0C, 0x60, 0x07, 0x0C, 0x1C, 0x18, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x18, 0x1C, 0x0C, 0x07, 0x38, 0x0C, 0x0E, 0x06, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x0E, 0x0C, 0x38, 0x6D, 0xB6, 0xCF,
0xC3, 0xC7, 0xF8, 0xF0, 0xFC, 0xDB, 0x6D, 0x80, 0x0C, 0x06, 0x03, 0x0F,
0xF7, 0xF8, 0x60, 0x30, 0x18, 0x1C, 0x71, 0xC3, 0x18, 0x7F, 0xBF, 0xC0,
0x1C, 0x71, 0xC0, 0x00, 0x20, 0x0C, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x03,
0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0x60, 0x00, 0x1F, 0x0F, 0xF9, 0x83,
0x60, 0x7C, 0x1F, 0x86, 0xF1, 0x9E, 0x63, 0xD8, 0x7E, 0x0F, 0x81, 0xB0,
0x67, 0xFC, 0x3E, 0x00, 0x06, 0x03, 0x83, 0xE0, 0xF8, 0x06, 0x01, 0x80,
0x60, 0x18, 0x06, 0x01, 0x80, 0x60, 0x18, 0x3F, 0xCF, 0xF0, 0x3F, 0x8F,
0xFB, 0x83, 0xE0, 0x3C, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80,
0xE0, 0x38, 0x0F, 0xFF, 0xFF, 0xC0, 0x3F, 0x8F, 0xFB, 0x83, 0xE0, 0x30,
0x06, 0x01, 0xC7, 0xF0, 0xFC, 0x00, 0xC0, 0x0F, 0x01, 0xF0, 0x77, 0xFC,
0x7F, 0x00, 0x03, 0x80, 0xF0, 0x3E, 0x0E, 0xC3, 0x98, 0xE3, 0x38, 0x66,
0x0C, 0xFF, 0xFF, 0xFC, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0xFF, 0xFF,
0xFF, 0x00, 0x60, 0x0C, 0x01, 0xFF, 0x1F, 0xF0, 0x07, 0x00, 0x60, 0x0F,
0x01, 0xF0, 0x77, 0xFC, 0x7F, 0x00, 0x07, 0x81, 0xF0, 0x70, 0x1C, 0x07,
0x00, 0xC0, 0x3F, 0xE7, 0xFE, 0xE0, 0xF8, 0x0F, 0x01, 0xF0, 0x77, 0xFC,
0x7F, 0x00, 0xFF, 0xFF, 0xFC, 0x03, 0x00, 0x60, 0x18, 0x03, 0x00, 0xC0,
0x18, 0x06, 0x00, 0xC0, 0x30, 0x06, 0x01, 0x80, 0x30, 0x00, 0x1F, 0x07,
0xF1, 0xC7, 0x30, 0x66, 0x0C, 0xE3, 0x8F, 0xE3, 0xFE, 0xE0, 0xF8, 0x0F,
0x01, 0xF0, 0x77, 0xFC, 0x7F, 0x00, 0x3F, 0x8F, 0xFB, 0x83, 0xE0, 0x3C,
0x07, 0xC1, 0xDF, 0xF9, 0xFF, 0x00, 0xC0, 0x38, 0x0E, 0x03, 0x83, 0xE0,
0x78, 0x00, 0x1C, 0x71, 0xC0, 0x00, 0x01, 0xC7, 0x1C, 0x1C, 0x71, 0xC0,
0x00, 0x01, 0xC7, 0x1C, 0x30, 0xC6, 0x01, 0x81, 0xC1, 0xC1, 0xC1, 0xC1,
0xC1, 0xC0, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0C, 0x7F, 0xDF,
0xF0, 0x00, 0x00, 0x7F, 0xDF, 0xF0, 0x60, 0x38, 0x0E, 0x03, 0x80, 0xE0,
0x38, 0x0E, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x00, 0x3F, 0x1F,
0xEE, 0x1F, 0x03, 0xC1, 0xC0, 0xE0, 0x70, 0x38, 0x0C, 0x03, 0x00, 0xC0,
0x00, 0x0C, 0x03, 0x00, 0x3F, 0x8F, 0xF9, 0x83, 0x67, 0xBD, 0xF7, 0xB6,
0xF6, 0xDE, 0xDB, 0xDB, 0x7B, 0xFB, 0x3E, 0x70, 0x07, 0xF8, 0x3F, 0x00,
0x0C, 0x03, 0x01, 0xE0, 0x78, 0x1E, 0x0C, 0xC3, 0x30, 0xCC, 0x61, 0x9F,
0xE7, 0xFB, 0x03, 0xC0, 0xF0, 0x30, 0xFE, 0x3F, 0xCC, 0x3B, 0x06, 0xC1,
0xB0, 0xEF, 0xF3, 0xFE, 0xC1, 0xF0, 0x3C, 0x0F, 0x07, 0xFF, 0xBF, 0xC0,
0x1F, 0x0F, 0xE7, 0x1D, 0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30,
0x06, 0x0D, 0xC7, 0x3F, 0x87, 0xC0, 0xFE, 0x3F, 0xCC, 0x3B, 0x06, 0xC0,
0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x1B, 0x0E, 0xFF, 0x3F, 0x80,
0xFF, 0xFF, 0xFC, 0x03, 0x00, 0xC0, 0x30, 0x0F, 0xF3, 0xFC, 0xC0, 0x30,
0x0C, 0x03, 0x00, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFC, 0x03, 0x00, 0xC0,
0x30, 0x0F, 0xF3, 0xFC, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x00,
0x1F, 0x8F, 0xF7, 0x0D, 0x80, 0xC0, 0x30, 0x0C, 0x7F, 0x1F, 0xC0, 0xF0,
0x36, 0x0D, 0xC3, 0x3F, 0xC7, 0xF0, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0,
0xF0, 0x3F, 0xFF, 0xFF, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30,
0x3F, 0x3F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x3F, 0x3F, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03,
0x00, 0xF0, 0x3C, 0x0F, 0x86, 0x7F, 0x8F, 0xC0, 0xC0, 0xF0, 0x7C, 0x3B,
0x1C, 0xCE, 0x37, 0x0F, 0x83, 0xE0, 0xDC, 0x33, 0x8C, 0x73, 0x0E, 0xC1,
0xF0, 0x30, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00,
0xC0, 0x30, 0x0C, 0x03, 0x00, 0xFF, 0xFF, 0xF0, 0xC0, 0xF8, 0x7E, 0x1F,
0xCF, 0xF3, 0xF7, 0xBD, 0xEF, 0x33, 0xCC, 0xF0, 0x3C, 0x0F, 0x03, 0xC0,
0xF0, 0x30, 0xC0, 0xF8, 0x3E, 0x0F, 0xC3, 0xD8, 0xF6, 0x3C, 0xCF, 0x33,
0xC6, 0xF1, 0xBC, 0x3F, 0x07, 0xC1, 0xF0, 0x30, 0x1E, 0x0F, 0xC7, 0x39,
0x86, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x36, 0x19, 0xCE, 0x3F,
0x07, 0x80, 0xFF, 0x3F, 0xEC, 0x1F, 0x03, 0xC0, 0xF0, 0x3C, 0x1F, 0xFE,
0xFF, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x00, 0x1E, 0x0F, 0xC7, 0x39,
0x86, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF1, 0xB6, 0x79, 0xCE, 0x3F,
0xC7, 0xB0, 0xFF, 0x3F, 0xEC, 0x1F, 0x03, 0xC0, 0xF0, 0x3C, 0x1F, 0xFE,
0xFF, 0x33, 0x8C, 0x73, 0x0E, 0xC1, 0xF0, 0x30, 0x3F, 0x1F, 0xEE, 0x1F,
0x03, 0xC0, 0x38, 0x07, 0xF0, 0xFE, 0x01, 0xC0, 0x3C, 0x0F, 0x87, 0x7F,
0x8F, 0xC0, 0x7F, 0xBF, 0xC3, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C,
0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0,
0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0D, 0x86, 0x7F, 0x8F, 0xC0,
0xC0, 0xF0, 0x3C, 0x0D, 0x86, 0x61, 0x98, 0x63, 0x30, 0xCC, 0x33, 0x07,
0x81, 0xE0, 0x78, 0x0C, 0x03, 0x00, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0,
0xF0, 0x3C, 0x0F, 0x33, 0xCC, 0xF7, 0xBF, 0x3F, 0x87, 0xE1, 0xF0, 0x30,
0xC0, 0xF0, 0x36, 0x19, 0x86, 0x33, 0x07, 0x80, 0xC0, 0x30, 0x1E, 0x0C,
0xC6, 0x19, 0x86, 0xC0, 0xF0, 0x30, 0xC0, 0xF0, 0x36, 0x19, 0x86, 0x33,
0x0C, 0xC1, 0xE0, 0x78, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00,
0xFF, 0xFF, 0xF0, 0x18, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x30, 0x18, 0x0C,
0x06, 0x01, 0x80, 0xFF, 0xFF, 0xF0, 0x3F, 0x3F, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3F, 0x3F, 0x80, 0x18, 0x03, 0x80,
0x38, 0x03, 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, 0x80,
0x30, 0x3F, 0x3F, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x3F, 0x3F, 0x04, 0x01, 0xC0, 0x7C, 0x1D, 0xC7, 0x1D, 0xC1, 0xF0,
0x18, 0xFF, 0xFF, 0xFC, 0x0E, 0x1C, 0x38, 0x60, 0xC0, 0xC0, 0x3F, 0x9F,
0xF0, 0x0C, 0xFF, 0x7F, 0xF0, 0x3C, 0x0F, 0xFF, 0x7F, 0xC0, 0xC0, 0x30,
0x0C, 0x03, 0x00, 0xC0, 0x37, 0xCF, 0xFB, 0x87, 0xC0, 0xF0, 0x3C, 0x0F,
0x07, 0xFF, 0xBF, 0xC0, 0x3F, 0x1F, 0xEE, 0x0F, 0x00, 0xC0, 0x30, 0x0E,
0x0D, 0xFE, 0x3F, 0x00, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xCF, 0xB7,
0xFF, 0x8F, 0xC0, 0xF0, 0x3C, 0x0F, 0x83, 0x7F, 0xCF, 0xF0, 0x3F, 0x1F,
0xEE, 0x0F, 0xFF, 0xFF, 0xB0, 0x0E, 0x01, 0xFE, 0x3F, 0x00, 0x0F, 0x1F,
0x38, 0x30, 0x30, 0x30, 0xFE, 0xFE, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x3F, 0xDF, 0xFE, 0x0F, 0x03, 0xE1, 0xDF, 0xF3, 0xEC, 0x03, 0x01, 0xDF,
0xE7, 0xF0, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0xF3, 0xFD, 0xC7, 0xC1,
0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0C, 0x0C, 0x0C, 0x00, 0x1C, 0x1C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x3F, 0x03, 0x03, 0x00, 0x07, 0x07, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x33, 0x3F, 0x1E, 0x60, 0x30, 0x18, 0x0C,
0x06, 0x03, 0x19, 0x9C, 0xDC, 0x7C, 0x3E, 0x1B, 0x8C, 0xE6, 0x3B, 0x0C,
0x1C, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x3F, 0x3F, 0xB3, 0x3F, 0xEF, 0xFF, 0x33, 0xCC, 0xF3, 0x3C, 0xCF, 0x33,
0xCC, 0xC0, 0x7F, 0x1F, 0xE6, 0x1D, 0x83, 0x60, 0xD8, 0x36, 0x0D, 0x83,
0x60, 0xC0, 0x3F, 0x1F, 0xEE, 0x1F, 0x03, 0xC0, 0xF0, 0x3E, 0x1D, 0xFE,
0x3F, 0x00, 0xFF, 0x3F, 0xEC, 0x1F, 0x03, 0xC0, 0xF8, 0x7F, 0xFB, 0x7C,
0xC0, 0x30, 0x0C, 0x00, 0x3F, 0xDF, 0xFE, 0x0F, 0x03, 0xC0, 0xF8, 0x77,
0xFC, 0xFB, 0x00, 0xC0, 0x30, 0x0C, 0x6F, 0x9F, 0xF7, 0x0D, 0x80, 0x60,
0x18, 0x06, 0x01, 0x80, 0x60, 0x00, 0x7E, 0xFF, 0xC0, 0xFE, 0x7F, 0x03,
0x03, 0xFF, 0x7E, 0x30, 0x30, 0x30, 0x30, 0xFE, 0xFE, 0x30, 0x30, 0x30,
0x30, 0x30, 0x3F, 0x1F, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3E,
0x1D, 0xFF, 0x3E, 0xC0, 0xC0, 0xF0, 0x36, 0x19, 0x86, 0x33, 0x0C, 0xC1,
0xE0, 0x78, 0x0C, 0x00, 0xCC, 0xF3, 0x3C, 0xCF, 0x33, 0xCC, 0xF7, 0xB7,
0xF9, 0xCE, 0x21, 0x00, 0xC1, 0xF1, 0xDD, 0xC7, 0xC1, 0xC1, 0xF1, 0xDD,
0xC7, 0xC1, 0x80, 0x61, 0xB0, 0xCC, 0xC6, 0x61, 0xE0, 0xF0, 0x30, 0x18,
0x18, 0x0C, 0x0C, 0x00, 0xFF, 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
0xFF, 0xFF, 0x80, 0x07, 0x87, 0xC7, 0x03, 0x01, 0x80, 0xC0, 0xE0, 0xE0,
0x38, 0x0C, 0x06, 0x03, 0x01, 0xC0, 0x7C, 0x1E, 0x0C, 0x30, 0xC3, 0x0C,
0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0x78, 0x3E, 0x03, 0x80, 0xC0,
0x60, 0x30, 0x1C, 0x07, 0x07, 0x03, 0x01, 0x80, 0xC0, 0xE3, 0xE1, 0xE0,
0x38, 0xDB, 0x6C, 0x70, 0x0C, 0x07, 0x83, 0x31, 0x86, 0xC0, 0xF0, 0x3F,
0xFF, 0xFF,
};
const GFXglyph Terminal11x16Glyphs[] = {
{ 0, 6, 0, 7, 0, 4 }, // 0x20 ' '
{ 0, 7, 14, 8, 0, -12 }, // 0x21 '!'
{ 13, 8, 4, 9, 0, -10 }, // 0x22 '"'
{ 17, 11, 12, 12, 0, -11 }, // 0x23 '#'
{ 34, 9, 14, 10, 0, -12 }, // 0x24 '$'
{ 50, 11, 12, 12, 0, -10 }, // 0x25 '%'
{ 67, 10, 13, 11, 0, -11 }, // 0x26 '&'
{ 84, 6, 6, 7, 0, -12 }, // 0x27 '''
{ 89, 8, 14, 9, 0, -12 }, // 0x28 '('
{ 103, 8, 14, 9, 0, -12 }, // 0x29 ')'
{ 117, 9, 9, 10, 0, -9 }, // 0x2a '*'
{ 128, 9, 8, 10, 0, -8 }, // 0x2b '+'
{ 137, 6, 5, 7, 0, -1 }, // 0x2c ','
{ 141, 9, 2, 10, 0, -5 }, // 0x2d '-'
{ 144, 6, 3, 7, 0, -1 }, // 0x2e '.'
{ 147, 11, 12, 12, 0, -11 }, // 0x2f '/'
{ 164, 11, 14, 12, 0, -12 }, // 0x30 '0'
{ 184, 10, 14, 11, 0, -12 }, // 0x31 '1'
{ 202, 11, 14, 12, 0, -12 }, // 0x32 '2'
{ 222, 11, 14, 12, 0, -12 }, // 0x33 '3'
{ 242, 11, 14, 12, 0, -12 }, // 0x34 '4'
{ 262, 11, 14, 12, 0, -12 }, // 0x35 '5'
{ 282, 11, 14, 12, 0, -12 }, // 0x36 '6'
{ 302, 11, 14, 12, 0, -12 }, // 0x37 '7'
{ 322, 11, 14, 12, 0, -12 }, // 0x38 '8'
{ 342, 11, 14, 12, 0, -12 }, // 0x39 '9'
{ 362, 6, 9, 7, 0, -8 }, // 0x3a ':'
{ 369, 6, 12, 7, 0, -8 }, // 0x3b ';'
{ 378, 9, 14, 10, 0, -12 }, // 0x3c '<'
{ 394, 10, 6, 11, 0, -7 }, // 0x3d '='
{ 402, 9, 14, 10, 0, -12 }, // 0x3e '>'
{ 418, 10, 14, 11, 0, -12 }, // 0x3f '?'
{ 436, 11, 14, 12, 0, -12 }, // 0x40 '@'
{ 456, 10, 14, 11, 0, -12 }, // 0x41 'A'
{ 474, 10, 14, 11, 0, -12 }, // 0x42 'B'
{ 492, 10, 14, 11, 0, -12 }, // 0x43 'C'
{ 510, 10, 14, 11, 0, -12 }, // 0x44 'D'
{ 528, 10, 14, 11, 0, -12 }, // 0x45 'E'
{ 546, 10, 14, 11, 0, -12 }, // 0x46 'F'
{ 564, 10, 14, 11, 0, -12 }, // 0x47 'G'
{ 582, 10, 14, 11, 0, -12 }, // 0x48 'H'
{ 600, 8, 14, 9, 0, -12 }, // 0x49 'I'
{ 614, 10, 14, 11, 0, -12 }, // 0x4a 'J'
{ 632, 10, 14, 11, 0, -12 }, // 0x4b 'K'
{ 650, 10, 14, 11, 0, -12 }, // 0x4c 'L'
{ 668, 10, 14, 11, 0, -12 }, // 0x4d 'M'
{ 686, 10, 14, 11, 0, -12 }, // 0x4e 'N'
{ 704, 10, 14, 11, 0, -12 }, // 0x4f 'O'
{ 722, 10, 14, 11, 0, -12 }, // 0x50 'P'
{ 740, 10, 14, 11, 0, -12 }, // 0x51 'Q'
{ 758, 10, 14, 11, 0, -12 }, // 0x52 'R'
{ 776, 10, 14, 11, 0, -12 }, // 0x53 'S'
{ 794, 9, 14, 10, 0, -12 }, // 0x54 'T'
{ 810, 10, 14, 11, 0, -12 }, // 0x55 'U'
{ 828, 10, 14, 11, 0, -12 }, // 0x56 'V'
{ 846, 10, 14, 11, 0, -12 }, // 0x57 'W'
{ 864, 10, 14, 11, 0, -12 }, // 0x58 'X'
{ 882, 10, 14, 11, 0, -12 }, // 0x59 'Y'
{ 900, 10, 14, 11, 0, -12 }, // 0x5a 'Z'
{ 918, 8, 14, 9, 0, -12 }, // 0x5b '['
{ 932, 11, 12, 12, 0, -11 }, // 0x5c '\'
{ 949, 8, 14, 9, 0, -12 }, // 0x5d ']'
{ 963, 11, 7, 12, 0, -12 }, // 0x5e '^'
{ 973, 11, 2, 12, 0, 2 }, // 0x5f '_'
{ 976, 7, 6, 8, 0, -11 }, // 0x60 '`'
{ 982, 10, 9, 11, 0, -7 }, // 0x61 'a'
{ 994, 10, 14, 11, 0, -12 }, // 0x62 'b'
{ 1012, 10, 9, 11, 0, -7 }, // 0x63 'c'
{ 1024, 10, 14, 11, 0, -12 }, // 0x64 'd'
{ 1042, 10, 9, 11, 0, -7 }, // 0x65 'e'
{ 1054, 8, 14, 9, 0, -12 }, // 0x66 'f'
{ 1068, 10, 11, 11, 0, -7 }, // 0x67 'g'
{ 1082, 9, 14, 10, 0, -12 }, // 0x68 'h'
{ 1098, 8, 12, 9, 0, -10 }, // 0x69 'i'
{ 1110, 8, 14, 9, 0, -10 }, // 0x6a 'j'
{ 1124, 9, 14, 10, 0, -12 }, // 0x6b 'k'
{ 1140, 8, 14, 9, 0, -12 }, // 0x6c 'l'
{ 1154, 10, 9, 11, 0, -7 }, // 0x6d 'm'
{ 1166, 10, 9, 11, 0, -7 }, // 0x6e 'n'
{ 1178, 10, 9, 11, 0, -7 }, // 0x6f 'o'
{ 1190, 10, 11, 11, 0, -7 }, // 0x70 'p'
{ 1204, 10, 11, 11, 0, -7 }, // 0x71 'q'
{ 1218, 10, 9, 11, 0, -7 }, // 0x72 'r'
{ 1230, 8, 9, 9, 0, -7 }, // 0x73 's'
{ 1239, 8, 13, 9, 0, -11 }, // 0x74 't'
{ 1252, 10, 9, 11, 0, -7 }, // 0x75 'u'
{ 1264, 10, 9, 11, 0, -7 }, // 0x76 'v'
{ 1276, 10, 9, 11, 0, -7 }, // 0x77 'w'
{ 1288, 9, 9, 10, 0, -7 }, // 0x78 'x'
{ 1299, 9, 11, 10, 0, -7 }, // 0x79 'y'
{ 1312, 9, 9, 10, 0, -7 }, // 0x7a 'z'
{ 1323, 9, 15, 10, 0, -12 }, // 0x7b '{'
{ 1340, 6, 14, 7, 0, -12 }, // 0x7c '|'
{ 1351, 9, 15, 10, 0, -12 }, // 0x7d '}'
{ 1368, 10, 3, 11, 0, -10 }, // 0x7e '~'
{ 1372, 10, 8, 11, 0, -8 }, // 0x7f ''
};
const GFXfont Terminal11x16Font = {
(uint8_t *)Terminal11x16Bitmap,
(GFXglyph *)Terminal11x16Glyphs,
0x20, 0x7F, 18 };

Wyświetl plik

@ -74,7 +74,6 @@ void MQTT::publishPacket(SondeInfo *s)
"\"sats\": %d,"
"\"validPos\": %d,"
"\"time\": %d,"
"\"sec\": %d,"
"\"frame\": %d,"
"\"validTime\": %d,"
"\"rssi\": %d,"
@ -104,7 +103,6 @@ void MQTT::publishPacket(SondeInfo *s)
s->sats,
s->validPos,
s->time,
s->sec,
s->frame,
(int)s->validTime,
s->rssi,

Wyświetl plik

@ -22,14 +22,16 @@ lib_deps_external =
stevemarple/MicroNMEA @ ^2.0.5
; nkawu/TFT 22 ILI9225 @ ^1.4.4
me-no-dev/ESP Async WebServer @ ^1.2.3
https://github.com/moononournation/Arduino_GFX
https://github.com/dx168b/async-mqtt-client
[env:ttgo-lora32]
;platform = espressif32
platform = https://github.com/platformio/platform-espressif32.git
board = ttgo-lora32-v1
framework = arduino
monitor_speed = 115200
lib_deps =
${extra.lib_deps_builtin}
${extra.lib_deps_external}
lib_deps =
${extra.lib_deps_builtin}
${extra.lib_deps_external}
paulstoffregen/Time@^1.6.0
lib_ignore = Time

3969
scripts/esptool.py 100755

Plik diff jest za duży Load Diff

130
scripts/ttgoconfig 100644
Wyświetl plik

@ -0,0 +1,130 @@
#!/usr/bin/python3
import requests
import sys
import os
import socket
import esptool
ttgohost = "rdzsonde.local"
# usually, rdzsonde.mooo.com should be an alias for that:
# or, more specifically:
updatehost = "https://github.com/dl9rdz/rdz_ttgo_sonde/blob/gh-pages/{}/{}?raw=true"
screens = ("screens1.txt", "screens2.txt", "screens3.txt")
allfiles = ("config.txt", "qrg.txt", "networks.txt") + screens
optprint = False
optdir = ""
def getfile(name):
urlg = url+"file/"+name;
print("Downloading: ",urlg);
data = requests.get(urlg);
if optprint:
print(data.text)
elif len(data.content)>0:
f = open(optdir+name, "wb");
f.write(data.content);
f.close();
else:
print("Error: empty response")
def putfile(name):
print("Uploading: ",optdir+name)
files = { 'data': (name, open(optdir+name, "rb")), }
response = requests.post(url+"file", files=files)
while len(sys.argv)>=2:
if sys.argv[1]=="--print":
del(sys.argv[1])
optprint = True
print("Printing file content on screen\n")
elif sys.argv[1].startswith("--dir="):
optdir = sys.argv[1][6:]+"/"
print("Using file directory ",optdir)
os.makedirs(optdir, exist_ok=True)
del(sys.argv[1])
elif sys.argv[1].startswith("--ttgo="):
ttgohost = sys.argv[1][7:]
del(sys.argv[1])
else:
break
if len(sys.argv)<=2:
print("Usage: ",sys.argv[0]," [--ttgo={ip}] [--print|--dir={dir}] <get|put> <all|config|qrg|networks|screens>");
print("or: ",sys.argv[0]," <get|put> file {filename}");
print("or: ",sys.argv[0]," update <devel-xxx|master-yyy>");
print("or: ",sys.argv[0]," <backup|restore> file.bin");
print("\n",
" screens is screens1.txt, screens2.txt, screens3.txt");
print(" networks is networks.txt (Wifi ssid and password)")
print(" qrg is qrg.txt (List with scan frequencies)")
print(" all is screens + network + qrg")
sys.exit(1)
if sys.argv[1]=="backup":
# backup installed firmware (+ all data) to backup.bin
sys._argv = sys.argv[:]
sys.argv=[sys._argv[0],"--chip", "esp32", "--baud", "921600", "--before", "default_reset", "--after", "hard_reset", "read_flash", "0x1000", "0x3FF000", sys.argv[2]]
esptool.main()
exit(0)
if sys.argv[1]=="restore":
# restore system from backup.bin
sys._argv = sys.argv[:]
sys.argv=[sys._argv[0],"--chip", "esp32", "--baud", "921600", "--before", "default_reset", "--after", "hard_reset", "write_flash", "-z", "--flash_mode", "dio", "--flash_freq", "80m", "--flash_size", "detect", "0x1000", sys.argv[2]]
esptool.main()
exit(0)
if sys.argv[1]=="update":
# update to a new version...
what = sys.argv[2]
imgdir = "devel"
if what.startswith("master"):
imgdir = "master"
host = updatehost.format(imgdir, what)
data = requests.get(host)
f = open("firmware.bin", "wb")
f.write(data.content)
f.close()
sys._argv = sys.argv[:]
sys.argv=[sys._argv[0],"--chip", "esp32", "--baud", "921600", "--before", "default_reset", "--after", "hard_reset", "write_flash", "-z", "--flash_mode", "dio", "--flash_freq", "80m", "--flash_size", "detect", "0x1000", "firmware.bin"]
esptool.main()
exit(0)
addrinfo = socket.gethostbyname(ttgohost)
url = "http://"+addrinfo+"/"
print("Using URL ",url)
files=()
if sys.argv[2]=="file":
if len(sys.argv)<=3:
print("get/put file: missing filename\n");
sys.exit(1);
files=(sys.argv[3],)
elif sys.argv[2]=="config":
files=("config.txt",)
elif sys.argv[2]=="qrg":
files=("qrg.txt",)
elif sys.argv[2]=="networks":
files=("networks.txt",)
elif sys.argv[2]=="screens":
files=screens
elif sys.argv[2]=="all":
files=allfiles
else:
print("Invalid file specification: ",sys.argv[2])
sys.exit(1)
if(sys.argv[1]=="get"):
for f in files:
getfile(f)
elif(sys.argv[1]=="put"):
for f in files:
putfile(f)
else:
print("Invalid command ",sys.argv[1])