kopia lustrzana https://github.com/dl9rdz/rdz_ttgo_sonde
Merge branch 'devel' of https://github.com/dl9rdz/rdz_ttgo_sonde into devel
commit
cd01f0db1c
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Version and specific configuration:**
|
||||
- What firmware version are you using? binary devel or master image? compiled code on your own, possibly with modification?
|
||||
- Any specific settings
|
||||
- Hardware you are using? (ESP32 board? Display? Additional hardware connected to ESP32?)
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
11
.travis.yml
11
.travis.yml
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
64
README.md
64
README.md
|
@ -1,11 +1,65 @@
|
|||
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 radiosondes is not practical.
|
||||
|
||||
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
|
@ -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
|
||||
#-------------------------------#
|
||||
|
|
|
@ -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 © 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.
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -0,0 +1,480 @@
|
|||
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> · 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> · Map: <a href="https://www.esri.com/">Esri</a> · 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();"/> <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(' '+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='+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,highest.longitude];
|
||||
|
||||
var descending = prediction.prediction[1].trajectory;
|
||||
var landing = descending[descending.length-1];
|
||||
var landing_location = [landing.latitude,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,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,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);
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
@Scanner
|
||||
timer=-1,0,0
|
||||
key1action=D,#,F,W
|
||||
key2action=#,#,#,#
|
||||
key2action=D,#,#,#
|
||||
timeaction=#,D,+
|
||||
0,0=XScan
|
||||
0,5=S#:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const char *version_name = "rdzTTGOsonde";
|
||||
const char *version_id = "devel20210315";
|
||||
const char *version_id = "devel20210817";
|
||||
const int SPIFFS_MAJOR=2;
|
||||
const int SPIFFS_MINOR=10;
|
||||
const int SPIFFS_MINOR=14;
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
@ -269,6 +269,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 +320,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) {
|
||||
|
@ -390,7 +392,7 @@ 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)
|
||||
{
|
||||
|
@ -404,7 +406,7 @@ void DFM::decodeDAT(uint8_t *dat)
|
|||
{
|
||||
int val = (((uint16_t)dat[4])<<8) + (uint16_t)dat[5];
|
||||
Serial.print("UTC-msec: "); Serial.print(val);
|
||||
sonde.si()->sec = val/1000;
|
||||
sonde.si()->sec = (val+500)/1000;
|
||||
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!?!?!?
|
||||
}
|
||||
|
@ -583,7 +585,7 @@ int DFM::receiveNew() {
|
|||
delay(2);
|
||||
}
|
||||
}
|
||||
return RX_TIMEOUT;
|
||||
return rxframes == 4 ? RX_TIMEOUT : RX_OK;
|
||||
}
|
||||
|
||||
int DFM::receiveOld() {
|
||||
|
|
|
@ -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,20 @@ 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);
|
||||
//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' };
|
||||
const char *sondeTypeStr[NSondeTypes] = { "DFM ", "DFM9", "RS41", "RS92", "M10 ", "M20 ", "DFM6", "MP3H" };
|
||||
const char *sondeTypeLongStr[NSondeTypes] = { "DFM (all)", "DFM9 (old)", "RS41", "RS92", "M10 ", "M20 ", "DFM6 (old)", "MP3-H1" };
|
||||
const char sondeTypeChar[NSondeTypes] = { 'D', '9', '4', 'R', 'M', '2', '6', '3' };
|
||||
|
||||
byte myIP_tiles[8*11];
|
||||
static uint8_t ap_tile[8]={0x00,0x04,0x22,0x92, 0x92, 0x22, 0x04, 0x00};
|
||||
|
@ -187,7 +196,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 +320,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 +333,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 +345,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 +409,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 +431,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 +499,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 +536,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 +570,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 +634,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 +686,7 @@ uint16_t MY_ILI9225::drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t
|
|||
|
||||
return (uint16_t)xa;
|
||||
}
|
||||
#endif
|
||||
///////////////
|
||||
|
||||
|
||||
|
@ -603,8 +698,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 +939,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 +1069,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 +1227,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 +1259,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 +1272,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 +1342,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 +1382,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 +1523,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) {
|
||||
|
@ -1478,7 +1595,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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -276,12 +276,13 @@ 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...");
|
||||
// Its a M10
|
||||
// getid...
|
||||
char ids[11];
|
||||
char ids[12];
|
||||
ids[0] = 'M';
|
||||
ids[1] = 'E';
|
||||
ids[2] = hex(data[95]/16);
|
||||
|
@ -297,14 +298,17 @@ 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);
|
||||
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(sonde.si()->ser, ids, 12);
|
||||
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
|
||||
|
@ -316,7 +320,8 @@ int M10M20::decodeframeM10(uint8_t *data) {
|
|||
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);
|
||||
sonde.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;
|
||||
|
@ -334,7 +339,7 @@ int M10M20::decodeframeM10(uint8_t *data) {
|
|||
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 +393,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);
|
||||
|
@ -515,7 +522,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,9 +532,24 @@ 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(sonde.si()->id, ids, 10);
|
||||
// Serial: AAB-C-DDEEE
|
||||
char *ser = sonde.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;
|
||||
//Serial.printf("ID is %s [%02x %02x %d]\n", ids, data[95], data[93], id);
|
||||
|
@ -547,7 +569,7 @@ int M10M20::decodeframeM20(uint8_t *data) {
|
|||
//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);
|
||||
float dir = atan2(ve, vn)*(1.0/RAD);
|
||||
if(dir<0) dir+=360;
|
||||
sonde.si()->dir = dir;
|
||||
sonde.si()->validPos = 0x3f;
|
||||
|
|
|
@ -0,0 +1,580 @@
|
|||
|
||||
/* 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;
|
||||
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();
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#ifndef inttypes_h
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
class Scanner
|
||||
{
|
||||
private:
|
||||
|
|
|
@ -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,9 @@ 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 *manufacturer_string[]={"Graw", "Graw", "Vaisala", "Vaisala", "Meteomodem", "Meteomodem", "Graw", "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 +82,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 +108,55 @@ 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==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 +169,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 +214,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 +223,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 +293,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 +341,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 +351,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 +408,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);
|
||||
}
|
||||
|
@ -466,6 +529,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();
|
||||
|
@ -498,6 +564,9 @@ void Sonde::receive() {
|
|||
case STYPE_DFM:
|
||||
res = dfm.receive();
|
||||
break;
|
||||
case STYPE_MP3H:
|
||||
res = mp3h.receive();
|
||||
break;
|
||||
}
|
||||
|
||||
// state information for RX_TIMER / NORX_TIMER events
|
||||
|
@ -595,6 +664,9 @@ rxloop:
|
|||
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;
|
||||
|
|
|
@ -53,11 +53,12 @@ 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 8
|
||||
enum SondeType { STYPE_DFM, STYPE_DFM09_OLD, STYPE_RS41, STYPE_RS92, STYPE_M10, STYPE_M20, STYPE_DFM06_OLD, 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_METEO(t) ( (t)==STYPE_M10 || (t)==STYPE_M20 )
|
||||
|
@ -66,6 +67,7 @@ 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)
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 };
|
|
@ -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
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -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])
|
||||
|
Ładowanie…
Reference in New Issue