#include #include #include #include #include #include "Display.h" #include "Sonde.h" #include "math.h" int readLine(Stream &stream, char *buffer, int maxlen); extern const char *version_name; extern const char *version_id; #include <../fonts/FreeMono9pt7b.h> #include <../fonts/FreeMono12pt7b.h> #include <../fonts/FreeSans9pt7b.h> #include <../fonts/FreeSans12pt7b.h> #include <../fonts/Picopixel.h> extern Sonde sonde; extern MicroNMEA nmea; extern AXP20X_Class axp; extern bool axp192_found; extern SemaphoreHandle_t axpSemaphore; SPIClass spiDisp(HSPI); const char *sondeTypeStr[NSondeTypes] = { "DFM6", "DFM9", "RS41", "RS92", "M10 " }; byte myIP_tiles[8*11]; static uint8_t ap_tile[8]={0x00,0x04,0x22,0x92, 0x92, 0x22, 0x04, 0x00}; 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 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, 0xA8, 0xA8, 0xA4, 0x40, 0x00 }; static unsigned char stattiles[5][4] = { 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 0x07, 0x05, 0x07, 0x00 }; // ° = rx ok, but no valid position static unsigned char stattilesXL[5][5] = { 0x00, 0x7F, 0x00, 0x00, 0x00, // | == ok 0x00, 0x40, 0x40, 0x00, 0x00, // . == no header found 0x7F, 0x49, 0x49, 0x49, 0x00, // E == decode error 0x00, 0x00, 0x00, 0x00, 0x00, // ' ' == unknown/unassigned 0x07, 0x05, 0x07, 0x00, 0x00 }; // ° = rx ok, but no valid position (not yet used?) //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 gps_tile[8]={0x3E, 0x77, 0x63, 0x77, 0x3E, 0x1C, 0x08, 0x00}; static uint8_t gps_tile[8]={0x00, 0x0E, 0x1F, 0x3B, 0x71, 0x3B, 0x1F, 0x0E}; static uint8_t nogps_tile[8]={0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x00}; static uint8_t deg_tile[8]={0x00, 0x06,0x09, 0x09, 0x06, 0x00, 0x00, 0x00}; /* Description of display layouts. * for each display, the content is described by a DispEntry structure * timeout values are in milliseconds, for view activ, rx signal present, no rx signal present * for each displey, actions (switching to different sonde or different view) can be defined * based on key presses or on expired timeouts */ DispEntry searchLayout[] = { {0, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawText, "Scan:"}, {0, 8, FONT_LARGE, -1, 0xFFFF, 0, disp.drawType, NULL}, {3, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawFreq, " MHz"}, {5, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawSite, "l"}, {7, 5, 0, -1, 0xFFFF, 0, disp.drawIP, NULL}, {-1, -1, -1, 0, 0, 0, NULL, NULL}, }; int16_t searchTimeouts[] = { -1, 0, 0 }; uint8_t searchActions[] = { ACT_NONE, ACT_DISPLAY_DEFAULT, ACT_NONE, ACT_DISPLAY_SPECTRUM, ACT_DISPLAY_WIFI, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_DISPLAY_DEFAULT, ACT_NEXTSONDE}; DispEntry legacyLayout[] = { {0, 5, FONT_SMALL, -1, 0xFFFF, 0, disp.drawFreq, " MHz"}, {1, 8, FONT_SMALL, -1, 0xFFFF, 0, disp.drawAFC, NULL}, {0, 0, FONT_SMALL, -1, 0xFFFF, 0, disp.drawType, NULL}, {1, 0, FONT_SMALL, -1, 0xFFFF, 0, disp.drawID, NULL}, {2, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawLat, NULL}, {4, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawLon, NULL}, {2, 10, FONT_SMALL, -1, 0xFFFF, 0, disp.drawAlt, NULL}, {3, 10, FONT_SMALL, -1, 0xFFFF, 0, disp.drawHS, NULL}, {4, 9, FONT_SMALL, -1, 0xFFFF, 0, disp.drawVS, NULL}, {6, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawRSSI, NULL}, {6, 7, 0, -1, 0xFFFF, 0, disp.drawQS, NULL}, {7, 0, FONT_SMALL, -1, 0xFFFF, 0, disp.drawVBat, "%"}, {7, 5, 0, -1, 0xFFFF, 0, disp.drawIP, NULL}, {-1, -1, -1, 0, 0, 0, NULL, NULL}, }; int16_t legacyTimeouts[] = { -1, -1, 20000 }; uint8_t legacyActions[] = { ACT_NONE, ACT_NEXTSONDE, ACT_DISPLAY(0), ACT_DISPLAY_SPECTRUM, ACT_DISPLAY_WIFI, ACT_DISPLAY(2), ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_DISPLAY(0)}; DispEntry fieldLayout[] = { {2, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawLat, NULL}, {4, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawLon, NULL}, {3, 10, FONT_SMALL, -1, 0xFFFF, 0, disp.drawHS, NULL}, {4, 9, FONT_SMALL, -1, 0xFFFF, 0, disp.drawVS, NULL}, {0, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawID, NULL}, {6, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawAlt, NULL}, {6, 7, 0, -1, 0xFFFF, 0, disp.drawQS, NULL}, {-1, -1, -1, 0, 0, 0, NULL, NULL}, }; int16_t fieldTimeouts[] = { -1, -1, -1 }; uint8_t fieldActions[] = { ACT_NONE, ACT_NEXTSONDE, ACT_DISPLAY(0), ACT_DISPLAY_SPECTRUM, ACT_DISPLAY_WIFI, ACT_DISPLAY(4), ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE}; DispEntry field2Layout[] = { {2, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawLat, NULL}, {4, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawLon, NULL}, {1, 12, FONT_SMALL, -1, 0xFFFF, 0, disp.drawType, NULL}, {0, 9, FONT_SMALL, -1, 0xFFFF, 0, disp.drawFreq, ""}, {3, 10, FONT_SMALL, -1, 0xFFFF, 0, disp.drawHS, NULL}, {4, 9, FONT_SMALL, -1, 0xFFFF, 0, disp.drawVS, NULL}, {0, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawID, NULL}, {6, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawAlt, NULL}, {6, 7, 0, -1, 0xFFFF, 0, disp.drawQS, NULL}, {-1, -1, -1, 0, 0, 0, NULL, NULL}, }; uint8_t field2Actions[] = { ACT_NONE, ACT_NEXTSONDE, ACT_DISPLAY(0), ACT_DISPLAY_SPECTRUM, ACT_DISPLAY_WIFI, ACT_DISPLAY(1), ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE}; DispEntry gpsLayout[] = { {0, 0, FONT_LARGE, -1, 0xFFFF, 0, disp.drawID, NULL}, {2, 0, FONT_SMALL, -1, 0xFFFF, 0, disp.drawLat, NULL}, {3, 0, FONT_SMALL, -1, 0xFFFF, 0, disp.drawLon, NULL}, {4, 0, FONT_SMALL, -1, 0xFFFF, 0, disp.drawAlt, NULL}, //{6, 0, FONT_SMALL, -1, 0xFFFF, 0, disp.drawGPS, "V"}, {6, 1, FONT_SMALL, -1, 0xFFFF, 0,disp.drawGPS, "A"}, {6, 8, FONT_SMALL, -1, 0xFFFF, 0,disp.drawGPS, "O"}, {7, 0, FONT_SMALL, -1, 0xFFFF, 0, disp.drawGPS, "D"}, {7, 8, FONT_SMALL, -1, 0xFFFF, 0, disp.drawGPS, "I"}, {-1, -1, -1, 0, 0, 0, NULL, NULL}, }; uint8_t gpsActions[] = { ACT_NONE, ACT_NEXTSONDE, ACT_DISPLAY(0), ACT_DISPLAY_SPECTRUM, ACT_DISPLAY_WIFI, ACT_DISPLAY(1), ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE, ACT_NONE}; DispInfo staticLayouts[5] = { { searchLayout, searchActions, searchTimeouts, "StaticSearch" }, { legacyLayout, legacyActions, legacyTimeouts, "StaticLegacy" }, { fieldLayout, fieldActions, fieldTimeouts, "StaticField1" }, { field2Layout, field2Actions, fieldTimeouts, "StaticFiel2" }, { gpsLayout, gpsActions, fieldTimeouts, "StaticGPS" } }; /////////////// Wrapper code for various display // ALLFONTS requires 30k extra flash memory... for now there is still enough space :) #define ALLFONTS 1 static const uint8_t *fl[] = { u8x8_font_chroma48medium8_r, // 0 ** default small u8x8_font_7x14_1x2_f, // 1 ** default large #ifdef ALLFONTS 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 #endif }; void U8x8Display::begin() { Serial.printf("Init SSD1306 display %d %d\n", sonde.config.oled_scl, sonde.config.oled_sda); //u8x8 = new U8X8_SSD1306_128X64_NONAME_SW_I2C(/* clock=*/ sonde.config.oled_scl, /* data=*/ sonde.config.oled_sda, /* reset=*/ sonde.config.oled_rst); // Unbuffered, basic graphics, software I2C if (_type==2) { u8x8 = new U8X8_SH1106_128X64_NONAME_HW_I2C(/* reset=*/ sonde.config.oled_rst, /* clock=*/ sonde.config.oled_scl, /* data=*/ sonde.config.oled_sda); // Unbuffered, basic graphics, software I2C } else { //__type==0 or anything else u8x8 = new U8X8_SSD1306_128X64_NONAME_HW_I2C(/* reset=*/ sonde.config.oled_rst, /* clock=*/ sonde.config.oled_scl, /* data=*/ sonde.config.oled_sda); // Unbuffered, basic graphics, software I2C } u8x8->begin(); if(sonde.config.oled_orient==3) u8x8->setFlipMode(true); fontlist = fl; nfonts = sizeof(fl)/sizeof(uint8_t *); Serial.printf("Size of font list is %d\n", nfonts); } void U8x8Display::clear() { u8x8->clear(); } // For u8x8 oled display: 0=small font, 1=large font 7x14 void U8x8Display::setFont(uint8_t fontindex) { if(fontindex>=nfonts) fontindex=0; // prevent overflow u8x8->setFont( fontlist[fontindex] ); } void U8x8Display::getDispSize(uint8_t *height, uint8_t *width, uint8_t *lineskip, uint8_t *colskip) { // TODO: maybe we should decided depending on font size (single/double?) if(height) *height = 8; if(width) *width = 16; if(lineskip) *lineskip = 1; if(colskip) *colskip = 1; } void U8x8Display::drawString(uint8_t x, uint8_t y, const char *s, int16_t width, uint16_t fg, uint16_t bg) { u8x8->drawString(x, y, s); } void U8x8Display::drawTile(uint8_t x, uint8_t y, uint8_t cnt, uint8_t *tile_ptr) { u8x8->drawTile(x, y, cnt, tile_ptr); } void U8x8Display::drawBitmap(uint16_t x1, uint16_t y1, const uint16_t* bitmap, int16_t w, int16_t h) { // not supported } void U8x8Display::drawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t color, bool fill) { // not supported (yet) } void U8x8Display::welcome() { u8x8->clear(); setFont(FONT_LARGE); drawString(8 - strlen(version_name) / 2, 1, version_name); //drawString(8 - strlen(version_id) / 2, 2, version_id); setFont(FONT_SMALL); drawString(0, 4, "RS41/92,DFM,M10"); drawString(0, 6, "by FRS2013 "); drawString(11, 6, version_id); } static String previp; void U8x8Display::drawIP(uint8_t x, uint8_t y, int16_t width, uint16_t fg, uint16_t bg) { if(!previp.equals(sonde.ipaddr)) { // ip address has changed // create tiles memset(myIP_tiles, 0, 11*8); int len = sonde.ipaddr.length(); const char *ip = sonde.ipaddr.c_str(); int pix = (len-3)*6+6; int tp = 80-pix+8; //if(sonde.isAP) memcpy(myIP_tiles+(tp<16?0:8), ap_tile, 8); memcpy(myIP_tiles+(tp<16?0:8), ap_tile, 8); for(int i=0; idrawTile(x, y, 11, myIP_tiles); } // len must be multiple of 2, size is fixed for u8x8 display void U8x8Display::drawQS(uint8_t x, uint8_t y, uint8_t len, uint8_t /*size*/, uint8_t *stat, uint16_t fg, uint16_t bg) { for(int i=0; i #define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) /////////////// char Display::buf[17]; char Display::lineBuf[Display::LINEBUFLEN]; RawDisplay *Display::rdis = NULL; //TODO: maybe merge with initFromFile later? void Display::init() { Serial.printf("disptype is %d\n",sonde.config.disptype); rdis = new U8x8Display(sonde.config.disptype); Serial.println("Display created"); rdis->begin(); delay(100); Serial.println("Display initialized"); rdis->clear(); } Display::Display() { layouts = staticLayouts; setLayout(0); } #define MAXSCREENS 20 #define DISP_ACTIONS_N 12 #define DISP_TIMEOUTS_N 3 void Display::replaceLayouts(DispInfo *newlayouts, int nnew) { if(nnew<1) return; // no new layouts => ignore // remember old layouts DispInfo *old = layouts; // assign new layouts and current layout Serial.printf("replaceLayouts: idx=%d n(new)=%d\n", layoutIdx, nLayouts); layouts = newlayouts; nLayouts = nnew; if(layoutIdx >= nLayouts) layoutIdx = 0; layout = layouts+layoutIdx; // Make it unlikely that anyone else is still using previous layouts delay(500); // and release memory not used any more if(old==staticLayouts) return; for(int i=0; ide = (DispEntry *)mem; mem += (entries+1) * sizeof(DispEntry); d->actions = (uint8_t *)mem; mem += DISP_ACTIONS_N * sizeof(uint8_t); d->actions[0] = ACT_NONE; d->timeouts = (int16_t *)mem; d->label = label; Serial.printf("allocated %d bytes (%d entries) for %p (addr=%p)\n", totalsize, entries, d, d->de); return 0; } uint16_t encodeColor(uint32_t col) { return (col>>19) << 11 | ((col>>10)&0x3F) << 5 | ((col>>3)&0x1F); } uint16_t encodeColor(char *colstr) { uint32_t col; int res=sscanf(colstr, "%" SCNx32, &col); if(res!=1) return 0xffff; return encodeColor(col); } void Display::parseDispElement(char *text, DispEntry *de) { char type = *text; if(type>='A'&&type<='Z') { type += 32; // lc de->fmt = fontlar; } else { de->fmt = fontsma; } de->fg = colfg; de->bg = colbg; switch(type) { case 'l': de->func = disp.drawLat; break; case 'o': de->func = disp.drawLon; break; case 'a': de->func = disp.drawAlt; break; case 'h': de->func = disp.drawHS; de->extra = text[1]?strdup(text+1):NULL; break; case 'v': de->func = disp.drawVS; de->extra = text[1]?strdup(text+1):NULL; break; case 'i': de->func = disp.drawID; de->extra = strdup(text+1); break; case 'q': { struct StatInfo *statinfo = (struct StatInfo *)malloc(sizeof(struct StatInfo)); // maybe enable more flexible configuration? statinfo->size=3; statinfo->len=18; if(text[1]=='4') statinfo->size = 4; de->extra = (const char *)statinfo; de->func = disp.drawQS; } break; case 't': de->func = disp.drawType; break; case 'c': de->func = disp.drawAFC; break; case 'y': de->func = disp.drawVBat; de->extra = strdup(text+1); break; case 'f': de->func = disp.drawFreq; de->extra = strdup(text+1); Serial.printf("parsing 'f' entry: extra is '%s'\n", de->extra); break; case 'n': // IP address / small always uses tiny font on TFT for backward compatibility // Large font can be used arbitrarily if(de->fmt==fontsma) de->fmt=0; de->func = disp.drawIP; break; case 's': de->func = disp.drawSite; de->extra = strdup(text+1); break; case 'k': de->func = disp.drawKilltimer; de->extra = strdup(text+1); break; case 'g': de->func = disp.drawGPS; de->extra = strdup(text+1); Serial.printf("parsing 'g' entry: extra is '%s'\n", de->extra); break; case 'r': de->func = disp.drawRSSI; break; case 'x': de->func = disp.drawText; de->extra = strdup(text+1); break; case 'b': de->func = disp.drawBatt; de->extra = strdup(text+1); break; default: Serial.printf("Unknown element: %c\n", type); break; } } static uint8_t ACTION(char c) { switch(c) { case 'D': return ACT_DISPLAY_DEFAULT; case 'F': return ACT_DISPLAY_SPECTRUM; case 'W': return ACT_DISPLAY_WIFI; case '+': return ACT_NEXTSONDE; case '#': return ACT_NONE; case '>': return ACT_DISPLAY_NEXT; default: if(c>='0'&&c<='9') return ACT_DISPLAY(c-'0'); // Hack, will change later to better syntax if(c>='a'&&c<='z') return ACT_ADDFREQ(c-'a'+2); } return ACT_NONE; } int Display::countEntries(File f) { int pos = f.position(); int n = 0; while(1) { //String line = readLine(f); //f.readStringUntil('\n'); //line.trim(); //const char *c=line.c_str(); readLine(f, lineBuf, LINEBUFLEN); const char *c = trim(lineBuf); if(*c=='#') continue; if(*c>='0'&&*c<='9') n++; if(strchr(c,'=')) continue; break; } f.seek(pos, SeekSet); Serial.printf("Counted %d entries\n", n); return n; } void Display::initFromFile() { File d = SPIFFS.open("/screens.txt", "r"); if(!d) return; DispInfo *newlayouts = (DispInfo *)malloc(MAXSCREENS * sizeof(DispInfo)); if(!newlayouts) { Serial.println("Init from file: FAILED, not updating layouts"); return; } memset(newlayouts, 0, MAXSCREENS * sizeof(DispInfo)); // default color colfg = 0xffff; // white; only used for ILI9225 colbg = 0; // black; only used for ILI9225 int idx = -1; int what = -1; int entrysize; Serial.printf("Reading from /screens.txt. available=%d\n",d.available()); while(d.available()) { //Serial.printf("Unused stack: %d\n", uxTaskGetStackHighWaterMark(0)); const char *ptr; readLine(d, lineBuf, LINEBUFLEN); const char *s = trim(lineBuf); // String line = readLine(d); // line.trim(); // const char *s = line.c_str(); Serial.printf("Line: '%s'\n", s); if(*s == '#') continue; // ignore comments switch(what) { case -1: // wait for start of screen (@) { if(*s != '@') { Serial.printf("Illegal start of screen: %s\n", s); continue; } char *label = strdup(s+1); entrysize = countEntries(d); Serial.printf("Reading entry with %d elements\n", entrysize); idx++; int res = allocDispInfo(entrysize, &newlayouts[idx], label); Serial.printf("allocDispInfo: idx %d: label is %p - %s\n",idx,newlayouts[idx].label, newlayouts[idx].label); if(res<0) { Serial.println("Error allocating memory for disp info"); continue; } what = 0; } break; default: // parse content... (additional data or line `what`) if(strncmp(s,"timer=",6)==0) { // timer values char t1[10],t2[10],t3[10]; sscanf(s+6, "%5[0-9a-zA-Z-] , %5[0-9a-zA-Z-] , %5[0-9a-zA-Z-]", t1, t2, t3); Serial.printf("timers are %s, %s, %s\n", t1, t2, t3); newlayouts[idx].timeouts[0] = (*t1=='n'||*t1=='N')?sonde.config.norx_timeout:atoi(t1); newlayouts[idx].timeouts[1] = (*t2=='n'||*t2=='N')?sonde.config.norx_timeout:atoi(t2); newlayouts[idx].timeouts[2] = (*t3=='n'||*t3=='N')?sonde.config.norx_timeout:atoi(t3); // Code later assumes milliseconds, but config.txt and screens.txt use values in seconds if(newlayouts[idx].timeouts[0]>0) newlayouts[idx].timeouts[0]*=1000; if(newlayouts[idx].timeouts[1]>0) newlayouts[idx].timeouts[1]*=1000; if(newlayouts[idx].timeouts[2]>0) newlayouts[idx].timeouts[2]*=1000; //sscanf(s+6, "%hd,%hd,%hd", newlayouts[idx].timeouts, newlayouts[idx].timeouts+1, newlayouts[idx].timeouts+2); Serial.printf("timer values: %d, %d, %d\n", newlayouts[idx].timeouts[0], newlayouts[idx].timeouts[1], newlayouts[idx].timeouts[2]); } else if(strncmp(s, "key1action=",11)==0) { // key 1 actions char c1,c2,c3,c4; sscanf(s+11, "%c,%c,%c,%c", &c1, &c2, &c3, &c4); newlayouts[idx].actions[1] = ACTION(c1); newlayouts[idx].actions[2] = ACTION(c2); newlayouts[idx].actions[3] = ACTION(c3); newlayouts[idx].actions[4] = ACTION(c4); } else if(strncmp(s, "key2action=",11)==0) { // key 2 actions char c1,c2,c3,c4; sscanf(s+11, "%c,%c,%c,%c", &c1, &c2, &c3, &c4); newlayouts[idx].actions[5] = ACTION(c1); newlayouts[idx].actions[6] = ACTION(c2); newlayouts[idx].actions[7] = ACTION(c3); newlayouts[idx].actions[8] = ACTION(c4); Serial.printf("parsing key2action: %c %c %c %c\n", c1, c2, c3, c4); } else if(strncmp(s, "timeaction=",11)==0) { // timer actions char c1,c2,c3; sscanf(s+11, "%c,%c,%c", &c1, &c2, &c3); newlayouts[idx].actions[9] = ACTION(c1); newlayouts[idx].actions[10] = ACTION(c2); newlayouts[idx].actions[11] = ACTION(c3); } else if(strncmp(s, "fonts=",6)==0) { // change font sscanf(s+6, "%d,%d", &fontsma, &fontlar); } else if(strncmp(s, "scale=",6)==0) { // change line->pixel scaling for ILI9225 display sscanf(s+6, "%d,%d", &yscale, &xscale); } else if(strncmp(s, "color=",6)==0) { // int res; uint32_t fg,bg; res=sscanf(s+6, "%" SCNx32 ",%" SCNx32, &fg, &bg); colfg = (fg>>19) << 11 | ((fg>>10)&0x3F) << 5 | ((fg>>3)&0x1F); if(res==2) { colbg = (bg>>19) << 11 | ((bg>>10)&0x3F) << 5 | ((bg>>3)&0x1F); } } else if( (ptr=strchr(s, '=')) ) { // one line with some data... float x,y,w; int n; 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; } newlayouts[idx].de[what].x = x; newlayouts[idx].de[what].y = y; newlayouts[idx].de[what].width = n>2 ? w : WIDTH_AUTO; parseDispElement(text, newlayouts[idx].de+what); Serial.printf("entry at %d,%d width=%d font %d, color=%x,%x\n", (int)x, (int)y, newlayouts[idx].de[what].width, newlayouts[idx].de[what].fmt, newlayouts[idx].de[what].fg, newlayouts[idx].de[what].bg); if(newlayouts[idx].de[what].func == disp.drawGPS) { newlayouts[idx].usegps = GPSUSE_BASE|GPSUSE_DIST|GPSUSE_BEARING; // just all for now } what++; newlayouts[idx].de[what].func = NULL; } else { for(int i=0; i<12; i++) { Serial.printf("action %d: %d\n", i, (int)newlayouts[idx].actions[i]); } what=-1; } break; } } replaceLayouts(newlayouts, idx+1); } void Display::circ(int x=1, int y=1) { // draw circle uint8_t tiles[48] = { 0, 0, 0, 0, 0, 0, 128, 64, 32, 16, 8, 4, 4, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 4, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0 }; disp.rdis->drawTile(x, y, 6, tiles); uint8_t tiles2[48] = { 0, 0, 0, 224, 28, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 28, 224, 0, 0, 0 }; disp.rdis->drawTile(x, y+1, 6, tiles2); uint8_t tiles3[48] = { 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0 }; disp.rdis->drawTile(x, y+2, 6, tiles3); uint8_t tiles4[48] = { 0, 0, 0, 31, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 31, 0, 0, 0}; disp.rdis->drawTile(x, y+3, 6, tiles4); uint8_t tiles5[48] = { 0, 0, 0, 0, 1, 6, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 64, 32, 16, 8, 6, 1, 0, 0, 0, 0 }; disp.rdis->drawTile(x, y+4, 6, tiles5); uint8_t tiles6[48] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; disp.rdis->drawTile(x, y+5, 6, tiles6); uint8_t centres[16] = { 0, 0, 0, 0, 0, 0, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0 }; disp.rdis->drawTile(x+2, y+2, 2, centres); uint8_t centres2[16] = { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; disp.rdis->drawTile(x+2, y+3, 2, centres2); //N uint8_t N[16] = {0,0,0,0,0, 63, 2, 4, 8, 16, 63, 0,0,0,0,0}; disp.rdis->drawTile(x+2, y-1, 2, N); //S uint8_t S[16] = {0, 0, 0, 0, 0, 38, 73, 73, 73, 73, 50, 0,0,0,0,0}; disp.rdis->drawTile(x+2, y+6, 2, S); //O uint8_t O[8] = {0,224, 48, 16, 16, 48, 224, 0}; disp.rdis->drawTile(x-1, y+2, 1, O); uint8_t O2[8] = {0,7, 12, 8, 8, 12, 7, 0}; disp.rdis->drawTile(x-1, y+3, 1, O2); //E uint8_t E[8] = {0,240, 144, 144, 16, 16, 16, 0}; disp.rdis->drawTile(x+6, y+2, 1, E); uint8_t E2[8] = {0,15, 9, 9, 8, 8, 8, 0}; disp.rdis->drawTile(x+6, y+3, 1, E2); } void Display::setLayout(int newidx) { Serial.printf("setLayout: %d (max is %d)\n", newidx, nLayouts); if(newidx>=nLayouts) newidx = 0; layout = &layouts[newidx]; layoutIdx = newidx; } void Display::drawString(DispEntry *de, const char *str) { rdis->drawString(de->x, de->y, str, de->width, de->fg, de->bg); } void Display::drawLat(DispEntry *de) { rdis->setFont(de->fmt); if(!sonde.si()->validPos) { drawString(de,"00.0000"); return; } snprintf(buf, 16, "%2.5f", sonde.si()->lat); drawString(de,buf); } void Display::drawLon(DispEntry *de) { rdis->setFont(de->fmt); if(!sonde.si()->validPos) { drawString(de,"00.0000"); return; } snprintf(buf, 16, "%2.5f", sonde.si()->lon); drawString(de,buf); } void Display::drawAlt(DispEntry *de) { rdis->setFont(de->fmt); if(!sonde.si()->validPos) { drawString(de,"00000m"); return; } float alt = sonde.si()->alt; //testing only.... alt += 30000-454; snprintf(buf, 16, alt>=1000?" %5.0fm":" %3.1fm", alt); drawString(de,buf+strlen(buf)-6); } void Display::drawHS(DispEntry *de) { rdis->setFont(de->fmt); if(!sonde.si()->validPos) { drawString(de,"00.00"); return; } boolean is_ms = (de->extra && de->extra[0]=='m')?true:false; // m/s or km/h float hs = sonde.si()->hs; if(is_ms) hs = hs / 3.6; boolean has_extra = (de->extra && de->extra[1]!=0)? true: false; snprintf(buf, 16, hs>99?" %3.0f":" %2.1f", hs); if(has_extra) { strcat(buf, de->extra+1); } drawString(de,buf+strlen(buf)-4- (has_extra?strlen(de->extra+1):0) ); if(!has_extra) rdis->drawTile(de->x+4,de->y,2,is_ms?ms_tiles:kmh_tiles); } void Display::drawVS(DispEntry *de) { rdis->setFont(de->fmt); if(!sonde.si()->validPos) { drawString(de," 00.00"); return; } snprintf(buf, 16, " %+2.1f", sonde.si()->vs); Serial.printf("drawVS: extra is %s width=%d\n", de->extra?de->extra:"", de->width); if(de->extra) { strcat(buf, de->extra); } drawString(de, buf+strlen(buf)-5- (de->extra?strlen(de->extra):0) ); if(!de->extra) rdis->drawTile(de->x+5,de->y,2,ms_tiles); } void Display::drawID(DispEntry *de) { rdis->setFont(de->fmt); if(!sonde.si()->validID) { drawString(de, "nnnnnnnn "); return; } // TODO: handle DFM6 IDs if(!de->extra || de->extra[0]=='s') { // real serial number, as printed on sonde drawString(de, sonde.si()->ser); } else if (de->extra[0]=='a') { // autorx sonde number ("DF9" and last 6 digits of real serial number) if(sonde.si()->type == STYPE_DFM09) { int n = strlen(sonde.si()->ser) - 6; if(n<0) n=0; memcpy(buf, "DF9", 3); memcpy(buf+3, sonde.si()->ser+n, 6); drawString(de, buf); } else { drawString(de, sonde.si()->ser); } } else { // dxlAPRS sonde number (DF6 (why??) and 5 last digits of serial number as hex number drawString(de, sonde.si()->id); } } void Display::drawRSSI(DispEntry *de) { rdis->setFont(de->fmt); Serial.printf("\ndBSmetre %d\n",sonde.config.dbsmetre); if (sonde.config.dbsmetre==0) { if(sonde.config.disptype!=1) { snprintf(buf, 16, "-%d ", sonde.si()->rssi/2); int len=strlen(buf)-3; Serial.printf("drawRSSI: %d %d %d (%d)[%d]\n", de->y, de->x, sonde.si()->rssi/2, sonde.currentSonde, len); buf[5]=0; drawString(de,buf); rdis->drawTile(de->x+len, de->y, 1, (sonde.si()->rssi&1)?halfdb_tile1:empty_tile1); rdis->drawTile(de->x+len, de->y+1, 1, (sonde.si()->rssi&1)?halfdb_tile2:empty_tile2); } else { // special for 2" display snprintf(buf, 16, "-%d.%c ", sonde.si()->rssi/2, (sonde.si()->rssi&1)?'5':'0'); drawString(de,buf); } } else { int dbmetre=sonde.si()->rssi/2; Serial.printf("\nSmetre %d\n",sonde.si()->rssi/2); //Ici tableau Smetre //S1 <-121dB //S2 <-115dB //S3 <-109dB //S4 <-103dB //S5 <-97dB //S6 <-91dB //S7 <-85dB //S8 <-79dB //S9 <-73dB //S9+10 <-63dB //S9+20 <-53dB //S9+30 <-43db //S9+40 <- 33dB //S9+50dB <-23dB //S9+60 <-13dB //S9+70 <-0dB if (dbmetre>138) drawString(de,"S0"); if ((dbmetre<137)&&(dbmetre>122)) drawString(de,"S1 "); if ((dbmetre<121)&&(dbmetre>116)) drawString(de,"S2 "); if ((dbmetre<115)&&(dbmetre>108)) drawString(de,"S3 "); if ((dbmetre<109)&&(dbmetre>104)) drawString(de,"S4 "); if ((dbmetre<103)&&(dbmetre>98)) drawString(de,"S5 "); if ((dbmetre<97)&&(dbmetre>92)) drawString(de,"S6 "); if ((dbmetre<91)&&(dbmetre>86)) drawString(de,"S7 "); if ((dbmetre<85)&&(dbmetre>80)) drawString(de,"S8 "); if ((dbmetre<79)&&(dbmetre>74)) drawString(de,"S9 "); if ((dbmetre<73)&&(dbmetre>64)) drawString(de,"S9+10"); if ((dbmetre<63)&&(dbmetre>54)) drawString(de,"S9+20"); if ((dbmetre<53)&&(dbmetre>44)) drawString(de,"S9+30"); if ((dbmetre<43)&&(dbmetre>34)) drawString(de,"S9+40"); if ((dbmetre<33)&&(dbmetre>24)) drawString(de,"S9+50"); if ((dbmetre<23)&&(dbmetre>14)) drawString(de,"S9+60"); if ((dbmetre<13)&&(dbmetre>0)) drawString(de,"S9+70"); } } void Display::drawQS(DispEntry *de) { uint8_t *stat = sonde.si()->rxStat; struct StatInfo *statinfo = (struct StatInfo *)de->extra; rdis->drawQS(de->x, de->y, statinfo->len, statinfo->size, stat, de->fg, de->bg); } void Display::drawType(DispEntry *de) { rdis->setFont(de->fmt); drawString(de, sondeTypeStr[sonde.si()->type]); } void Display::drawFreq(DispEntry *de) { rdis->setFont(de->fmt); snprintf(buf, 16, "%3.3f%s", sonde.si()->freq, de->extra?de->extra:""); drawString(de, buf); } void Display::drawVBat(DispEntry *de) { rdis->setFont(de->fmt); snprintf(buf, 16, "%3.0f%s", sonde.si()->vbat, "%"); 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); } void Display::drawIP(DispEntry *de) { rdis->setFont(de->fmt); rdis->drawIP(de->x, de->y, de->width, de->fg, de->bg); } void Display::drawSite(DispEntry *de) { rdis->setFont(de->fmt); switch(de->extra[0]) { case '#': // currentSonde is index in array starting with 0; // but we draw "1" for the first entrie and so on... snprintf(buf, 3, "%2d", sonde.currentSonde+1); buf[2]=0; break; case 't': snprintf(buf, 3, "%d", sonde.config.maxsonde); buf[2]=0; break; case 'a': { uint8_t active = 0; for(int i=0; ilaunchsite); return; } if(de->extra[0]) strcat(buf, de->extra+1); drawString(de, buf); } void Display::drawTelemetry(DispEntry *de) { } void Display::drawKilltimer(DispEntry *de) { rdis->setFont(de->fmt); uint16_t value; switch(de->extra[0]) { case 'l': value = sonde.si()->launchKT; break; case 'b': value = sonde.si()->burstKT; break; case 'c': value = sonde.si()->countKT; break; } // format: 4=h:mm; 6=h:mm:ss; s=sssss uint16_t h=value/3600; uint16_t m=(value-h*3600)/60; uint16_t s=value%60; switch(de->extra[1]) { case '4': if(value!=0xffff) snprintf(buf, 5, "%d:%02d", h, m); else strcpy(buf, " "); break; case '6': if(value!=0xffff) snprintf(buf, 7, "%d:%02d:%02d", h, m, s); else strcpy(buf, " "); break; default: if(value!=0xffff) snprintf(buf, 6, "%5d", value); else strcpy(buf, " "); break; } if(de->extra[1]) strcat(buf, de->extra+2); drawString(de, buf); } #define EARTH_RADIUS (6371000.0F) #define PE (111.6F) // la terre a un rayon de r = 6371km. Soit un périmètre de P = 2*PI*r = 40030.17km. Soit, pour un degré a = P/360 = 111.16km #ifndef PI #define PI (3.1415926535897932384626433832795) #endif // defined by Arduino.h #define radians(x) ( (x)*180.0F/PI ) extern int lastCourse; // from RadioSonde_FSK.ino void Display::calcGPS() { // base data if (sonde.config.gpsOn==0){ gpsValid = true; gpsLon = atof(sonde.config.gps_lon); gpsLat = atof(sonde.config.gps_lat); gpsAlt = sonde.config.gps_alt; static int tmpc=0; tmpc = (tmpc+5)%360; gpsCourse = tmpc; } else { gpsValid = nmea.isValid(); gpsLon = nmea.getLongitude()*0.000001; gpsLat = nmea.getLatitude()*0.000001; long alt; nmea.getAltitude(alt); gpsAlt=(int)(alt/1000); gpsCourse = (int)(nmea.getCourse()/1000); gpsCourseOld = false; if(gpsCourse==0) { // either north or not new if(lastCourse!=0) // use old value... { gpsCourseOld = true; gpsCourse = lastCourse; } } } // distance if( gpsValid ) { float lat1 = gpsLat; float lat2 = sonde.si()->lat; float lon1 = gpsLon; float lon2 = sonde.si()->lon; float x = abs(lon2-lon1); Serial.printf("\nx= %f",x); float y = abs(lat2-lat1); Serial.printf("\ny= %f",y); float d = sqrt(x*x+y*y)*PE*1000; Serial.printf("\ndist= %5.1fm",d); gpsDist = d; } else { gpsDist = -1; } // bearing if( gpsValid ) { float lat1 = (gpsLat); float lat2 = (sonde.si()->lat); float lon1 = (gpsLon); float lon2 = (sonde.si()->lon); float y = abs(sin(lon2-lon1)*cos(lat2)); float x = abs(cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(lon2-lon1)); float dir = atan2(y, x)/PI*180; if(dir<0) dir+=360; gpsDir = (int)dir; gpsBear = gpsDir - gpsCourse; if(gpsBear < 0) gpsBear += 360; if(gpsBear >= 360) gpsBear -= 360; } else { gpsDir = -1; gpsBear = -1; } Serial.printf("\nGPS data: valid%d GPS at %f,%f (alt=%d,cog=%d); sonde at dist=%d, dir=%d rel.bear=%d\n",gpsValid?1:0, gpsLat, gpsLon, gpsAlt, gpsCourse, gpsDist, gpsDir, gpsBear); //VBat % float vbat,r,Dmax, Dact; float ADC_PIN = 35; float vbatmax=atof(sonde.config.vbatmax), vbatmin=atof(sonde.config.vbatmin); r = analogRead(ADC_PIN); vbat = r * (3.30 / 4094.00); //convert the value to a true voltage. Serial.printf("\nVbatMAX= %1.2fV",vbatmax); Serial.printf("\nVbatMIN= %1.2fV",vbatmin); Dmax = vbatmax - vbatmin; Dact = vbat - vbatmin; vbat = (Dact / Dmax)*100; Serial.printf("\nVbat:%3.0f\%\n",vbat); sonde.si()->vbat=vbat; } void Display::drawGPS(DispEntry *de) { //if(sonde.config.gps_rxd<0) return; rdis->setFont(de->fmt); switch(de->extra[0]) { case 'W': { // show if GPS location is valid uint8_t *tile = disp.gpsValid?gps_tile:nogps_tile; rdis->drawTile(de->x, de->y, 1, tile); } break; case 'O': // GPS long snprintf(buf, 16, "%2.5f", disp.gpsLon); drawString(de,buf); break; case 'A': // GPS lat snprintf(buf, 16, "%2.5f", disp.gpsLat); drawString(de,buf); break; case 'H': // GPS alt snprintf(buf, 16, "%4dm", disp.gpsAlt); drawString(de,buf); break; case 'C': // GPS Course over ground snprintf(buf, 4, "%3d", disp.gpsCourse); drawString(de, buf); break; case 'D': { rdis->setFont(de->fmt); // distance // equirectangular approximation is good enough if( (sonde.si()->validPos&0x03)!=0x03 ) { snprintf(buf, 16, "no pos "); if(de->extra && *de->extra=='5') buf[5]=0; } else if((!disp.gpsValid)||(disp.gpsLat!=0)&&(disp.gpsLon!=0)) { snprintf(buf, 16, "no gps "); if(de->extra && *de->extra=='5') buf[5]=0; } else { if(de->extra && *de->extra=='5') { // 5-character version: ****m / ***km / **e6m if(disp.gpsDist>999999) snprintf(buf, 16, "%3.0de6m ", (disp.gpsDist/1000000)); if(disp.gpsDist>999) snprintf(buf, 16, "%3.0dkm ", (disp.gpsDist/1000)); else snprintf(buf, 16, "%5.0dm ", (disp.gpsDist)); } else { // 6-character version: *****m / ****km) if(disp.gpsDist>999) snprintf(buf, 16, "%3.0dkm ", (disp.gpsDist/1000)); else snprintf(buf, 16, "%3.0dm ", (disp.gpsDist)); } } Serial.printf("\nDist= %dm",disp.gpsDist); drawString(de, buf); } break; case 'I': // dIrection rdis->setFont(de->fmt); if (!sonde.si()->validPos) { rdis->setFont(de->fmt); drawString(de, "---"); break; } snprintf(buf, 16, "%3d", disp.gpsDir); buf[3]=0; drawString(de, buf); //if(de->extra[1]==(char)176) rdis->drawTile(de->x+3, de->y, 1, deg_tile); break; case 'B': // relative bearing rdis->setFont(de->fmt); if (!sonde.si()->validPos) { drawString(de, "---"); break; } snprintf(buf, 16, "%3d", disp.gpsBear); buf[3]=0; drawString(de, buf); if(de->extra[1]==(char)176) rdis->drawTile(de->x+3, de->y, 1, deg_tile); break; case '0': // diagram { Serial.printf("\nDiagram\n"); int x=1, y=1; disp.circ(x,y); int dirdegres=disp.gpsDist; if (dirdegres==360) dirdegres=0; //reset aiguille uint8_t resetaiguilles[16] = {0, 0, 0, 0, 0, 0, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0}; disp.rdis->drawTile(x+2, y+2, 2, resetaiguilles); uint8_t resetaiguilles2[16] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}; disp.rdis->drawTile(x+2, y+3, 2, resetaiguilles2); //Pour 0 à 90° if (dirdegres<91) { if (dirdegres<7) { uint8_t trait[8]={255,0,0,0,0,0,0,0}; //0° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>6)&&(dirdegres<13)) { uint8_t trait[8]={240,15,0,0,0,0,0,0}; //6° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>12)&&(dirdegres<19)) { uint8_t trait[8]={224,24,7,0,0,0,0,0}; //12° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>18)&&(dirdegres<25)) { uint8_t trait[8]={128,112,14,1,0,0,0,0}; //18° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>24)&&(dirdegres<31)) { uint8_t trait[8]={128,96,24,6,1,0,0,0}; //24° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>30)&&(dirdegres<37)) { uint8_t trait[8]={192,48,24,12,6,3,0,0}; //30° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>36)&&(dirdegres<43)) { uint8_t trait[8]={128,64,32,24,12,6,1,0}; //36° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>42)&&(dirdegres<49)) { uint8_t trait[8]={128,64,32,16,8,4,2,1}; //42° à 49° dont 45° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>48)&&(dirdegres<55)) { uint8_t trait[8]={128,96,48,24,12,8,4,2}; //48° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>54)&&(dirdegres<61)) { uint8_t trait[8]={128,64,32,32,16,16,8,4}; //54° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>60)&&(dirdegres<67)) { uint8_t trait[8]={128,64,64,32,32,16,16,8}; //60° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>66)&&(dirdegres<73)) { uint8_t trait[8]={128,64,64,64,32,32,32,16}; //66° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>72)&&(dirdegres<79)) { uint8_t trait[8]={128,128,64,64,64,64,32,32}; //72° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>78)&&(dirdegres<85)) { uint8_t trait[8]={128,128,128,128,64,64,64,64}; //78° disp.rdis->drawTile(x+3, y+2, 1, trait); } if ((dirdegres>84)&&(dirdegres<91)) { uint8_t trait[8]={128,128,128,128,128,128,128,128}; //84° disp.rdis->drawTile(x+3, y+2, 1, trait); } } //Pour 90° à 180° if ((dirdegres>90)&&(dirdegres<181)) { if (dirdegres<97) { uint8_t trait[8]={1,1,1,1,1,1,1,1}; //90° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>96)&&(dirdegres<103)) { uint8_t trait[8]={1,1,1,1,2,2,2,2}; //96° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>102)&&(dirdegres<109)) { uint8_t trait[8]={1,2,2,2,2,2,2,4}; //102° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>108)&&(dirdegres<115)) { uint8_t trait[8]={1,2,2,4,4,4,8,8}; //108° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>114)&&(dirdegres<121)) { uint8_t trait[8]={1,2,2,4,4,8,8,16}; //114° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>120)&&(dirdegres<127)) { uint8_t trait[8]={1,2,4,12,24,32,32,64}; //120° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>126)&&(dirdegres<133)) { uint8_t trait[8]={1,1,2,12,48,64,128,128}; //126° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>132)&&(dirdegres<139)) { uint8_t trait[8]={1,2,4,8,16,32,64,128}; //132° à 138° dont 135° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>138)&&(dirdegres<145)) { uint8_t trait[8]={1,6,12,24,48,96,128,0}; //144° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>144)&&(dirdegres<151)) { uint8_t trait[8]={1,6,24,96,128,0,0,0}; //150° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>150)&&(dirdegres<163)) { uint8_t trait[8]={1,14,112,128,0,0,0,0}; //162° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>162)&&(dirdegres<169)) { uint8_t trait[8]={3,60,192,0,0,0,0,0}; //168° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>168)&&(dirdegres<175)) { uint8_t trait[8]={15,240,0,0,0,0,0,0}; //174° disp.rdis->drawTile(x+3, y+3, 1, trait); } if ((dirdegres>174)&&(dirdegres<181)) { uint8_t trait[8]={255,0,0,0,0,0,0,0}; //180° disp.rdis->drawTile(x+3, y+3, 1, trait); } } //Pour 180 à 270 if ((dirdegres>180)&&(dirdegres<271)) { if (dirdegres<187) { uint8_t trait[8]={0,0,0,0,0,0,0,255}; //180° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>186)&&(dirdegres<193)) { uint8_t trait[8]={0,0,0,0,0,0,240,15}; //186° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>192)&&(dirdegres<199)) { uint8_t trait[8]={0,0,0,0,0,192,60,3}; //192° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>198)&&(dirdegres<205)) { uint8_t trait[8]={0,0,0,0,128,112,14,1}; //198° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>204)&&(dirdegres<211)) { uint8_t trait[8]={0,0,0,128,96,24,6,1}; //204° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>210)&&(dirdegres<217)) { uint8_t trait[8]={0,0,192,32,48,12,4,3}; //210° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>216)&&(dirdegres<223)) { uint8_t trait[8]={0,128,96,48,24,12,6,1}; //216° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>222)&&(dirdegres<229)) { uint8_t trait[8]={128,64,32,16,8,4,2,1}; //222° à 229° dont 225° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>228)&&(dirdegres<235)) { uint8_t trait[8]={128,128,96,48,24,12,6,1}; //228° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>234)&&(dirdegres<241)) { uint8_t trait[8]={64,32,32,24,12,8,2,1}; //234° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>240)&&(dirdegres<247)) { uint8_t trait[8]={32,32,16,8,4,2,1}; //240° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>246)&&(dirdegres<253)) { uint8_t trait[8]={16,8,8,4,4,2,2,1}; //246° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>252)&&(dirdegres<259)) { uint8_t trait[8]={8,4,4,4,2,2,2,1}; //252° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>258)&&(dirdegres<265)) { uint8_t trait[8]={4,4,2,2,2,2,1,1}; //258° disp.rdis->drawTile(x+2, y+3, 1, trait); } if ((dirdegres>264)&&(dirdegres<271)) { uint8_t trait[8]={1,1,1,1,1,1,1,1}; //264° disp.rdis->drawTile(x+2, y+3, 1, trait); } } // 270° à 360° if ((dirdegres>270)&&(dirdegres<360)) { if (dirdegres<277) { uint8_t trait[8]={128,128,128,128,128,128,128,128}; //270° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>276)&&(dirdegres<283)) { uint8_t trait[8]={64,64,64,64,128,128,128,128}; //276° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>282)&&(dirdegres<289)) { uint8_t trait[8]={32,32,64,64,64,64,128,128}; //282° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>288)&&(dirdegres<295)) { uint8_t trait[8]={16,32,32,32,64,64,64,128}; //288° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>294)&&(dirdegres<301)) { uint8_t trait[8]={8,16,16,32,32,64,64,128}; //294° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>300)&&(dirdegres<307)) { uint8_t trait[8]={4,4,8,16,32,64,64,128}; //300° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>306)&&(dirdegres<313)) { uint8_t trait[8]={2,4,4,24,48,32,64,128}; //306° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>312)&&(dirdegres<319)) { uint8_t trait[8]={1,2,4,8,16,32,64,128}; //312° à 319° dont 315° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>318)&&(dirdegres<325)) { uint8_t trait[8]={0,1,6,8,24,32,96,128}; //318° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>324)&&(dirdegres<331)) { uint8_t trait[8]={0,1,2,6,24,32,64,128}; //324° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>330)&&(dirdegres<337)) { uint8_t trait[8]={0,0,1,6,8,16,96,128}; //330° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>336)&&(dirdegres<343)) { uint8_t trait[8]={0,0,0,1,6,24,96,128}; //336° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>342)&&(dirdegres<349)) { uint8_t trait[8]={0,0,0,0,1,6,112,128}; //342° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>348)&&(dirdegres<355)) { uint8_t trait[8]={0,0,0,0,0,1,126,128}; //348° disp.rdis->drawTile(x+2, y+2, 1, trait); } if ((dirdegres>354)&&(dirdegres<360)) { uint8_t trait[8]={0,0,0,0,0,0,0,255}; //354° disp.rdis->drawTile(x+2, y+2, 1, trait); } } } break; case 'E': // elevation float el,ele; el=acos(cos(PI/180*(disp.gpsLon-disp.gpsDir))*cos(PI/180*(abs(disp.gpsLat)))); ele=180*atan((cos(el)-(EARTH_RADIUS/(EARTH_RADIUS+disp.gpsAlt)))/sin(el))/PI; ele=abs(ele); Serial.printf("\nElevation:%3.0f\n",ele); snprintf(buf, 16, "%3.0f", ele); buf[3]=0; drawString(de, buf); rdis->drawTile(de->x+3, de->y, 1, deg_tile); break; } } void Display::drawBatt(DispEntry *de) { float val, pwr, Dmax, Dact, r, GPIO_PIN=4,ADC_PIN = 35; float vbatmax=atof(sonde.config.vbatmax), vbatmin=atof(sonde.config.vbatmin); char buf[30]; //if(!axp192_found) return; xSemaphoreTake( axpSemaphore, portMAX_DELAY ); switch(de->extra[0]) { case 'V': r = analogRead(ADC_PIN); val = r * (3.30 / 4094.00); //convert the value to a true voltage. snprintf(buf, 30, "%.2f%s", val, de->extra+1); break; case 'C': r = analogRead(GPIO_PIN); val = r * (3.30 / 4094.00); //convert the value to a true voltage. snprintf(buf, 30, "%.2f%s", val, de->extra+1); break; case 'P': r = analogRead(ADC_PIN); val = r * (3.30 / 4094.00); //convert the value to a true voltage. Dmax = vbatmax - vbatmin; Dact = val - vbatmin; val = (Dact / Dmax)*100; val = (vbatmax * val) /100; pwr = (val * 0.40)*1000; snprintf(buf, 30, "%4.0f%s", pwr, de->extra+1); break; default: *buf=0; } xSemaphoreGive( axpSemaphore ); rdis->setFont(de->fmt); drawString(de, buf); } void Display::drawText(DispEntry *de) { rdis->setFont(de->fmt); drawString(de, de->extra); } extern void buzzerLed(int temps); void Display::updateDisplayPos() { for(DispEntry *di=layout->de; di->func != NULL; di++) { if(di->func != disp.drawLat && di->func != disp.drawLon) continue; di->func(di); } } void Display::updateDisplayPos2() { for(DispEntry *di=layout->de; di->func != NULL; di++) { if(di->func != disp.drawAlt && di->func != disp.drawHS && di->func != disp.drawVS) continue; di->func(di); } } void Display::updateDisplayVBat() { for(DispEntry *di=layout->de; di->func != NULL; di++) { if(di->func != disp.drawVBat) continue; di->func(di); } } void Display::updateDisplayVBatt() { for(DispEntry *di=layout->de; di->func != NULL; di++) { if(di->func != disp.drawBatt) continue; di->func(di); } } void Display::updateDisplayID() { for(DispEntry *di=layout->de; di->func != NULL; di++) { if(di->func != disp.drawID) continue; di->func(di); } } void Display::updateDisplayRSSI() { for(DispEntry *di=layout->de; di->func != NULL; di++) { if(di->func != disp.drawRSSI) continue; di->func(di); } } void Display::updateStat() { for(DispEntry *di=layout->de; di->func != NULL; di++) { if(di->func != disp.drawQS) continue; di->func(di); } } void Display::updateDisplayRXConfig() { for(DispEntry *di=layout->de; di->func != NULL; di++) { if(di->func != disp.drawQS && di->func != disp.drawAFC) continue; di->func(di); } } void Display::updateDisplayIP() { for(DispEntry *di=layout->de; di->func != NULL; di++) { if(di->func != disp.drawIP) continue; Serial.printf("updateDisplayIP: %d %d\n",di->x, di->y); di->func(di); } } void Display::updateDisplay() { calcGPS(); for(DispEntry *di=layout->de; di->func != NULL; di++) { di->func(di); } } Display disp = Display();