merging devel changes 0d64f8d..9b0785b

pull/4/head
Hans P. Reiser 2019-04-12 18:53:29 +02:00
rodzic f9a16f827c
commit 69cb203a4f
12 zmienionych plików z 849 dodań i 98 usunięć

Wyświetl plik

@ -43,3 +43,20 @@ A SHORT press will switch to the next channel in channels.txt
A medium press will active scan the whole band (400..406 MHz) and display a
spectrum diagram (each line == 50 kHz)
## Setup
Download https://github.com/me-no-dev/ESPAsyncWebServer/archive/master.zip
and move to your Arduino IDE's libraries directory
Rename to (name without "-master")
Download https://github.com/me-no-dev/AsyncTCP/archive/master.zip
and move to your Arduino IDE's libraries directory
Rename to (name without "-master")
Install Arduino ESP32 file system uploader
https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/
Download https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/download/1.0/ESP32FS-1.0.zip
Move to your Arduino IDE's tools directory

Wyświetl plik

@ -1,4 +1,5 @@
#include <WiFi.h>
#include <WiFiUdp.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
#include <U8x8lib.h>
@ -7,6 +8,7 @@
#include <SX1278FSK.h>
#include <Sonde.h>
#include <Scanner.h>
#include <aprs.h>
//#include <RS41.h>
//#include <DFM.h>
@ -26,6 +28,13 @@ int e;
AsyncWebServer server(80);
const char * udpAddress = "192.168.179.21";
const int udpPort = 9002;
boolean connected = false;
WiFiUDP udp;
// Set LED GPIO
const int ledPin = 2;
// Stores LED state
@ -48,58 +57,29 @@ String processor(const String& var){
return String();
}
void SetupAsyncServer() {
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", "Hello, world");
});
server.on("/index.html", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Route to load style.css file
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/style.css", "text/css");
});
#define MAX_QRG 10
// Route to set GPIO to HIGH
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, HIGH);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Route to set GPIO to LOW
server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, LOW);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Start server
server.begin();
const String sondeTypeSelect(int activeType) {
String sts = "";
for(int i=0; i<3; i++) {
sts += "<option value=\"";
sts += sondeTypeStr[i];
sts += "\"";
if(activeType==i) { sts += " selected"; }
sts += ">";
sts += sondeTypeStr[i];
sts += "</option>";
}
return sts;
}
int nNetworks;
struct { String id; String pw; } networks[20];
void setupWifiList() {
File file = SPIFFS.open("/networks.txt", "r");
if(!file){
Serial.println("There was an error opening the file '/networks.txt' for reading");
return;
}
int i=0;
while(file.available()) {
String line = file.readStringUntil('\n');
if(!file.available()) break;
networks[i].id = line;
networks[i].pw = file.readStringUntil('\n');
i++;
}
nNetworks = i;
Serial.print(i); Serial.println(" networks in networks.txt\n");
for(int j=0; j<i; j++) { Serial.print(networks[j].id); Serial.print(": "); Serial.println(networks[j].pw); }
}
//trying to work around
//"assertion "heap != NULL && "free() target pointer is outside heap areas"" failed:"
// which happens if request->send is called in createQRGForm!?!??
char message[10240];
///////////////////////// Functions for Reading / Writing QRG list from/to qrg.txt
void setupChannelList() {
File file = SPIFFS.open("/qrg.txt", "r");
@ -122,16 +102,241 @@ void setupChannelList() {
else if (space[1]=='9') { type=STYPE_DFM09; }
else if (space[1]=='6') { type=STYPE_DFM06; }
else continue;
Serial.printf("Adding %f with type %d\b",freq,type);
sonde.addSonde(freq, type);
int active = space[3]=='+'?1:0;
Serial.printf("Adding %f with type %d (active: %d)\n",freq,type,active);
sonde.addSonde(freq, type, active);
i++;
}
}
const char *createQRGForm() {
char *ptr = message;
strcpy(ptr,"<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"qrg.html\" method=\"post\"><table><tr><th>ID</th><th>Active</th><th>Freq</th><th>Mode</th></tr>");
for(int i=0; i<10; i++) {
String s = sondeTypeSelect(i>=sonde.nSonde?2:sonde.sondeList[i].type);
sprintf(ptr+strlen(ptr), "<tr><td>%d</td><td><input name=\"A%d\" type=\"checkbox\" %s/></td>"
"<td><input name=\"F%d\" type=\"text\" value=\"%3.3f\"></td>"
"<td><select name=\"T%d\">%s</select></td>",
i+1,
i+1, (i<sonde.nSonde&&sonde.sondeList[i].active)?"checked":"",
i+1, i>=sonde.nSonde?400.000:sonde.sondeList[i].freq,
i+1, s.c_str());
}
strcat(ptr,"</table><input type=\"submit\" value=\"Update\"/></form></body></html>");
return message;
}
const char *handleQRGPost(AsyncWebServerRequest *request) {
char label[10];
// parameters: a_i, f_1, t_i (active/frequency/type)
#if 1
File f = SPIFFS.open("/qrg.txt", "w");
if(!f) {
Serial.println("Error while opening '/qrg.txt' for writing");
return "Error while opening '/qrg.txt' for writing";
}
#endif
Serial.println("Handling post request");
#if 0
int params = request->params();
for(int i=0; i<params; i++) {
Serial.println(request->getParam(i)->name().c_str());
}
#endif
for(int i=1; i<=MAX_QRG; i++) {
snprintf(label, 10, "A%d", i);
AsyncWebParameter *active = request->getParam(label, true);
snprintf(label, 10, "F%d", i);
AsyncWebParameter *freq = request->getParam(label, true);
if(!freq) continue;
snprintf(label, 10, "T%d", i);
AsyncWebParameter *type = request->getParam(label, true);
if(!type) continue;
const char *fstr = freq->value().c_str();
const char *tstr = type->value().c_str();
Serial.printf("Processing a=%s, f=%s, t=%s\n", active?"YES":"NO", fstr, tstr);
char typech = (tstr[2]=='4'?'4':tstr[3]); // Ugly TODO
f.printf("%3.3f %c %c\n", atof(fstr), typech, active?'+':'-');
}
f.close();
setupChannelList();
}
/////////////////// Functions for reading/writing Wifi networks from networks.txt
#define MAX_WIFI 10
int nNetworks;
struct { String id; String pw; } networks[MAX_WIFI];
// FIXME: For now, we don't uspport wifi networks that contain newline or null characters
// ... would require a more sophisicated file format (currently one line SSID; one line Password
void setupWifiList() {
File file = SPIFFS.open("/networks.txt", "r");
if(!file){
Serial.println("There was an error opening the file '/networks.txt' for reading");
return;
}
int i=0;
while(file.available()) {
String line = file.readStringUntil('\n');
if(!file.available()) break;
networks[i].id = line;
networks[i].pw = file.readStringUntil('\n');
i++;
}
nNetworks = i;
Serial.print(i); Serial.println(" networks in networks.txt\n");
for(int j=0; j<i; j++) { Serial.print(networks[j].id); Serial.print(": "); Serial.println(networks[j].pw); }
}
const char *createWIFIForm() {
char *ptr = message;
char tmp[4];
strcpy(ptr,"<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"wifi.html\" method=\"post\"><table><tr><th>Nr</th><th>SSID</th><th>Password</th></tr>");
for(int i=0; i<MAX_WIFI; i++) {
sprintf(tmp,"%d",i);
sprintf(ptr+strlen(ptr), "<tr><td>%s</td><td><input name=\"S%d\" type=\"text\" value=\"%s\"/></td>"
"<td><input name=\"P%d\" type=\"text\" value=\"%s\"/></td>",
i==0?"<b>AP</b>":tmp,
i+1, i<nNetworks?networks[i].id.c_str():"",
i+1, i<nNetworks?networks[i].pw.c_str():"");
}
strcat(ptr,"</table><input type=\"submit\" value=\"Update\"></input></form></body></html>");
return message;
}
const char *handleWIFIPost(AsyncWebServerRequest *request) {
char label[10];
// parameters: a_i, f_1, t_i (active/frequency/type)
#if 1
File f = SPIFFS.open("/networks.txt", "w");
if(!f) {
Serial.println("Error while opening '/networks.txt' for writing");
return "Error while opening '/networks.txt' for writing";
}
#endif
Serial.println("Handling post request");
#if 0
int params = request->params();
for(int i=0; i<params; i++) {
Serial.println(request->getParam(i)->name().c_str());
}
#endif
for(int i=1; i<=MAX_WIFI; i++) {
snprintf(label, 10, "S%d", i);
AsyncWebParameter *ssid = request->getParam(label, true);
if(!ssid) continue;
snprintf(label, 10, "P%d", i);
AsyncWebParameter *pw = request->getParam(label, true);
if(!pw) continue;
const char *sstr = ssid->value().c_str();
const char *pstr = pw->value().c_str();
if(strlen(sstr)==0) continue;
Serial.printf("Processing S=%s, P=%s\n", sstr, pstr);
f.printf("%s\n%s\n", sstr, pstr);
}
f.close();
setupWifiList();
}
// Show current status
void addSondeStatus(char *ptr, int i)
{
SondeInfo *s = &sonde.sondeList[i];
strcat(ptr, "<table>");
sprintf(ptr+strlen(ptr),"<tr><td id=\"sfreq\">%3.3f MHz, Type: %s</td><tr><td>ID: %s</td></tr><tr><td>QTH: %.6f,%.6f h=%.0fm</td></tr>\n",
s->freq, sondeTypeStr[s->type],
s->validID?s->id:"<??>",
s->lat, s->lon, s->hei);
sprintf(ptr+strlen(ptr), "<tr><td><a target=\"_empty\" href=\"geo:%.6f,%.6f\">Geo-Ref</a> -", s->lat, s->lon);
sprintf(ptr+strlen(ptr), "<a target=\"_empty\" href=\"https://www.google.com/maps/search/?api=1&query=%.6f,%.6f\">Google map</a> - ", s->lat, s->lon);
sprintf(ptr+strlen(ptr), "<a target=\"_empty\" href=\"https://www.openstreetmap.org/?mlat=%.6f&mlon=%.6f&zoom=14\">OSM</a></td></tr>", s->lat, s->lon);
strcat(ptr, "</table><p/>\n");
}
const char *createStatusForm() {
char *ptr = message;
strcpy(ptr,"<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"><meta http-equiv=\"refresh\" content=\"5\"></head><body>");
for(int i=0; i<sonde.nSonde; i++) {
addSondeStatus(ptr, (i+sonde.currentSonde)%sonde.nSonde);
}
strcat(ptr,"</body></html>");
return message;
}
const char* PARAM_MESSAGE = "message";
void SetupAsyncServer() {
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", "Hello, world");
});
server.on("/index.html", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", String(), false, processor);
});
server.on("/test.html", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/test.html", String(), false, processor);
});
server.on("/qrg.html", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", createQRGForm());
});
server.on("/qrg.html", HTTP_POST, [](AsyncWebServerRequest *request){
handleQRGPost(request);
request->send(200, "text/html", createQRGForm());
});
server.on("/wifi.html", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", createWIFIForm());
});
server.on("/wifi.html", HTTP_POST, [](AsyncWebServerRequest *request){
handleWIFIPost(request);
request->send(200, "text/html", createWIFIForm());
});
server.on("/status.html", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", createStatusForm());
});
// Route to load style.css file
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/style.css", "text/css");
});
// Route to set GPIO to HIGH
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, HIGH);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Route to set GPIO to HIGH
server.on("/test.php", HTTP_POST, [](AsyncWebServerRequest *request){
//digitalWrite(ledPin, HIGH);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Route to set GPIO to LOW
server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, LOW);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Send a POST request to <IP>/post with a form field message set to <message>
server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
handleQRGPost(request);
request->send(200, "text/plain", "Hello, POST done");
});
// Start server
server.begin();
}
const char *fetchWifiPw(const char *id) {
for(int i=0; i<nNetworks; i++) {
Serial.print("Comparing '");
@ -194,6 +399,7 @@ void setup()
Serial.begin(115200);
u8x8.begin();
aprs_gencrctab();
pinMode(LORA_LED, OUTPUT);
@ -273,7 +479,23 @@ void loopDecoder() {
return;
}
// sonde knows the current type and frequency, and delegates to the right decoder
sonde.receiveFrame();
int res = sonde.receiveFrame();
if(res==0 && connected){
//Send a packet with position information
// first check if ID and position lat+lonis ok
if(sonde.si()->validID && (sonde.si()->validPos&0x03==0x03)) {
Serial.println("Sending position via UDP");
SondeInfo *s = sonde.si();
char raw[201];
const char *str = aprs_senddata(s->lat, s->lon, s->hei, s->hs, s->dir, s->vs, sondeTypeStr[s->type], s->id, "TE0ST", "EO");
int rawlen = aprsstr_mon2raw(str, raw, MAXLEN);
Serial.print("Sending: "); Serial.println(raw);
udp.beginPacket(udpAddress,udpPort);
udp.write((const uint8_t *)raw,rawlen);
udp.endPacket();
}
}
sonde.updateDisplay();
}
@ -341,6 +563,25 @@ String translateEncryptionType(wifi_auth_mode_t encryptionType) {
}
}
//wifi event handler
void WiFiEvent(WiFiEvent_t event){
switch(event) {
case SYSTEM_EVENT_STA_GOT_IP:
//When connected set
Serial.print("WiFi connected! IP address: ");
Serial.println(WiFi.localIP());
//initializes the UDP state
//This initializes the transfer buffer
udp.begin(WiFi.localIP(),udpPort);
connected = true;
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
connected = false;
break;
}
}
static char* _scan[2]={"/","\\"};
void loopWifiScan() {
u8x8.setFont(u8x8_font_chroma48medium8_r);
@ -348,6 +589,7 @@ void loopWifiScan() {
int line=0;
int cnt=0;
WiFi.disconnect(true);
WiFi.mode(WIFI_STA);
const char *id, *pw;
int n = WiFi.scanNetworks();
@ -368,10 +610,13 @@ void loopWifiScan() {
pw=fetchWifiPw(id);
if(pw) break;
}
if(1||!pw) { id="test"; pw="test"; }
if(!pw) { id="test"; pw="test"; }
Serial.print("Connecting to: "); Serial.println(id);
u8x8.drawString(0,6, "Conn:");
u8x8.drawString(6,6, id);
//register event handler
WiFi.onEvent(WiFiEvent);
WiFi.begin(id, pw);
while(WiFi.status() != WL_CONNECTED) {
delay(500);
@ -379,17 +624,20 @@ void loopWifiScan() {
u8x8.drawString(15,7,_scan[cnt&1]);
cnt++;
if(cnt==4) {
WiFi.disconnect(); // retry, for my buggy FritzBox
WiFi.disconnect(true); // retry, for my buggy FritzBox
WiFi.onEvent(WiFiEvent);
WiFi.begin(id, pw);
}
if(cnt==10) {
WiFi.disconnect();
WiFi.disconnect(true);
delay(1000);
WiFi.softAP("sonde","sondesonde");
WiFi.softAP(networks[0].id.c_str(),networks[0].pw.c_str());
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
sonde.setIP(myIP.toString().c_str());
u8x8.drawString(0,6, "AP: ");
u8x8.drawString(6,6, networks[0].id.c_str());
sonde.setIP(myIP.toString().c_str(), true);
sonde.updateDisplayIP();
SetupAsyncServer();
delay(5000);
@ -402,7 +650,7 @@ void loopWifiScan() {
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
sonde.setIP(WiFi.localIP().toString().c_str());
sonde.setIP(WiFi.localIP().toString().c_str(), false);
sonde.updateDisplayIP();
SetupAsyncServer();
delay(5000);

Wyświetl plik

@ -13,22 +13,50 @@
<p><a href="/on"><button class="button">ON</button></a></p>
<p><a href="/off"><button class="button button2">OFF</button></a></p>
-->
<table class="KKK">
<tr>
<th></th>
<th style="width:100px">ID</th>
<th style="width:100px">PW</th>
</tr>
<tr>
<td>1</td>
<td contenteditable>DinoGast</td>
<td contenteditable>Schokolade</td>
<tr>
<td>2</td>
<td contenteditable></td>
<td contenteditable></td>
</tr>
<div class="tab">
<button class="tablinks" onclick="selTab(event,'QRG')" id="defaultTab">QRG</button>
<button class="tablinks" onclick="selTab(event,'WIFI')">WLAN</button>
<button class="tablinks" onclick="selTab(event,'Data')">Data</button>
<button class="tablinks" onclick="selTab(event,'About')">About</button>
</div>
<div id="QRG" class="tabcontent">
<h3> QRG</h3>
<iframe src="qrg.html" style="border:none;" width="100%%" height="100%%"></iframe>
</div>
<div id="WIFI" class="tabcontent">
<h3> WIFI</h3>
<iframe src="wifi.html" style="border:none;" width="100%%" height="100%%"></iframe>
</div>
<div id="Data" class="tabcontent">
<h3>Data</h3>
<iframe src="status.html" style="border:none;" width="100%%" height="100%%"></iframe>
</div>
<div id="About" class="tabcontent">
<h3>About</h3>
RDZSonde
</div>
<script>
function selTab(evt, id) {
var i, tabcontent, tablinks;
tabcontent=document.getElementsByClassName("tabcontent");
for(i=0; i<tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks=document.getElementsByClassName("tablinks");
for(i=0; i<tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(id).style.display = "block";
evt.currentTarget.className += " active";
}
document.getElementById("defaultTab").click();
</script>
</body>
</html>

Wyświetl plik

@ -1,3 +1,5 @@
RDZsonde
RDZsonde
DinoGast
Schokolade
AndroidDD

Wyświetl plik

@ -1,8 +1,8 @@
# Frequency in Mhz (format nnn.nnn)
# Type (4=RS41, 6=DFM normal, DFM-06, 9=DFM inverted, DFM-09)
#
402.700 4
402.300 4
403.450 9
405.100 4
402.700 4 +
402.300 4 +
403.450 9 +
405.100 4 -
# end

Wyświetl plik

@ -1,3 +1,50 @@
body, html {
height: 100%;
margin: 0;
font-family: Arial;
}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
background-color: #ddd
}
td#sfreq {
background-color: #ccc;
}
.tab {
overflow: hidden;
border: 1px solid #ccc;
}
.tab button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
}
.tab button:hover {
background-color: #ddd;
}
.tab button.active {
background-color: #ccc;
}
.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
height: 100%;
}
html {
font-family: Helvetica;
display: inline-block;

66
Setup.md 100644
Wyświetl plik

@ -0,0 +1,66 @@
# Prerequisites
## Arduini IDE
Get the latest Arduino IDE software from arduino.cc/en/Main/Software
## ESP32 support
File -> Preferences (or Arduino -> Preferences on MacOS)
go to "Additional Board Manager URLs"
Add *https://dl.espressif.com/dl/package_esp32_index.json* and press oK
Tool -> Boad -> Boards Manager
search for "esp32"
Install "esp32 by Espressif Systems"
## ESP32 Flash Filesystem Upload support
Get the zip file of the latest release from
https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/
Unzip the content to the tools folder of your Arduino IDE (~/Documents/Arduino/tools on MacOS,
similar on other OS) and restart IDE
## Additional libraries
Select Tools -> Library Manager
Install "U8g2"
## Additional libraries, part 2
From https://github.com/me-no-dev/ESPAsyncWebServer select "Download ZIP", extract to the libraries
folder of your Arduino IDE (~/Documents/Arduino/libraries on MacOS), rename main folder to ESPAsyncWebServer
(remove the "-master")
From https://github.com/me-no-dev/AsyncTCP select "Download ZIP", extract to the libraries folder
of your Arduino IDE, and rename main folder to AsyncTCP
## Additional libraries, part 3
Copy the libraries/SX1278FSK and libraries/SondeLib folder of this project to your Arduino IDE's libraries
folders, or, alternatively, create symbolic links (MacOS/Linux):
cd ~/Documents/Arduino/libraries
ln -s <whereyouclonedthegit>/rdz_ttgo_sonde/libraries/SondeLib/ .
ln -s <whereyouclonedthegit>/rdz_ttgo_sonde/libraries/SX1278FSK/ .
Restart the Arduino IDE
## Final steps
In the IDE Tools -> Board: ->
Select "TTGO LoRa32-OLED v1"
Compile and Upload code
Upload data to SPIFFS with Tools -> ESP32 Sketch Data Upload

Wyświetl plik

@ -4,15 +4,18 @@
extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8;
#define CHANBW 25
#define SMOOTH 2
#define CHANBW 10
#define PIXSAMPL (50/CHANBW)
#define SMOOTH 4
#define STARTF 400000000
#define NCHAN ((int)(6000/CHANBW))
int scanresult[NCHAN];
int scandisp[NCHAN/2];
int scandisp[NCHAN/PIXSAMPL];
#define PLOT_N 120
#define TICK1 (120/6)
#define TICK2 (TICK1/4)
#define PLOT_MIN -220
#define PLOT_SCALE(x) (x<PLOT_MIN?0:(x-PLOT_MIN)/2)
@ -37,6 +40,8 @@ void Scanner::plotResult()
for(int i=0; i<PLOT_N; i+=8) {
for(int j=0; j<8; j++) {
fillTiles(row+j, PLOT_SCALE(scandisp[i+j]));
if( ((i+j)%TICK1)==0) { row[j] |= 0x07; }
if( ((i+j)%TICK2)==0) { row[j] |= 0x01; }
}
for(int y=0; y<8; y++) {
u8x8.drawTile(i/8, y, 1, row+8*y);
@ -59,11 +64,12 @@ void Scanner::scan()
sx1278.writeRegister(REG_RSSI_CONFIG, SMOOTH&0x07);
sx1278.setFrequency(STARTF);
sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE);
delay(5);
delay(20);
unsigned long start = millis();
uint32_t lastfrf=-1;
for(int i=0; i<NCHAN; i++) {
for(int iter=0; iter<2; iter++) { // two interations, to catch all RS41 transmissions
for(int i=0; i<NCHAN; i++) {
float freq = STARTF + 1000.0*i*CHANBW;
uint32_t frf = freq * 1.0 * (1<<19) / SX127X_CRYSTAL_FREQ;
if( (lastfrf>>16)!=(frf>>16) ) {
@ -78,16 +84,24 @@ void Scanner::scan()
int wait = 20 + 1000*(1<<(SMOOTH+1))/4/CHANBW;
delayMicroseconds(wait);
int rssi = -(int)sx1278.readRegister(REG_RSSI_VALUE_FSK);
scanresult[i] = rssi;
if(iter==0) { scanresult[i] = rssi; } else {
if(rssi>scanresult[i]) scanresult[i]=rssi;
}
}
}
unsigned long duration = millis()-start;
Serial.print("Scan time: ");
Serial.println(duration);
for(int i=0; i<NCHAN; i+=2) {
scandisp[i/2] = scanresult[i];
for(int j=1; j<2; j++) { if(scanresult[i+j]>scandisp[i/2]) scandisp[i/2] = scanresult[i+j]; }
for(int i=0; i<NCHAN; i+=PIXSAMPL) {
scandisp[i/PIXSAMPL]=scanresult[i];
for(int j=1; j<PIXSAMPL; j++) { scandisp[i/PIXSAMPL]+=scanresult[i+j]; }
//for(int j=1; j<PIXSAMPL; j++) { if(scanresult[i+j]>scandisp[i/PIXSAMPL]) scandisp[i/PIXSAMPL] = scanresult[i+j]; }
Serial.print(scanresult[i]); Serial.print(", ");
if(((i+1)%32) == 0) Serial.println();
}
Serial.println("\n");
for(int i=0; i<NCHAN/PIXSAMPL; i++) {
scandisp[i]/=PIXSAMPL;
Serial.print(scandisp[i]); Serial.print(", ");
}
Serial.println("\n");
}

Wyświetl plik

@ -22,7 +22,7 @@ static unsigned char stattiles[4][4] = {
0x1F, 0x15, 0x15, 0x00 ,
0x00, 0x1F, 0x00, 0x00 };
byte myIP_tiles[8*10];
byte myIP_tiles[8*11];
static const uint8_t font[10][5]={
0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
@ -37,12 +37,23 @@ static const uint8_t font[10][5]={
0x06, 0x49, 0x39, 0x29, 0x1E }; // 9; .=0x40
static uint8_t halfdb_tile[8]={0x80, 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, 0x00};
static uint8_t halfdb_tile1[8]={0x00, 0x38, 0x28, 0x28, 0x28, 0xC8, 0x00, 0x00};
static uint8_t halfdb_tile2[8]={0x00, 0x11, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00};
static uint8_t empty_tile[8]={0x80, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x00};
static uint8_t empty_tile1[8]={0x00, 0xF0, 0x88, 0x48, 0x28, 0xF0, 0x00, 0x00};
static uint8_t empty_tile2[8]={0x00, 0x11, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00};
static uint8_t ap_tile[8]={0x00,0x04,0x22,0x92, 0x92, 0x22, 0x04, 0x00};
void Sonde::setIP(const char *ip) {
int tp = 0;
void Sonde::setIP(const char *ip, bool AP) {
memset(myIP_tiles, 0, 11*8);
int len = strlen(ip);
int pix = (len-3)*6+6;
int tp = 80-pix+8;
if(AP) memcpy(myIP_tiles+(tp<16?0:8), ap_tile, 8);
for(int i=0; i<len; i++) {
if(ip[i]=='.') { myIP_tiles[tp++]=0x40; myIP_tiles[tp++]=0x00; }
else {
@ -58,18 +69,26 @@ void Sonde::setIP(const char *ip) {
void Sonde::clearSonde() {
nSonde = 0;
}
void Sonde::addSonde(float frequency, SondeType type) {
void Sonde::addSonde(float frequency, SondeType type, int active) {
if(nSonde>=MAXSONDE) {
Serial.println("Cannot add another sonde, MAXSONDE reached");
return;
}
sondeList[nSonde].type = type;
sondeList[nSonde].freq = frequency;
sondeList[nSonde].active = active;
memcpy(sondeList[nSonde].rxStat, "\x00\x01\x2\x3\x2\x1\x1\x2\x0\x3\x0\x0\x1\x2\x3\x1\x0", 18);
nSonde++;
}
void Sonde::nextConfig() {
currentSonde++;
// Skip non-active entries (but don't loop forever if there are no active ones
for(int i=0; i<MAXSONDE; i++) {
if(!sondeList[currentSonde].active) {
currentSonde++;
if(currentSonde>=nSonde) currentSonde=0;
}
}
if(currentSonde>=nSonde) {
currentSonde=0;
}
@ -160,7 +179,8 @@ void Sonde::updateDisplayRSSI() {
int len=strlen(buf)-3;
buf[5]=0;
u8x8.drawString(0,6,buf);
u8x8.drawTile(len,6,1,(sonde.si()->rssi&1)?halfdb_tile:empty_tile);
u8x8.drawTile(len,6,1,(sonde.si()->rssi&1)?halfdb_tile1:empty_tile1);
u8x8.drawTile(len,7,1,(sonde.si()->rssi&1)?halfdb_tile2:empty_tile2);
}
void Sonde::updateStat() {
@ -183,7 +203,7 @@ void Sonde::updateDisplayRXConfig() {
}
void Sonde::updateDisplayIP() {
u8x8.drawTile(6, 7, 10, myIP_tiles);
u8x8.drawTile(5, 7, 11, myIP_tiles);
}
// Probing RS41

Wyświetl plik

@ -12,6 +12,7 @@ extern const char *sondeTypeStr[5];
typedef struct st_sondeinfo {
// receiver configuration
bool active;
SondeType type;
float freq;
// decoded ID
@ -37,12 +38,13 @@ typedef struct st_sondeinfo {
class Sonde
{
private:
int nSonde;
int currentSonde = 0;
SondeInfo sondeList[MAXSONDE+1];
public:
int currentSonde = 0;
int nSonde;
SondeInfo sondeList[MAXSONDE+1];
void clearSonde();
void addSonde(float frequency, SondeType type);
void addSonde(float frequency, SondeType type, int active);
void nextConfig();
void setup();
@ -59,7 +61,7 @@ public:
void updateDisplay();
void updateDisplayScanner();
void clearDisplay();
void setIP(const char *ip);
void setIP(const char *ip, bool isAP);
};
extern Sonde sonde;

Wyświetl plik

@ -0,0 +1,286 @@
/* Copyright (C) Hansi Reiser, dl9rdz
*
* partially based on dxlAPRS toolchain
*
* Copyright (C) Christian Rabler <oe5dxl@oevsv.at>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>;
//#include <arpa/inet.h>
//#include <sys/socket.h>
#include <math.h>
#include <unistd.h>
#include <inttypes.h>
#include "aprs.h"
#if 0
int openudp(const char *ip, int port, struct sockaddr_in *si) {
int fd;
if((fd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) return -1;
memset((char *)&si, 0, sizeof(si));
si->sin_family = AF_INET;
si->sin_port = htons(port);
if(inet_aton(ip, &(si->sin_addr))==0) {
return -1;
}
return fd;
}
int sendudp(int fd, struct sockaddr_in *si, char *frame, int framelen)
{
if(sendto(fd, frame, framelen, 0, (struct sockaddr *)si, sizeof(struct sockaddr_in))==-1) {
return -1;
}
return 0;
}
#endif
void aprsstr_append(char *b, const char *data)
{
int blen=strlen(b);
int len=strlen(data);
if(blen+len>MAXLEN) len=MAXLEN-blen;
strncat(b, data, len);
}
uint32_t realcard(float x) {
if(x<0) return 0;
else return (uint32_t)x;
}
/* CRC for AXUDP frames */
#define APRSCRC_POLY 0x8408
static uint8_t CRCL[256];
static uint8_t CRCH[256];
void aprs_gencrctab(void)
{
uint32_t c;
uint32_t crc;
uint32_t i;
for (c = 0UL; c<=255UL; c++) {
crc = 255UL-c;
for (i = 0UL; i<=7UL; i++) {
if ((crc&1)) crc = (uint32_t)((uint32_t)(crc>>1)^APRSCRC_POLY);
else crc = crc>>1;
} /* end for */
CRCL[c] = (uint8_t)crc;
CRCH[c] = (uint8_t)(255UL-(crc>>8));
} /* end for */
} /* end Gencrctab() */
static void aprsstr_appcrc(char frame[], uint32_t frame_len, int32_t size)
{
uint8_t h;
uint8_t l;
uint8_t b;
int32_t i;
int32_t tmp;
l = 0U;
h = 0U;
tmp = size-1L;
i = 0L;
if (i<=tmp) for (;; i++) {
b = (uint8_t)((uint8_t)(uint8_t)frame[i]^l);
l = CRCL[b]^h;
h = CRCH[b];
if (i==tmp) break;
} /* end for */
frame[size] = (char)l;
frame[size+1L] = (char)h;
} /* end aprsstr_appcrc() */
static int mkaprscall(int32_t * p, char raw[],
uint32_t * i, const char mon[],
char sep1, char sep2, char sep3,
uint32_t sbase)
{
uint32_t s;
uint32_t l;
l = 0UL;
while ((((mon[*i] && mon[*i]!=sep1) && mon[*i]!=sep2) && mon[*i]!=sep3)
&& mon[*i]!='-') {
s = (uint32_t)(uint8_t)mon[*i]*2UL&255UL;
if (s<=64UL) return 0;
raw[*p] = (char)s;
++*p;
++*i;
++l;
if (l>=7UL) return 0;
}
while (l<6UL) {
raw[*p] = '@';
++*p;
++l;
}
s = 0UL;
if (mon[*i]=='-') {
++*i;
while ((uint8_t)mon[*i]>='0' && (uint8_t)mon[*i]<='9') {
s = (s*10UL+(uint32_t)(uint8_t)mon[*i])-48UL;
++*i;
}
if (s>15UL) return 0;
}
raw[*p] = (char)((s+sbase)*2UL);
++*p;
return 1;
} /* end call() */
// returns raw len, 0 in case of error
extern int aprsstr_mon2raw(const char *mon, char raw[], int raw_len)
{
uint32_t r;
uint32_t n;
uint32_t i;
uint32_t tmp;
int p = 7L;
i = 0UL;
fprintf(stderr,"mon2raw for %s\n", mon);
if (!mkaprscall(&p, raw, &i, mon, '>', 0, 0, 48UL)) {
return 0;
}
p = 0L;
if (mon[i]!='>') return 0;
/* ">" */
++i;
if (!mkaprscall(&p, raw, &i, mon, ':', ',', 0, 112UL)) {
return 0;
}
p = 14L;
n = 0UL;
while (mon[i]==',') {
++i;
if (!mkaprscall(&p, raw, &i, mon, ':', ',', '*', 48UL)) {
return 0;
}
++n;
if (n>8UL) {
return 0;
}
if (mon[i]=='*') {
/* "*" has repeatet sign */
++i;
r = (uint32_t)p;
if (r>=21UL) for (tmp = (uint32_t)(r-21UL)/7UL;;) {
raw[r-1UL] = (char)((uint32_t)(uint8_t)raw[r-1UL]+128UL);
/* set "has repeated" flags */
if (!tmp) break;
--tmp;
r -= 7UL;
} /* end for */
}
}
if (p==0L || mon[i]!=':') {
return 0;
}
raw[p-1L] = (char)((uint32_t)(uint8_t)raw[p-1L]+1UL);
/* end address field mark */
raw[p] = '\003';
++p;
raw[p] = '\360';
++p;
++i;
n = 256UL;
while (mon[i]) {
/* copy info part */
if (p>=(int32_t)(raw_len-1)-2L || n==0UL) {
return 0;
}
raw[p] = mon[i];
++p;
++i;
--n;
}
aprsstr_appcrc(raw, raw_len, p);
fprintf(stderr,"results in %s\n",raw);
return p+2;
} /* end mon2raw() */
#define FEET (1.0/0.3048)
#define KNOTS (1.851984)
char b[201];
char raw[201];
char * aprs_senddata(float lat, float lon, float hei, float speed, float dir, float climb, const char *type, const char *objname, const char *usercall, const char *sym)
{
*b=0;
aprsstr_append(b, usercall);
aprsstr_append(b, ">");
const char *destcall="APZRDZ";
aprsstr_append(b, destcall);
// uncompressed
aprsstr_append(b, ":;");
char tmp[10];
snprintf(tmp,10,"%s ",objname);
aprsstr_append(b, tmp);
aprsstr_append(b, "*");
// TODO: time
//aprsstr_append_data(time, ds);
aprsstr_append(b, "121212z");
int i = strlen(b);
int lati = abs((int)lat);
int latm = (fabs(lat)-lati)*6000;
snprintf(b+i, MAXLEN-i, "%02d%02d.%02d%c%c", lati, latm/100, latm%100, lat<0?'S':'N', sym[0]);
i = strlen(b);
int loni = abs((int)lon);
int lonm = (fabs(lon)-loni)*6000;
snprintf(b+i, MAXLEN-i, "%03d%02d.%02d%c%c", loni, lonm/100, lonm%100, lon<0?'W':'E', sym[1]);
#if 1
if(speed>0.5) {
i=strlen(b);
snprintf(b+i, MAXLEN-i, "%03d/%03d", realcard(dir+1.5), realcard(speed*1.0/KNOTS+0.5));
}
#endif
if(hei>0.5) {
i=strlen(b);
snprintf(b+i, MAXLEN-i, "/A=%06d", realcard(hei*FEET+0.5));
}
#if 0
int dao=1;
if(dao) {
i=strlen(b);
snprintf(b+i, MAXLEN-i, "!w%c%c!", 33+dao91(lat), 33+dao91(lon));
}
#endif
const char *comm="&test";
strcat(b, comm);
return b;
}
#if 0
int main(int argc, char *argv[])
{
Gencrctab();
struct sockaddr_in si;
int fd = openudp("127.0.0.1",9002,&si);
if(fd<0) { fprintf(stderr,"open failed\n"); return 1; }
float lat=48, lon=10;
while(1) {
const char *str = aprs_senddata(lat, lon, 543, 5, 180, 1.5, "RS41", "TE0ST", "TE1ST", "EO");
int rawlen = aprsstr_mon2raw(str, raw, MAXLEN);
sendudp(fd, raw, rawlen);
str = "OE3XKC>APMI06,qAR,OE3XLR:;ER-341109*111111z4803.61NE01532.39E0145.650MHz R15k OE3XPA";
rawlen = aprsstr_mon2raw(str, raw, MAXLEN);
sendudp(fd, &si, raw, rawlen);
lat += 0.002; lon += 0.01;
sleep(5);
}
}
#endif

Wyświetl plik

@ -0,0 +1,21 @@
#ifndef _aprs_h
#define _aprs_h
enum IDTYPE { ID_DFMDXL, ID_DFMREAL, ID_DFMAUTO };
typedef struct st_feedinfo {
bool active;
int type; // 0:UDP(axudp), 1:TCP(aprs.fi)
char *host;
int port;
int rate;
int idformat; // 0: dxl 1: real 2: auto
}
#define MAXLEN 201
void aprs_gencrctab(void);
int aprsstr_mon2raw(const char *mon, char raw[], int raw_len);
char * aprs_senddata(float lat, float lon, float hei, float speed, float dir, float climb, const char *type, const char *objname, const char *usercall, const char *sym);
#endif