2019-04-03 15:16:51 +00:00
|
|
|
#include <U8x8lib.h>
|
|
|
|
#include <U8g2lib.h>
|
|
|
|
|
|
|
|
#include "Sonde.h"
|
2019-04-05 18:05:18 +00:00
|
|
|
#include "RS41.h"
|
|
|
|
#include "DFM.h"
|
2019-04-03 15:16:51 +00:00
|
|
|
|
2019-04-16 15:51:41 +00:00
|
|
|
extern U8X8_SSD1306_128X64_NONAME_SW_I2C *u8x8;
|
2019-04-03 15:16:51 +00:00
|
|
|
|
2019-04-05 18:05:18 +00:00
|
|
|
//SondeInfo si = { STYPE_RS41, 403.450, "P1234567", true, 48.1234, 14.9876, 543, 3.97, -0.5, true, 120 };
|
|
|
|
const char *sondeTypeStr[5] = { "DFM6", "DFM9", "RS41" };
|
2019-04-03 15:16:51 +00:00
|
|
|
|
|
|
|
static unsigned char kmh_tiles[] U8X8_PROGMEM = {
|
|
|
|
0x1F, 0x04, 0x0A, 0x11, 0x00, 0x1F, 0x02, 0x04, 0x42, 0x3F, 0x10, 0x08, 0xFC, 0x22, 0x20, 0xF8
|
|
|
|
};
|
|
|
|
static unsigned char ms_tiles[] U8X8_PROGMEM = {
|
|
|
|
0x1F, 0x02, 0x04, 0x02, 0x1F, 0x40, 0x20, 0x10, 0x08, 0x04, 0x12, 0xA4, 0xA4, 0xA4, 0x40, 0x00
|
|
|
|
};
|
2019-04-05 18:05:18 +00:00
|
|
|
static unsigned char stattiles[4][4] = {
|
2019-04-17 12:54:08 +00:00
|
|
|
0x00, 0x1F, 0x00, 0x00 , // | == ok
|
|
|
|
0x00, 0x10, 0x10, 0x00 , // . == no header found
|
|
|
|
0x1F, 0x15, 0x15, 0x00 , // E == decode error
|
|
|
|
0x00, 0x00, 0x00, 0x00 }; // ' ' == unknown/unassigned
|
2019-04-05 18:05:18 +00:00
|
|
|
|
2019-04-08 17:22:08 +00:00
|
|
|
byte myIP_tiles[8*11];
|
2019-04-05 18:05:18 +00:00
|
|
|
|
|
|
|
static const uint8_t font[10][5]={
|
|
|
|
0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
|
|
|
|
0x00, 0x42, 0x7F, 0x40, 0x00, // 1
|
|
|
|
0x42, 0x61, 0x51, 0x49, 0x46, // 2
|
|
|
|
0x21, 0x41, 0x45, 0x4B, 0x31, // 3
|
|
|
|
0x18, 0x14, 0x12, 0x7F, 0x10, // 4
|
|
|
|
0x27, 0x45, 0x45, 0x45, 0x39, // 5
|
|
|
|
0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
|
|
|
|
0x01, 0x01, 0x79, 0x05, 0x03, // 7
|
|
|
|
0x36, 0x49, 0x49, 0x49, 0x36, // 8
|
|
|
|
0x06, 0x49, 0x39, 0x29, 0x1E }; // 9; .=0x40
|
|
|
|
|
2019-04-05 21:32:26 +00:00
|
|
|
static uint8_t halfdb_tile[8]={0x80, 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, 0x00};
|
2019-04-08 17:22:08 +00:00
|
|
|
|
|
|
|
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};
|
|
|
|
|
2019-04-05 21:32:26 +00:00
|
|
|
static uint8_t empty_tile[8]={0x80, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x00};
|
|
|
|
|
2019-04-08 17:22:08 +00:00
|
|
|
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};
|
2019-04-05 21:32:26 +00:00
|
|
|
|
2019-04-15 18:59:30 +00:00
|
|
|
Sonde::Sonde() {
|
2019-04-17 12:54:08 +00:00
|
|
|
config.button_pin = 0;
|
|
|
|
config.oled_sda = 4;
|
|
|
|
config.oled_scl = 15;
|
|
|
|
config.oled_rst = 16;
|
|
|
|
|
2019-04-15 18:59:30 +00:00
|
|
|
config.noisefloor = -130;
|
|
|
|
strcpy(config.call,"NOCALL");
|
|
|
|
strcpy(config.passcode, "---");
|
|
|
|
config.udpfeed.active = 1;
|
|
|
|
config.udpfeed.type = 0;
|
|
|
|
strcpy(config.udpfeed.host, "192.168.42.20");
|
2019-04-16 14:52:16 +00:00
|
|
|
strcpy(config.udpfeed.symbol, "/O");
|
2019-04-15 18:59:30 +00:00
|
|
|
config.udpfeed.port = 9002;
|
|
|
|
config.udpfeed.highrate = 1;
|
|
|
|
config.udpfeed.idformat = ID_DFMGRAW;
|
|
|
|
config.tcpfeed.active = 0;
|
|
|
|
config.tcpfeed.type = 1;
|
|
|
|
strcpy(config.tcpfeed.host, "radiosondy.info");
|
2019-04-16 14:52:16 +00:00
|
|
|
strcpy(config.tcpfeed.symbol, "/O");
|
2019-04-15 18:59:30 +00:00
|
|
|
config.tcpfeed.port = 12345;
|
|
|
|
config.tcpfeed.highrate = 10;
|
|
|
|
config.tcpfeed.idformat = ID_DFMDXL;
|
|
|
|
}
|
2019-04-08 17:22:08 +00:00
|
|
|
|
2019-04-16 15:51:41 +00:00
|
|
|
void Sonde::setConfig(const char *cfg) {
|
|
|
|
while(*cfg==' '||*cfg=='\t') cfg++;
|
|
|
|
if(*cfg=='#') return;
|
|
|
|
char *s = strchr(cfg,'=');
|
|
|
|
if(!s) return;
|
|
|
|
char *val = s+1;
|
|
|
|
*s=0; s--;
|
|
|
|
while(s>cfg && (*s==' '||*s=='\t')) { *s=0; s--; }
|
|
|
|
Serial.printf("handling option '%s'='%s'\n", cfg, val);
|
|
|
|
if(strcmp(cfg,"noisefloor")==0) {
|
|
|
|
config.noisefloor = atoi(val);
|
|
|
|
if(config.noisefloor==0) config.noisefloor=-130;
|
|
|
|
} else if(strcmp(cfg,"call")==0) {
|
|
|
|
strncpy(config.call, val, 9);
|
|
|
|
} else if(strcmp(cfg,"passcode")==0) {
|
|
|
|
strncpy(config.passcode, val, 9);
|
2019-04-17 12:54:08 +00:00
|
|
|
} else if(strcmp(cfg,"button_pin")==0) {
|
|
|
|
config.button_pin = atoi(val);
|
2019-04-16 15:51:41 +00:00
|
|
|
} else if(strcmp(cfg,"oled_sda")==0) {
|
|
|
|
config.oled_sda = atoi(val);
|
|
|
|
} else if(strcmp(cfg,"oled_scl")==0) {
|
|
|
|
config.oled_scl = atoi(val);
|
|
|
|
} else if(strcmp(cfg,"oled_rst")==0) {
|
|
|
|
config.oled_rst = atoi(val);
|
|
|
|
} else if(strcmp(cfg,"axudp.active")==0) {
|
|
|
|
config.udpfeed.active = atoi(val)>0;
|
|
|
|
} else if(strcmp(cfg,"axudp.host")==0) {
|
|
|
|
strncpy(config.udpfeed.host, val, 63);
|
|
|
|
} else if(strcmp(cfg,"axudp.port")==0) {
|
|
|
|
config.udpfeed.port = atoi(val);
|
|
|
|
} else if(strcmp(cfg,"axudp.symbol")==0) {
|
|
|
|
strncpy(config.udpfeed.symbol, val, 3);
|
|
|
|
} else if(strcmp(cfg,"axudp.highrate")==0) {
|
|
|
|
config.udpfeed.highrate = atoi(val);
|
|
|
|
} else if(strcmp(cfg,"axudp.idformat")==0) {
|
|
|
|
config.udpfeed.idformat = atoi(val);
|
|
|
|
} else if(strcmp(cfg,"tcp.active")==0) {
|
|
|
|
config.tcpfeed.active = atoi(val)>0;
|
|
|
|
} else if(strcmp(cfg,"tcp.host")==0) {
|
|
|
|
strncpy(config.tcpfeed.host, val, 63);
|
|
|
|
} else if(strcmp(cfg,"tcp.port")==0) {
|
|
|
|
config.tcpfeed.port = atoi(val);
|
|
|
|
} else if(strcmp(cfg,"tcp.symbol")==0) {
|
|
|
|
strncpy(config.tcpfeed.symbol, val, 3);
|
|
|
|
} else if(strcmp(cfg,"tcp.highrate")==0) {
|
|
|
|
config.tcpfeed.highrate = atoi(val);
|
|
|
|
} else if(strcmp(cfg,"tcp.idformat")==0) {
|
|
|
|
config.tcpfeed.idformat = atoi(val);
|
|
|
|
} else {
|
|
|
|
Serial.printf("Invalid config option '%s'='%s'\n", cfg, val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 17:22:08 +00:00
|
|
|
void Sonde::setIP(const char *ip, bool AP) {
|
|
|
|
memset(myIP_tiles, 0, 11*8);
|
2019-04-05 18:05:18 +00:00
|
|
|
int len = strlen(ip);
|
2019-04-08 17:22:08 +00:00
|
|
|
int pix = (len-3)*6+6;
|
|
|
|
int tp = 80-pix+8;
|
|
|
|
if(AP) memcpy(myIP_tiles+(tp<16?0:8), ap_tile, 8);
|
2019-04-05 18:05:18 +00:00
|
|
|
for(int i=0; i<len; i++) {
|
|
|
|
if(ip[i]=='.') { myIP_tiles[tp++]=0x40; myIP_tiles[tp++]=0x00; }
|
|
|
|
else {
|
|
|
|
int idx = ip[i]-'0';
|
|
|
|
memcpy(myIP_tiles+tp, &font[idx], 5);
|
|
|
|
myIP_tiles[tp+5] = 0;
|
|
|
|
tp+=6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while(tp<8*10) { myIP_tiles[tp++]=0; }
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sonde::clearSonde() {
|
|
|
|
nSonde = 0;
|
|
|
|
}
|
2019-04-08 17:22:08 +00:00
|
|
|
void Sonde::addSonde(float frequency, SondeType type, int active) {
|
2019-04-05 18:05:18 +00:00
|
|
|
if(nSonde>=MAXSONDE) {
|
|
|
|
Serial.println("Cannot add another sonde, MAXSONDE reached");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sondeList[nSonde].type = type;
|
|
|
|
sondeList[nSonde].freq = frequency;
|
2019-04-08 17:22:08 +00:00
|
|
|
sondeList[nSonde].active = active;
|
2019-04-17 12:54:08 +00:00
|
|
|
memcpy(sondeList[nSonde].rxStat, "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", 18); // unknown/undefined
|
2019-04-05 18:05:18 +00:00
|
|
|
nSonde++;
|
|
|
|
}
|
|
|
|
void Sonde::nextConfig() {
|
|
|
|
currentSonde++;
|
2019-04-08 17:22:08 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
2019-04-05 18:05:18 +00:00
|
|
|
if(currentSonde>=nSonde) {
|
|
|
|
currentSonde=0;
|
|
|
|
}
|
|
|
|
setup();
|
|
|
|
}
|
|
|
|
SondeInfo *Sonde::si() {
|
|
|
|
return &sondeList[currentSonde];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sonde::setup() {
|
|
|
|
// Test only: setIP("123.456.789.012");
|
|
|
|
// update receiver config: TODO
|
|
|
|
Serial.print("Setting up receiver on channel ");
|
|
|
|
Serial.println(currentSonde);
|
|
|
|
switch(sondeList[currentSonde].type) {
|
|
|
|
case STYPE_RS41:
|
|
|
|
rs41.setup();
|
|
|
|
rs41.setFrequency(sondeList[currentSonde].freq * 1000000);
|
|
|
|
break;
|
|
|
|
case STYPE_DFM06:
|
|
|
|
case STYPE_DFM09:
|
|
|
|
dfm.setup( sondeList[currentSonde].type==STYPE_DFM06?0:1 );
|
|
|
|
dfm.setFrequency(sondeList[currentSonde].freq * 1000000);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Update display
|
|
|
|
//updateDisplayRXConfig();
|
|
|
|
//updateDisplay();
|
|
|
|
}
|
|
|
|
int Sonde::receiveFrame() {
|
|
|
|
int ret;
|
|
|
|
if(sondeList[currentSonde].type == STYPE_RS41) {
|
|
|
|
ret = rs41.receiveFrame();
|
|
|
|
} else {
|
|
|
|
ret = dfm.receiveFrame();
|
|
|
|
}
|
|
|
|
memmove(sonde.si()->rxStat+1, sonde.si()->rxStat, 17);
|
2019-04-17 12:54:08 +00:00
|
|
|
sonde.si()->rxStat[0] = ret;
|
|
|
|
return ret; // 0: OK, 1: Timeuot, 2: Other error, 3: unknown
|
2019-04-05 18:05:18 +00:00
|
|
|
}
|
2019-04-03 15:16:51 +00:00
|
|
|
|
|
|
|
void Sonde::updateDisplayPos() {
|
|
|
|
char buf[16];
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->setFont(u8x8_font_7x14_1x2_r);
|
2019-04-05 18:05:18 +00:00
|
|
|
if(si()->validPos) {
|
|
|
|
snprintf(buf, 16, "%2.5f", si()->lat);
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString(0,2,buf);
|
2019-04-05 18:05:18 +00:00
|
|
|
snprintf(buf, 16, "%2.5f", si()->lon);
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString(0,4,buf);
|
2019-04-03 15:16:51 +00:00
|
|
|
} else {
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString(0,2,"<??> ");
|
|
|
|
u8x8->drawString(0,4,"<??> ");
|
2019-04-03 15:16:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sonde::updateDisplayPos2() {
|
|
|
|
char buf[16];
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
2019-04-05 18:05:18 +00:00
|
|
|
if(!si()->validPos) {
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString(10,2," ");
|
|
|
|
u8x8->drawString(10,3," ");
|
|
|
|
u8x8->drawString(10,4," ");
|
2019-04-03 15:16:51 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-04-15 18:28:50 +00:00
|
|
|
snprintf(buf, 16, si()->hei>999?" %5.0fm":" %3.1fm", si()->hei);
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString((10+6-strlen(buf)),2,buf);
|
2019-04-15 18:28:50 +00:00
|
|
|
snprintf(buf, 16, si()->hs>99?" %3.0f":" %2.1f", si()->hs);
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString((10+4-strlen(buf)),3,buf);
|
2019-04-15 18:28:50 +00:00
|
|
|
snprintf(buf, 16, " %+2.1f", si()->vs);
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString((10+4-strlen(buf)),4,buf);
|
|
|
|
u8x8->drawTile(14,3,2,kmh_tiles);
|
|
|
|
u8x8->drawTile(14,4,2,ms_tiles);
|
2019-04-03 15:16:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sonde::updateDisplayID() {
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
2019-04-05 18:05:18 +00:00
|
|
|
if(si()->validID) {
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString(0,1, si()->id);
|
2019-04-03 15:16:51 +00:00
|
|
|
} else {
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString(0,1, "nnnnnnnn ");
|
2019-04-03 15:16:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sonde::updateDisplayRSSI() {
|
|
|
|
char buf[16];
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->setFont(u8x8_font_7x14_1x2_r);
|
2019-04-05 21:32:26 +00:00
|
|
|
snprintf(buf, 16, "-%d ", sonde.si()->rssi/2);
|
|
|
|
int len=strlen(buf)-3;
|
|
|
|
buf[5]=0;
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString(0,6,buf);
|
|
|
|
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);
|
2019-04-03 15:16:51 +00:00
|
|
|
}
|
|
|
|
|
2019-04-05 18:05:18 +00:00
|
|
|
void Sonde::updateStat() {
|
|
|
|
uint8_t *stat = si()->rxStat;
|
|
|
|
for(int i=0; i<18; i+=2) {
|
|
|
|
uint8_t tile[8];
|
|
|
|
*(uint32_t *)(&tile[0]) = *(uint32_t *)(&(stattiles[stat[i]]));
|
|
|
|
*(uint32_t *)(&tile[4]) = *(uint32_t *)(&(stattiles[stat[i+1]]));
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawTile(7+i/2, 6, 1, tile);
|
2019-04-05 18:05:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-03 15:16:51 +00:00
|
|
|
void Sonde::updateDisplayRXConfig() {
|
|
|
|
char buf[16];
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->setFont(u8x8_font_chroma48medium8_r);
|
|
|
|
u8x8->drawString(0,0, sondeTypeStr[si()->type]);
|
2019-04-05 18:05:18 +00:00
|
|
|
snprintf(buf, 16, "%3.3f MHz", si()->freq);
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString(5,0, buf);
|
2019-04-03 15:16:51 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-04-05 18:05:18 +00:00
|
|
|
void Sonde::updateDisplayIP() {
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawTile(5, 7, 11, myIP_tiles);
|
2019-04-05 18:05:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Probing RS41
|
|
|
|
// 40x.xxx MHz
|
|
|
|
void Sonde::updateDisplayScanner() {
|
|
|
|
char buf[16];
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->setFont(u8x8_font_7x14_1x2_r);
|
|
|
|
u8x8->drawString(0, 0, "Probing");
|
|
|
|
u8x8->drawString(8, 0, sondeTypeStr[si()->type]);
|
2019-04-05 18:05:18 +00:00
|
|
|
snprintf(buf, 16, "%3.3f MHz", si()->freq);
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->drawString(0,3, buf);
|
2019-04-05 18:05:18 +00:00
|
|
|
updateDisplayIP();
|
|
|
|
}
|
|
|
|
|
2019-04-03 15:16:51 +00:00
|
|
|
void Sonde::updateDisplay()
|
|
|
|
{
|
|
|
|
char buf[16];
|
|
|
|
updateDisplayRXConfig();
|
|
|
|
updateDisplayID();
|
|
|
|
updateDisplayPos();
|
|
|
|
updateDisplayPos2();
|
|
|
|
updateDisplayRSSI();
|
2019-04-05 18:05:18 +00:00
|
|
|
updateStat();
|
|
|
|
updateDisplayIP();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sonde::clearDisplay() {
|
2019-04-16 15:51:41 +00:00
|
|
|
u8x8->clearDisplay();
|
2019-04-03 15:16:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Sonde sonde = Sonde();
|