/********************************* BATTERYTEST ******************************** * * Jan 2018 24. - Modificeret til at benytte AD8307 som Detektor * -------------------------------------------------------------------- * Modificeret af OZ6YM, Palle A. Andersen * OZ6YM-release 1.18.6 - Måned, Årstal, Version * ****************************************************************************** */ String MSG = "Software Rel: 1.18.6 - by"; // #define DEBUG // Comment out if not debugging code // Baggrundsfarven sættes i linje 2151 /*****************************************************************************/ /* * The graphics package that I modifier is taken from this package: * Youtube video at: https://www.youtube.com/watch?v=U5hOU-xxQgk * * It requires and Arduino Mega Pro Mini and an mcufriend TFT display * * Adafruit libraries can DOAWNMLOADES here: * https://github.com/adafruit/Adafruit_HX8357_Library/archive/master.zip * https://github.com/adafruit/Adafruit-GFX-Library/archive/master.zip * * optional touch screen libraries * https://github.com/adafruit/Touch-Screen-Library/archive/master.zip * */ /*****************************************************************************/ // All the mcufriend.com UNO shields have the same pinout. // i.e. control pins A0-A4. Data D2-D9. microSD D10-D13. // Touchscreens are normally A1, A2, D7, D6 but the order varies // // This demo should work with most Adafruit TFT libraries // If you are not using a shield, use a full Adafruit constructor() // e.g. Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET); /*****************************************************************************/ // Non-standard libraries may be found at the websites specified: /*****************************************************************************/ #include // https://github.com/F4GOJ/AD9850SPI #include // https://github.com/adafruit/Adafruit-GFX-Library #include // https://github.com/prenticedavid/MCUFRIEND_kbv #include // https://github.com/brianlow/Rotary #include // Standard with IDE #include // Standard with IDE #include // Standard with IDE /*****************************************************************************/ #define ELEMENTS(x) (sizeof(x) / sizeof(x[0])) #define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); } #define PORTRAIT 0 // NOTE: These can set the origin to any corner of the display with value 0 - 3 #define LANDSCAPE 1 #define MAXSAMPLES 100 // Max points on band sample #define MAXPOINTSPERSAMPLE 10 // Max points read at each frequency #define RIGHTMARGIN 70 #define LEFTMARGIN 40 #define BOTTOMMARGIN 60 #define TOPMARGIN 25 #define UPPERPLOTMARGIN TOPMARGIN + 10 #define MINIMUMOFFSET 10 #define GRAPHAREATOPX LEFTMARGIN #define GRAPHAREATOPY TOPMARGIN // This is actually the menu width at top of display #define GRAPHAREAORIGIN h - BOTTOMMARGIN #define GRAPHAREAORIGINY GRAPHAREATOPX #define GRAPHAXIXWIDTH w - RIGHTMARGIN #define MENUITEMWIDTH 100 // The pixel width of the background of main menu item #define INTERMENUSPACING 25 #define MAXSCANPOINTS 100 // Number of plot data points #define SWRMINSSET 0 // EEPROM minimum swrs set if this int = 1 #define SWRMINSADDRESS 2 // Starting EEPROM address for mins #define NEXTSDFILENUMBER 90 // Two EEPROM bytes that holds the next number to be used in a file name #define EEPROMSTARTSCAN1 100 // Starting addresses for save scan data #define EEPROMSTARTSCAN2 500 #define CW 1 #define CCW -1 #define PINA 18 // Encoder hookup; reverse A and B if it moves the wrong way #define PINB 19 #define SWITCH 20 // following three pin definitions are needed by AD9850SPI library /*****************************************************************************/ #define FQ_UD 48 // connected to AD9850 freq update pin (FQ) #define RESET 49 // connected to AD9850 reset pin (RST) #define DATA 51 // connected to AD9850 serial data pin (MOSI) #define W_CLK 52 // connected to AD9850 module word load clock pin (CLK) #define ANALOGFORWARD A6 // Read FORWARD power #define ANALOGREFLECTED A7 // Read REFLECTED power as RETURNLOSS #define INPUTPORT A15 // Measuring the battery Voltage #define SD_SS 53 // SS pin used for SD card on LCD panel; connected through AA PCB to Mega board #define MAXFILES 20 // Number of SD files that can be opened #define NAMELENGTH 13 // The max name size #define FREQINCREMENT 100 // Used for upper/lower boundaries of scan adjustment #define FIXEDFREQINCR 5 // Used in frequency adjustment measures #define LOWER 1 // Used for lower band edge #define UPPER 2 // upper #define SMALL 0 // Small display type #define LARGE 1 char mySDFiles[MAXFILES][NAMELENGTH]; #ifndef min #define min(a, b) (((a) < (b)) ? (a) : (b)) #endif //================================ Function Prototypes =============== void DrawBarChartHAxes(int flag); void DrawBarChartH(int flag); void DrawBarChartV( double x , double y , double w, double h , double loval , double hival , double inc , double curval , int dig , int dec, unsigned int barcolor, unsigned int voidcolor, unsigned int bordercolor, unsigned int textcolor, unsigned int backcolor, String label, boolean & redraw); void DrawTable(int index); char *Format(float val, int dec, int dig, char sbuf[]); void FormatFrequency(float f, char buff[]); float GenerateTestData(float x); void GraphAxis(float gx, float gy, float w, float h, float xlo, float xhi, float xinc, float ylo, float yhi, float yinc, char * title, char * xlabel, char * ylabel, unsigned int gcolor, unsigned int acolor, unsigned int pcolor, unsigned int tcolor, unsigned int bcolor); void GraphPoints(float x, float y, float gx, float gy, float w, float h, float xlo, float xhi, float ylo, float yhi, unsigned int pcolor); void PrintNextPoint(double currentFreq, int index); int ReadSWRValue(); void ShowSubMenu(const char *menu[], int len); void GEN_kalibrering(); void WriteScanData(File root); int ShowFiles(); int ReadScanDataFile(int *thisSWR, int *thisFreq, float * min, float * max); int SelectFile(); int ConfirmDelete(int fnToDelete); //=============================== Globals ============================= volatile int aVal; volatile int encoderDirection; volatile int dir; volatile int pinALast; long _frequency = 0; // unsigned long long FREQtmp = 0; long ADD = 0.00; long kalib = 0; long SHORTkalib; // Colors #define LTBLUE 0xB6DF #define LTTEAL 0xBF5F #define LTGREEN 0xBFF7 #define LTCYAN 0xC7FF #define LTRED 0xFD34 #define LTMAGENTA 0xFD5F #define LTYELLOW 0xFFF8 #define LTORANGE 0xFE73 #define LTPINK 0xFDDF #define LTPURPLE 0xCCFF #define LTGREY 0xE71C #define BLUE 0x001F #define TEAL 0x0438 #define GREEN 0x07E0 #define CYAN 0x07FF #define RED 0xF800 #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define ORANGE 0xFD20 #define PINK 0xF81F #define PURPLE 0x801F #define GREY 0xC618 #define WHITE 0xFFFF #define BLACK 0x0000 #define DKBLUE 0x000D #define DKTEAL 0x020C #define DKGREEN 0x03E0 #define DKCYAN 0x03EF #define DKRED 0x6000 #define DKMAGENTA 0x8008 #define DKYELLOW 0x8400 #define DKORANGE 0x8200 #define DKPINK 0x9009 #define DKPURPLE 0x4010 #define DKGREY 0x4A49 int BACKGND; boolean _frequencyHasChanged = false; boolean _modeHasChanged = false; const int EE_FREQUENCY = 0; // last frequency const int EE_MODE = 6; // mode byte _mode; // the current mode //uint8_t latest_interrupted_pin; //int FwdOffSet; //int RevOffSet; int displaySize; int encoderPassCount = 0; int eepromMinIndex; int filesFound; int menuIndex; int menuDepth; int nextSDFileNumber; int row; int col; int k; int plotActive; int w, h; int spacing; int scanMinX; int scanMinY; int switchState; int swr[MAXSCANPOINTS]; int freq[MAXSCANPOINTS]; uint16_t g_identifier; int16_t last, value; // HF band edges follow... int bandEdges[] = {1800, 2000, 3500, 3800, 5250, 5450, 7000, 7200, 10100, 10150, 14000, 14350, 18068, 18158, 21000, 21450, 24890, 24990, 28000, 29700}; int minSWRs[9]; // Each element is the minimum for the bands above int pip[] = {60, 130, 200, 270, 340, 410}; int _ms; float currentFreq, ox , oy; float delta; float hedge; float bump; float lastX; float scanMinSWR; float targetMinSWR[3]; float RTL; float dBm; int NEWaddress = 100; long SHORTcalib; byte RunOnes = (1); // detect wether in SETUP or LOOP mode // Menus const char *menuLevel1[] = {" Analysis ", " Options ", " View Mins"}; const char *menuBands[] = {"All", "160M", "80M", "60M", "40M", "30M", "20M", "17M", "15M", "12M", "10M"}; const char *menuResults[] = {"Table"}; const char *menuLevel2[] = {"New Scan", "Repeat", "Frequency"}; const char *menuFile[] = {"Save Scan", "View Plot", "View Table", "Overlay", "Serial", "Delete File", "Test Battery"}; struct grafix { // Graph structure declaration int x; // upper left coordinate horizontal int y; // upper left coordinate vertical int w; // width of graph int h; // height of graph float minX; // minimum X graph value, can be negative float maxX; // maximum X graph value float minY; // minimum Y float maxY; // maximum Y float xInc; // scale division between lo and hi float yInc; // y increment float currentValue; // Current value int digitTotal; // total digits displayed, not counting decimal point int decimals; // digits after decimal point int barColor; // Color for bar int voidColor; // Background color in bar chart int backBar; // Background bar color int border; // Border color int textColor; // Color for text int backFill; // Background color for entire graph char label[30]; // Label text } myG; double volts; double bvolts; MCUFRIEND_kbv tft; // Graph structure definition File root; Rotary rotary = Rotary(19, 18); // Interrupts #4 and 5 on the Mega int MyBar = BLUE; int flag; boolean graph_1 = true; /***** Purpose: To show a menu option Paramter list: const char *whichMenu[] // Array of pointers to the menu option int len; // The number of menus Return value: void *****/ void ShowMenu(const char *whichMenu[], int len) { int i; tft.setTextColor(WHITE, BACKGND); for (i = 0; i < len; i++) { tft.setCursor(i * spacing, 0); tft.print(whichMenu[i]); } tft.setCursor(menuIndex * spacing, 0); tft.setTextColor(BLUE, WHITE); tft.print(whichMenu[menuIndex]); row = 0; col = menuIndex * spacing; } /*************************************************************** Purpose: To read the minimum SWRs in EEPROM memory Paramter list: void Return value: void ****************************************************************/ void ReadEEPROMMins() { for (int i = 0; i < ELEMENTS(minSWRs); i++) { EEPROM.get(SWRMINSADDRESS + i * sizeof(int), minSWRs[i]); #ifdef DEBUG Serial.print("i = "); Serial.println(minSWRs[i]); #endif } EEPROM.get(NEXTSDFILENUMBER, nextSDFileNumber); // The file number for the next SD file name } /**************************************************************** Purpose: To prepare EEPROM memory for holding minimum scan values Paramter list: void Return value: void ******************************************************************/ void SetEEPROMMins() { EEPROM.put(SWRMINSSET, 1); // Says we've been here before for (int i = 0; i < ELEMENTS(minSWRs); i++) { EEPROM.put(SWRMINSADDRESS + i * sizeof(int), 0); } } /**************************************************************** Purpose: This sets the default values for the graphics struccture Paramter list: void Return value: void *****************************************************************/ void SetGraphixDefaults() { myG.x = 20; myG.y = 100; myG.w = 350; myG.h = 30; myG.minX = 1.0; myG.maxX = 3.0; myG.yInc = .25; myG.xInc = .25; myG.minY = 1.0; myG.maxY = 3.0; myG.digitTotal = 3; myG.decimals = 2; myG.barColor = GREEN; myG.backBar = DKGREEN; myG.border = GREEN; myG.textColor = WHITE; myG.backFill = BLACK; } /********************************************************** Purpose: Sign-on screen Paramter list: void Return value: void **********************************************************/ void Splash() { int row, col; tft.fillScreen(BACKGND); //BLACK row = h / 5; col = w / 4; tft.setTextSize(4); tft.setTextColor(YELLOW, BACKGND); tft.setCursor(90, row - INTERMENUSPACING * 2); tft.print(F("Amateur Radio")); tft.setTextColor(YELLOW, BACKGND); tft.setCursor(col-70, row); tft.print(F("Antenna Analyzer")); tft.setTextSize(2); tft.setTextColor(WHITE, BACKGND); col = w / 3; tft.setCursor(col - 80, row + INTERMENUSPACING * 2); tft.print(MSG); col = w / 3; tft.setTextSize(2); tft.setTextColor(GREEN, BACKGND); tft.setCursor(col - 80, row + INTERMENUSPACING * 4); tft.print(F("First by Jack Purdum, W8TEE")); tft.setCursor(col - 50, row + INTERMENUSPACING * 5); tft.print(F("and Farrukh Zia, K2ZIA")); tft.setTextColor(MAGENTA, BACKGND); tft.setCursor(col - 140, row + INTERMENUSPACING * 7); tft.print(F("Modyfied by OZ6YM, Palle A. Andersen")); tft.setTextColor(YELLOW, BACKGND); tft.setTextSize(3); tft.setCursor(col - 60, row + INTERMENUSPACING * 9); tft.print(F("Detector: AD8307")); tft.setTextSize(2); delay(3000); } /********************************************************* Purpose: To update a menu display. Paramter list: int whichWay is the movement CW or CCW Return value: void ***********************************************************/ void AlterMenuOption(int whichWay) { int oldColumn = col; int oldIndex = menuIndex; switch (whichWay) { case CW: menuIndex++; if (menuIndex == ELEMENTS(menuLevel1)) { menuIndex = 0; col = 0; } break; case CCW: menuIndex--; if (menuIndex < 0) { menuIndex = ELEMENTS(menuLevel1) - 1; } break; default: break; } col = spacing * menuIndex; tft.setTextColor(WHITE, BACKGND); // Erase old menu option tft.setCursor(oldColumn, 0); tft.print(menuLevel1[oldIndex]); tft.setCursor(col, 0); // Show new menu option tft.setTextColor(BLUE, WHITE); tft.print(menuLevel1[menuIndex]); } #ifdef DEBUG int freeRam() { extern int __heap_start, *__brkval; //Calculate the free RAM between the top of the heap and top of the stack //This new variable is on the top of the stack int l_total = 0; if (__brkval == 0) l_total = (int) &l_total - (int) &__heap_start; else l_total = (int) &l_total - (int) __brkval; // l_total -= sizeof(l_total); //Because free RAM starts after this local variable return l_total; } #endif /****************************************************************** Purpose: To display and scroll data Paramter list: int swr[] // The swr data int freq[] // the frequency data Return value: void *******************************************************************/ void ShowAndScroll() { int index = 0; encoderPassCount = 0; DrawTable(index); while (true) { if (digitalRead(SWITCH) == LOW) // Return to main menu? break; if (encoderDirection != 0) // If it has rotated... { encoderPassCount++; // Need because there are 2 strobes per detent if (encoderPassCount == 2) { switch (encoderDirection) { case CW: index -= 3; // Showing 3 values per row break; case CCW: index += 3; // Showing 3 values per row break; } DrawTable(index); encoderPassCount = 0; } } } // end while (true) } /****************************************************************** Purpose: To display the data from the most-recent scan Paramter list: int index // Because list is scrollable, we need to track the index of the first display element int swh[]; // Array of measured swr's. Note: Values must be divided by 100 to get swr. Done to save memory int freq[]; // Array of associated frequencies Return value: void ******************************************************************/ void DrawTable(int index) { char buff[12]; if (index < 0 || index > MAXSAMPLES - 42) // There are 42 points shown on a screen return; tft.setTextColor(GREEN, BACKGND); //BLACK for (int k = 0; k < 14; k++) { tft.setCursor(0, k * 20 + TOPMARGIN); tft.print(((float) swr[index] * .01)); tft.setCursor(70, k * 20 + TOPMARGIN); FormatFrequency(freq[index], buff); tft.print(buff); index++; tft.setCursor(160, k * 20 + TOPMARGIN); tft.print(((float) swr[index] * .01)); tft.setCursor(230, k * 20 + TOPMARGIN); FormatFrequency(freq[index], buff); tft.print(buff); index++; tft.setCursor(320, k * 20 + TOPMARGIN); tft.print(((float) swr[index] * .01)); tft.setCursor(390, k * 20 + TOPMARGIN); FormatFrequency(freq[index], buff); tft.print(buff); index++; } } /************************************************************* Purpose: To display the axes for a graph Paramter list: see list above... Return value: void **************************************************************/ void GraphAxis(float gx, float gy, float w, float h, float xlo, float xhi, float xinc, float ylo, float yhi, float yinc, char * title, char * xlabel, char * ylabel, unsigned int gcolor, unsigned int acolor, unsigned int pcolor, unsigned int tcolor, unsigned int bcolor) { char buff[10]; int f; float i; float temp; tft.fillScreen(BACKGND); // BLACK for (i = ylo; i <= yhi; i += yinc) { // compute the transform temp = (i - ylo) * (gy - h - gy) / (yhi - ylo) + gy; if (i == 0) { tft.drawLine(gx, temp, gx + w, temp, acolor); } else { tft.drawLine(gx, temp, gx + w, temp, gcolor); } tft.setTextSize(1); //1 tft.setTextColor(tcolor, bcolor); tft.setCursor(gx - 40, temp); // precision is default Arduino--this could really use some format control tft.println(i); } hedge = xlo; bump = (xhi - xlo) / 5.0; // draw x scale for (int i = 0; i < ELEMENTS(pip); i++) { temp = pip[i]; tft.drawLine(temp, UPPERPLOTMARGIN, temp, gy, GREEN); tft.setTextSize(1); tft.setTextColor(tcolor, bcolor); tft.setCursor(temp - 10, gy + 10); f = (int) (hedge * .0001); FormatFrequency(f, buff); hedge += bump; tft.println(buff); } lastX = temp; //now draw the labels tft.setTextSize(2); tft.setTextColor(acolor, bcolor); tft.setCursor(430, gy + 10); tft.println(xlabel); tft.setTextSize(2); tft.setTextColor(acolor, bcolor); tft.setCursor(5, gy - h - 15); tft.println(ylabel); } /************************************************************* Purpose: Break out frequency value into format: XX.XXX. This removes redundant "000" at end of frequency Paramter list: float f // The frequency to format char buff[] // Where to store formatted result Return value: void ***************************************************************/ void FormatFrequency(int f, char buff[]) { char temp[11]; if (f < 1000) { // Under 40M itoa(f, buff, 10); temp[0] = buff[0]; temp[1] = '.'; strncpy(&temp[2], &buff[1], 3); temp[5] = '\0'; } else { // Over 40M itoa(f, buff, 10); temp[0] = buff[0]; temp[1] = buff[1]; temp[2] = '.'; strncpy(&temp[3], &buff[2], 3); temp[6] = '\0'; } strcpy(buff, temp); } /**************************************************************** Purpose: To format any floating point number. Little more than wrapper around dtostrf() Paramter list: float val // THe number to format int dec // The number of digits to display, including decimal point int dig // Digits after decimal point char sbuf[] // Where to put formatted result Return value: char * // Pointer to formatted result ****************************************************************/ char *Format(float val, int dec, int dig, char sbuf[] ) { int addpad = 0; char temp[dec + dig + 1]; sbuf[0] = '\0'; dtostrf(val, dec, dig, temp); int slen = strlen(temp); for (addpad = 1; addpad <= dec + dig - slen; addpad++) { strcat(sbuf, " "); } strcat(sbuf, temp); return sbuf; } /*************************************************************** Purpose: To show a sub-menu that is below the main options Paramter list: const char *whichMenu[] // Array of pointers to the menu option int len; // The number of menus Return value: void ****************************************************************/ void ShowSubMenu(const char *menu[], int len) { int i; tft.fillRect(0, TOPMARGIN, w + 10, h, BACKGND); // BLACK - Erase screen below top menu tft.setTextColor(GREEN, BACKGND); // BLACK for (i = i; i < len; i++) { tft.setCursor(col, i * INTERMENUSPACING + TOPMARGIN); tft.print(menu[i]); } tft.setCursor(col, TOPMARGIN); tft.setTextColor(BLUE, WHITE); tft.print(menu[0]); AlterMenuDepth(encoderDirection, menu, len); encoderDirection = aVal = CCW; encoderPassCount = 0; tft.fillRect(0, TOPMARGIN, w, h, BACKGND); // BLACK - Erase screen below top menu } /******************************************************************** Purpose: To highlight menu options as user scrolls through the list Paramter list: int whichWay // Are we scrolling up or down const carh *menu[] // The menu that is being scrolled int len // The number of menu options Return value: void *********************************************************************/ void AlterMenuDepth(int whichWay, const char *menu[], int len) { int oldRow = TOPMARGIN; int oldIndex; int itemCount = len; int ss; aVal = menuDepth = oldIndex = 0; while (true) { ss = digitalRead(SWITCH); if (ss == LOW) { return; } if (aVal != 0) { #ifdef DEBUG Serial.print("In AlterMenuDepth 2, passcount = 2"); Serial.print(" aVal = "); Serial.println(aVal); #endif oldIndex = menuDepth; switch (aVal) { case CW: menuDepth++; if (menuDepth == itemCount) { menuDepth = 0; row = TOPMARGIN; } else { row = menuDepth * INTERMENUSPACING + TOPMARGIN; // Scroll to next menu item } break; case CCW: menuDepth--; if (menuDepth < 0) { menuDepth = itemCount - 1; } row = menuDepth * INTERMENUSPACING + TOPMARGIN; break; default: break; } aVal = 0; tft.setTextColor(GREEN, BACKGND); // BLACK - Erase old menu option tft.setCursor(col, oldRow); tft.print(menu[oldIndex]); tft.print(" "); tft.setCursor(col, row); // Show new menu option tft.setTextColor(BLUE, WHITE); tft.print(menu[menuDepth]); oldRow = row; } } } /******************************************************************* Purpose: To plot the X and Y axis for horizontal bar chart with appropriate tick marks and labels Parameter list: MCUFRIEND_kbv d the graphics object struct grafix myG the current state of that object int flag if 1 labels are added, 0 if not Return value: void ********************************************************************/ void DrawBarChartHAxes(int flag) { int offset = myG.x - 10; float stepval; float i, data; char buff[10]; // draw the border, scale, and label once // avoid doing this on every update to minimize flicker // draw the border and scale tft.drawRect(myG.x , myG.y , myG.w, myG.h, myG.border); tft.setTextColor(myG.textColor, myG.backFill); // step val basically scales the hival and low val to the width stepval = (myG.xInc * (float (myG.w) / (float (myG.maxX - myG.minX)))) - .00001; if (flag) { for (i = 0; i <= myG.w; i += stepval) { tft.drawFastVLine(i + myG.x , myG.y + myG.h + 1, 5, myG.textColor); // draw lables tft.setTextSize(2); tft.setTextColor(myG.textColor, myG.backFill); // tft.setCursor(i + myG.x ,myG.y + myG.h + 10); tft.setCursor(i + offset , myG.y + myG.h + 10); // addling a small value to eliminate round off errors // this val may need to be adjusted data = ( i * (myG.xInc / stepval)) + myG.minX + 0.00001; Format(data, myG.digitTotal, myG.decimals, buff); tft.println(buff); } } } /***************************************************************** Purpose: To plot the X and Y values for the bar Parameter list: MCUFRIEND_kbv d the graphics object struct grafix myG the current state of that object int flag if 1 labels are added, 0 if not Return value: void *****************************************************************/ void DrawBarChartH(int flag) { float level; char buff[10]; // compute level of bar graph that is scaled to the width and the hi and low vals // this is needed to accompdate for +/- range capability draw the bar graph // write a upper and lower bar to minimize flicker cause by blanking out bar and redraw on update if (myG.currentValue > 0.0 && myG.currentValue < 3.0) { level = (myG.w * (((myG.currentValue - myG.minX) / (myG.maxX - myG.minX)))); } else { level = 0; } tft.fillRect(myG.x + level + 1, myG.y + 1, myG.w - level - 2, myG.h - 2, myG.backBar); tft.fillRect(myG.x + 1, myG.y + 1 , level - 1, myG.h - 2, myG.barColor); tft.setTextColor(myG.textColor, myG.backFill); // write the current value tft.setTextSize(2); tft.setCursor(myG.x + myG.w + 10 , myG.y + 5); if (myG.currentValue > 0.0 && myG.currentValue < 3.0) { Format(myG.currentValue, myG.digitTotal, myG.decimals, buff); // Changed to get rid of String object } else { strcpy(buff, " N/A"); } tft.println(buff); tft.setTextSize(2); switch (flag) { case 0: case 1: tft.setCursor(myG.x + 300, myG.y + 5); break; default: tft.setTextSize(2); tft.setCursor(myG.x , myG.y - 20); break; } tft.setTextColor(myG.textColor, myG.backFill); tft.println(myG.label); } /***************************************************************** Purpose: To perform the scan options from the main menu Paramter list: void Return value: void ******************************************************************/ void ViewMinimums() { int i, flag, len; flag = 2; // Draw tick marks len = ELEMENTS(menuBands); // col = MENUITEMWIDTH * 3; col = spacing * 2; ShowSubMenu(menuBands, len); myG.x = 20; myG.y = 100; myG.w = 350; myG.h = 30; myG.minX = 1.0; myG.maxX = 3.0; myG.yInc = .25; myG.xInc = .25; myG.voidColor = BLACK; myG.currentValue = ( (float) minSWRs[menuDepth - 1]) / 100.0; strcpy(myG.label, menuBands[menuDepth]); switch (menuDepth) { case 0: // All myG.voidColor = GREEN; myG.y = 25; flag = 0; myG.h = 25; for (i = 1; i < ELEMENTS(menuBands); i++) { strcpy(myG.label, menuBands[i]); myG.currentValue = (float) minSWRs[i - 1] / 100.0; if (i == ELEMENTS(menuBands) - 1) flag = 1; DrawBarChartHAxes(flag); DrawBarChartH(flag); myG.y += 30; } break; case 1: // 160 DrawBarChartHAxes(flag); DrawBarChartH(flag); break; case 2: // 80M DrawBarChartHAxes(flag); DrawBarChartH(flag); break; case 3: // 40M DrawBarChartHAxes(flag); DrawBarChartH(flag); break; case 4: // 30M DrawBarChartHAxes(flag); DrawBarChartH(flag); break; case 5: // 20M DrawBarChartHAxes(flag); DrawBarChartH(flag); break; case 6: // 17M DrawBarChartHAxes(flag); DrawBarChartH(flag); break; case 7: // 15M DrawBarChartHAxes(flag); DrawBarChartH(flag); break; case 8: // 12M DrawBarChartHAxes(flag); DrawBarChartH(flag); break; case 9: // 10M DrawBarChartHAxes(flag); DrawBarChartH(flag); break; default: break; } } /*************************************************************** Purpose: To plot the points of a scan Paramter list: see list above... Return value: void ****************************************************************/ void GraphPoints(float x, float y, float gx, float gy, float w, float h, float xlo, float xhi, float ylo, float yhi, unsigned int pcolor) { byte flag = 0; int p = 0; if (y > oy) { flag = 1; } x = (x - xlo) * ( w) / (xhi - xlo) + gx; y = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy; if (y > UPPERPLOTMARGIN && x > gx) { tft.drawLine(ox, oy, x, y, pcolor); tft.drawLine(ox, oy + 1, x, y + 1, pcolor); tft.drawLine(ox, oy - 1, x, y - 1, pcolor); if (flag) { scanMinX = x; scanMinY = y; } } ox = x; // Save old coordinates so we know where line starts oy = y; } /******************************************************************* Purpose: To set the lower and upper limits of a scan Paramter list: int whichOne // Which edge to set: 1 = low, 2 = high Return value: int // The frequency of the edge ********************************************************************/ void SetBandEdge(int whichOne) { int edge, offset; if (whichOne == LOWER) { tft.fillRect(0, TOPMARGIN, w + 10, h, BACKGND); // BLACK - Erase screen below top menu tft.setCursor(0, TOPMARGIN); tft.setTextColor(WHITE, BACKGND); // BLACK tft.print(F("Set scan edges, defaults:")); tft.setCursor(0, TOPMARGIN + INTERMENUSPACING); tft.print(F("start: ")); offset = TOPMARGIN + INTERMENUSPACING; // Sets for lower frequency edge = bandEdges[menuDepth * 2]; #ifdef DEBUG Serial.print("In SetBandEdge, edge = "); Serial.println(edge); #endif } else { tft.setCursor(0, TOPMARGIN + 2 * INTERMENUSPACING); tft.setTextColor(WHITE, BACKGND); // BLACK tft.print(F(" end: ")); tft.setTextColor(GREEN, BACKGND); //BLACK tft.setCursor(80, TOPMARGIN + 2 * INTERMENUSPACING); tft.print( bandEdges[menuDepth * 2 + 1]); offset = TOPMARGIN + 2 * INTERMENUSPACING; // Sets for upper frequency edge = bandEdges[menuDepth * 2 + 1]; } tft.setTextColor(BLUE, WHITE); tft.setCursor(80, offset); tft.print(edge); encoderDirection = 0; while (digitalRead(SWITCH) == HIGH) { // Wait for encoder switch change if (encoderDirection != 0) // If it has rotated... { #ifdef DEBUG Serial.print("encoderDirection = "); Serial.print(encoderDirection); Serial.print(" edge = "); Serial.print(edge); Serial.print(" pass = "); Serial.println(encoderPassCount); #endif switch (encoderDirection) { case CW: edge -= FREQINCREMENT; // Increased edge value break; case CCW: edge += FREQINCREMENT; // Decreased edge value break; } tft.setCursor(80, offset); tft.print(edge); encoderDirection = 0; } } if (whichOne == LOWER) { myG.minX = edge; } else { myG.maxX = edge; } tft.setCursor(80, offset); tft.setTextColor(GREEN, BACKGND); // BLACK tft.print(edge); delay(250); } /****************************************************************** Purpose: To present the different options via menus Paramter list: void Return value: void *******************************************************************/ void NewScanOptions() { int i, flag, len; int val; int saveIndex; float x, y; col = 0; if (plotActive == 0) { eepromMinIndex = menuDepth; len = ELEMENTS(menuBands) - 1; ShowSubMenu(&menuBands[1], len); } if (plotActive == 1) eepromMinIndex = menuDepth; if (plotActive == 0) { SetBandEdge(LOWER); // Set edges of scan SetBandEdge(UPPER); // Set edges of scan } #ifdef DEBUG Serial.print("min = "); Serial.print(myG.minX ); Serial.print(" max = "); Serial.println(myG.maxX); #endif if (myG.minX < 1.0 || myG.minY < 1.0) { tft.setTextSize(2); tft.setTextColor(GREEN, BACKGND); //BLACK tft.setCursor(col - 20, row + INTERMENUSPACING * 4); tft.print("Band edges not set"); return; } scanMinSWR = targetMinSWR[0] = 5.0; scanMinX = scanMinY = 0; myG.x = 60; // CAUTION: Assumes minimum SWRs are in the vswrs[] array myG.y = 290; myG.w = 350; myG.h = 260; if (myG.maxX < 30000.0) { // Need for repeat run with same parameters myG.minX *= 1000; myG.maxX *= 1000; } myG.xInc = 100000.0; myG.yInc = .25; ox = myG.minX ; oy = myG.maxY; GraphAxis( myG.x, myG.y, myG.w, myG.h, myG.minX, myG.maxX, myG.xInc, myG.minY, myG.maxY, .25, "VSWR", "FREQ", "VSWR", GREEN, RED, YELLOW, WHITE, BACKGND); delta = (myG.maxX - myG.minX) / MAXSCANPOINTS; for (x = myG.minX, k = 0; x < myG.maxX; x += delta, k++) { PrintNextPoint(x, k); freq[k] = x * .001; y = (float) ( (float) swr[k]) * .01; if (y < 1.0 || x < 1.0) // In case of garbage in array continue; } ox = myG.minX; oy = myG.maxY; scanMinSWR = 5.0; for (x = myG.minX, k = 0; x < myG.maxX; x += delta, k++) { y = (float) swr[k] * .01; x = (float) freq[k] * 1000; if (y < 1.0 || x < 1.0) // In case of garbage in array break; GraphPoints(x, y, myG.x, myG.y, myG.w, myG.h, myG.minX, myG.maxX, 1.0, 3.0, YELLOW); if (y < scanMinSWR) { scanMinSWR = y; targetMinSWR[0] = oy; targetMinSWR[1] = ox; lastX = x; saveIndex = k; } } lastX = ((myG.maxX - myG.minX) / (lastX - myG.x)); lastX = lastX * (targetMinSWR[1] - myG.x) + myG.minX; val = scanMinSWR * 100.0; minSWRs[menuDepth] = val; // Update array #ifdef DEBUG Serial.print("minIndex1 = "); Serial.print(eepromMinIndex); Serial.print(" plotActive = "); Serial.print(plotActive); Serial.print(" menuDepth = "); Serial.println(menuDepth); #endif menuDepth *= sizeof(int); EEPROM.put(SWRMINSADDRESS + menuDepth, val); // Update minimums in memory tft.setTextSize(2); // Plot header info tft.setTextColor(WHITE, BACKGND); //BLACK tft.setCursor(myG.x , myG.y - myG.h - 30); tft.print("Min SWR: "); tft.print(scanMinSWR); tft.print(" at Freq: "); tft.print((long) freq[saveIndex] * 1000); tft.setCursor(targetMinSWR[1] - MINIMUMOFFSET + 5, targetMinSWR[0] - 5); // Min point tft.setTextColor(RED, BACKGND); //BLACK tft.print("+"); plotActive = 1; } /************************************************************* Purpose: To perform the second menu option, "Options" Parameter list: void Return value: void *************************************************************/ void DoOptions() { int i, flag, len; int val; int saveIndex; int eepromAddr; int useFile; char fileName[13]; char tempNum[5]; float x, y; len = ELEMENTS(menuFile); col = spacing; ShowSubMenu(menuFile, len); strcpy(fileName, "SCAN"); switch (menuDepth) { /************************************************************************************************/ case 0: // Save Scan tft.setTextColor(GREEN, BACKGND); itoa(nextSDFileNumber, tempNum, 10); // Build the new file name strcat(fileName, tempNum); strcat(fileName, ".CSV"); tft.setCursor(50, 80); tft.print("Write new file: "); tft.print(fileName); root = SD.open(fileName, FILE_WRITE); if (!root) { tft.setTextColor(RED, BACKGND); tft.setCursor(50, 120); tft.print("File open failure."); tft.setCursor(50, 145); tft.print("Is SD card inserted?"); break; } WriteScanData(root); tft.setCursor(50, 145); tft.print("File named "); tft.print(fileName); tft.print(" successfully"); nextSDFileNumber++; // Update for next new file EEPROM.put(NEXTSDFILENUMBER, nextSDFileNumber); // Save in EEPROM break; /************************************************************************************************/ case 1: // View Plot int count; filesFound = ShowFiles(); useFile = SelectFile(); root = SD.open(mySDFiles[useFile], FILE_READ); tft.setTextColor(GREEN, BACKGND); memset(swr, 0, sizeof(swr)); memset(freq, 0, sizeof(freq)); count = ReadScanDataFile(swr, freq, &myG.minX, &myG.maxX); myG.x = 60; // CAUTION: Assumes minimum SWRs are in the vswrs[] array myG.y = 290; myG.w = 350; myG.h = 260; myG.minX *= 1000; myG.maxX *= 1000; myG.yInc = .25; ox = myG.minX; oy = myG.maxY; scanMinSWR = 5.0; GraphAxis( myG.x, myG.y, myG.w, myG.h, myG.minX, myG.maxX, myG.xInc, myG.minY, myG.maxY, .25, "VSWR", "FREQ", "VSWR", GREEN, RED, YELLOW, WHITE, BACKGND); delta = (myG.maxX - myG.minX) / MAXSCANPOINTS; for (x = myG.minX, k = 0; x < myG.maxX; x += delta, k++) { y = (float) swr[k] * .01; x = (float) freq[k] * 1000; if (y < 1.0 || x < 1.0) // In case of garbage in array break; GraphPoints(x, y, myG.x, myG.y, myG.w, myG.h, myG.minX, myG.maxX, 1.0, 3.0, YELLOW); if (y < scanMinSWR) { scanMinSWR = y; targetMinSWR[0] = oy; targetMinSWR[1] = ox; lastX = x; saveIndex = k; } } lastX = ((myG.maxX - myG.minX) / (lastX - myG.x)); lastX = lastX * (targetMinSWR[1] - myG.x) + myG.minX; val = scanMinSWR * 100.0; minSWRs[eepromMinIndex] = val; // Update array eepromMinIndex *= sizeof(int); tft.setTextSize(2); // Plot header info tft.setTextColor(WHITE, BACKGND); //BLACK tft.setCursor(myG.x , myG.y - myG.h - 30); tft.print("Min SWR: "); tft.print(scanMinSWR); tft.print(" at Freq: "); tft.print((long) freq[saveIndex] * 1000); tft.setCursor(targetMinSWR[1] - MINIMUMOFFSET + 5, targetMinSWR[0] - 5); // Min point tft.setTextColor(RED, BACKGND); //BLACK tft.print("+"); break; /************************************************************************************************/ case 2: // View Table filesFound = ShowFiles(); useFile = SelectFile(); root = SD.open(mySDFiles[useFile], FILE_READ); tft.setTextColor(GREEN, BACKGND); //BLACK count = ReadScanDataFile(swr, freq, &myG.minX, &myG.maxX); if (myG.minX < 1.0 || myG.minY < 1.0) { tft.setTextSize(2); tft.setTextColor(GREEN, BACKGND); //BLACK tft.setCursor(col - 20, row + INTERMENUSPACING * 4); tft.print("Band edges not set"); break; } tft.fillScreen(BACKGND); // BLACK tft.setTextColor(GREEN, BACKGND); //BLACK scanMinSWR = targetMinSWR[0] = 5.0; scanMinX = scanMinY = 0; myG.x = 60; // CAUTION: Assumes minimum SWRs are in the vswrs[] array myG.y = 290; myG.h = 260; ox = myG.minX; oy = myG.maxY; delta = (myG.maxX - myG.minX) / MAXSAMPLES; int k; myG.xInc = 0; for (x = myG.minX, k = 0; x < myG.maxX; x += delta, k++) { y = (float) swr[k] * .01; x = (float) freq[k] * 1000; if (y < 1.0 || x < 1.0) // In case of garbage in array break; } ShowAndScroll(); break; /************************************************************************************************/ case 3: // Overlay // int count; float overlayMinSWR; float overlayTargetMinSWR[2]; float overlayLastX; int overlaySWR[MAXSCANPOINTS]; int overlayFreq[MAXSCANPOINTS]; filesFound = ShowFiles(); useFile = SelectFile(); root = SD.open(mySDFiles[useFile], FILE_READ); tft.setTextColor(GREEN, BACKGND); //BLACK count = ReadScanDataFile(overlaySWR, overlayFreq, &myG.minX, &myG.maxX); // Read scan myG.x = 60; // CAUTION: Assumes minimum SWRs are in the overlays[] array myG.y = 290; myG.w = 350; myG.h = 260; myG.minX *= 1000; myG.maxX *= 1000; myG.yInc = .25; ox = myG.minX ; oy = myG.maxY; scanMinSWR = 5.0; overlayMinSWR = 5.0; GraphAxis( myG.x, myG.y, myG.w, myG.h, myG.minX, myG.maxX, myG.xInc, myG.minY, myG.maxY, .25, "VSWR", "FREQ", "VSWR", GREEN, RED, YELLOW, WHITE, BACKGND); delta = (myG.maxX - myG.minX) / MAXSCANPOINTS; overlayLastX = lastX; for (x = myG.minX, k = 0; x < myG.maxX; x += delta, k++) { y = (float) swr[k] * .01; x = (float) freq[k] * 1000; if (y < 1.0 || x < 1.0) // In case of garbage in array break; GraphPoints(x, y, myG.x, myG.y, myG.w, myG.h, myG.minX, myG.maxX, 1.0, 3.0, YELLOW); if (y < scanMinSWR) { scanMinSWR = y; targetMinSWR[0] = oy; targetMinSWR[1] = ox; } } tft.setCursor(targetMinSWR[1] - MINIMUMOFFSET + 5, targetMinSWR[0] - 5); // Min point tft.setTextColor(RED, BACKGND); //BLACK tft.print("+"); overlayMinSWR = 5.0; scanMinSWR = targetMinSWR[0] = 5.0; scanMinX = scanMinY = 0; ox = myG.minX; oy = myG.maxY; for (x = myG.minX, k = 0; x < myG.maxX; x += delta, k++) { y = (float) overlaySWR[k] * .01; x = (float) overlayFreq[k] * 1000; if (y < 1.0 || x < 1.0) // In case of garbage in array break; GraphPoints(x, y, myG.x, myG.y, myG.w, myG.h, myG.minX, myG.maxX, 1.0, 3.0, WHITE); if (y <= overlayMinSWR) { overlayMinSWR = y; overlayTargetMinSWR[0] = oy; overlayTargetMinSWR[1] = ox; lastX = x; saveIndex = k; } } lastX = ((myG.maxX - myG.minX) / (overlayLastX - myG.x)); lastX = lastX * (overlayTargetMinSWR[1] - myG.x) + myG.minX; val = overlayMinSWR * 100.0; minSWRs[eepromMinIndex] = val; // Update array eepromMinIndex *= sizeof(int); tft.setTextSize(2); // Plot header info tft.setTextColor(WHITE, BACKGND); //BLACK tft.setCursor(myG.x , myG.y - myG.h - 30); tft.print("Min SWR: "); tft.print(overlayMinSWR); tft.print(" at Freq: "); tft.print((long)lastX); tft.setCursor(targetMinSWR[1] - MINIMUMOFFSET + 5, targetMinSWR[0] - 5); // Min point tft.setTextColor(RED, BACKGND); //BLACK tft.print("+"); break; /************************************************************************************************ * VIRKER IKKE... /************************************************************************************************/ case 4: // Serial monitor output filesFound = ShowFiles(); useFile = SelectFile(); root = SD.open(mySDFiles[useFile], FILE_READ); tft.setTextColor(GREEN, BACKGND); //BLACK count = ReadScanDataFile(swr, freq, &myG.minX, &myG.maxX); tft.fillScreen(BACKGND); //BLACK tft.setCursor(100, 100); tft.print("Writing to Serial port"); /************************************************************************************************ * * Det efterfølgende virker ikke... * Der er intet der forbinder denne rutine med SERIAL-OUTPUT vis USB-porten * Der skal således arbejdes en del med denne efterfølgende rutine * ************************************************************************************************/ for (k = 0; k < MAXSCANPOINTS; k++) { // her er der noget der IKKE virker--------------------> if (freq[k] < 1 || swr[k] < 1) // In case of garbage in array break; // dette sender ingen data til serial-port } tft.setCursor(130, 200); tft.print("Done"); break; /************************************************************************************************/ case 5: // Delete file filesFound = ShowFiles(); useFile = SelectFile(); if (ConfirmDelete(useFile)) { tft.fillScreen(BACKGND); //BLACK tft.setTextColor(GREEN, BACKGND); //BLACK tft.setCursor(100, 80); tft.print("Deleting file: "); tft.print(mySDFiles[useFile]); SD.remove(mySDFiles[useFile]); tft.setCursor(130, 125); tft.print("Deleted successfully"); tft.setCursor(110, 150); tft.print("Press switch to continue:"); break; /************************************************************************************************/ case 6: // Measure battry voltage // ******************************* tft.fillScreen(BACKGND); //BLUE tft.setTextColor(GREEN, BACKGND); tft.setTextSize(3); tft.setCursor( 60, 40); tft.print("Battery Measuring!"); tft.setTextSize(2); tft.setCursor( 60, 80); tft.print("If Volt < 8 = Yellow"); tft.setCursor( 60, 110); tft.print("If Volt < 7.7 = RED"); // tft.setCursor( 80, 140); // tft.print("Charge the battery!!!"); tft.setCursor(80, 200); tft.print("Press switch to continue:"); // her skal teste on Svitzen er trykket og derefter hoppes ud Goon: DrawBarChartV( 455, 40, 10, 260, 0, 1500 , 100, bvolts , 4 , 0, BLUE, DKGREEN, BLUE, WHITE, BLACK, "Volt", graph_1); tft.setTextColor(GREEN, BACKGND); tft.setCursor( 80, 140); tft.print("Charge the battery!!!"); if (digitalRead(SWITCH) == HIGH) { goto Goon; } else { break; } default: break; } } } /************************************************************************************************ Purpose: To confirm that the user really wants to delete this file Parameter list: int fileIndex Index to name of the file selected for deletion Return value: int 1 if they want to delete, 0 otherwise ************************************************************************************************/ int ConfirmDelete(int fnToDelete) { char optionDelete[] = " Delete "; char optionCancel[] = " Cancel "; int colOffset; int columnDelete = 100; int columnCancel = 200; tft.fillScreen(BACKGND); // BLACK tft.setTextColor(RED, BACKGND); //BLACK tft.setCursor(100, 80); tft.print("File to Delete: "); tft.print(mySDFiles[fnToDelete]); tft.setTextColor(GREEN, BACKGND); //BLACK tft.setCursor(columnDelete, 120); tft.print(optionDelete); tft.setTextColor(BLUE, WHITE); tft.setCursor(columnCancel, 120); tft.print(optionCancel); colOffset = columnCancel; encoderPassCount = 0; while (true) { #ifdef DEBUG Serial.println("In ConfirmDelete"); #endif if (aVal != 0) { encoderPassCount++; // Need because there are 2 strobes per detent if (encoderPassCount == 2) { if (colOffset == columnCancel) { // Switch to delete colOffset = columnDelete; tft.setTextColor(GREEN, BACKGND); //BLACK tft.setCursor(columnCancel, 120); tft.print(optionCancel); tft.setTextColor(BLUE, WHITE); tft.setCursor(columnDelete, 120); tft.print(optionDelete); } else { // Switch to cancel colOffset = columnCancel; tft.setTextColor(GREEN, BACKGND); //BLACK; tft.setCursor(columnDelete, 120); tft.print(optionDelete); tft.setTextColor(BLUE, WHITE); tft.setCursor(columnCancel, 120); tft.print(optionCancel); } encoderDirection = aVal = CW; //har været CCW encoderPassCount = 0; } } switchState = digitalRead(SWITCH); delay(150); if (switchState == LOW) { if (colOffset == columnDelete) return 1; else return 0; } } } /***************************************************************** Purpose: To read the most-recent scan data to a CSV data file Parameter list: int *thisSWR base of array for SWR int *thisFreq base of array for frequency float *min starting freq for scan float *max ending freq for scan Return value: int the number of data pairs read ******************************************************************/ int ReadScanDataFile(int *thisSWR, int *thisFreq, float * min, float * max) { char temp; char buff[10]; int i; int index = 0; i = 0; while (root.available()) { // The first two values are min and max buff[i] = root.read(); if (buff[i] != ',' && buff[i] != '\n') { i++; } else { if (buff[i] == ',') { // Read SWR data buff[i] = '\0'; *min = atof(buff); i = 0; } else { buff[i] = '\0'; *max = atof(buff); i = 0; break; } } } i = 0; while (root.available() ) { buff[i] = root.read(); if (buff[i] != ',' && buff[i] != '\n') { i++; } else { if (buff[i] == ',') { // Read SWR data buff[i] = '\0'; thisSWR[index] = atoi(buff); i = 0; } else { buff[i] = '\0'; thisFreq[index++] = atoi(buff); i = 0; } } } root.close(); return index; } /***************************************************************** Purpose: To save the most-recent scan data to a CSV data file Parameter list: File root the currently-open file Return value: void CAUTION: This must be called AFTER a new scan is done ******************************************************************/ void WriteScanData(File root) { char temp[10]; int i; int xValMin, xValMax; xValMin = (int) (myG.minX / 1000.0); xValMax = (int) (myG.maxX / 1000.0); itoa(xValMin, temp, 10); // Set frequency limits root.print(temp); root.print(","); itoa(xValMax, temp, 10); root.println(temp); for (i = 0; i < MAXSCANPOINTS; i++) { if (swr[i] == 0) continue; itoa(swr[i], temp, 10); root.print(temp); // Write data... root.print(","); // ...and a comma itoa(freq[i], temp, 10); root.println(temp); // Add data and newline } root.close(); } /***************************************************************** Purpose: To select a file from a list of the current SD files Parameter list: void Return value: int an index into the file name array for the file to be used ******************************************************************/ int SelectFile() { int dir, edge, offset; offset = TOPMARGIN + 25; edge = 0; for (int k = 0; k < filesFound; k++) { tft.setCursor(LEFTMARGIN + 80, offset + k * 25); tft.print(mySDFiles[k]); } tft.setTextColor(BLUE, WHITE); tft.setCursor(LEFTMARGIN + 80, offset); tft.print(mySDFiles[0]); encoderPassCount = 0; while (digitalRead(SWITCH) == HIGH) { // Wait for encoder switch change if (encoderDirection != 0) // If it has rotated... { encoderPassCount++; // Need because there are 2 strobes per detent if (encoderPassCount == 2) { tft.setTextColor(GREEN, BACKGND); //BLACK tft.setCursor(LEFTMARGIN + 80, offset + edge * 25); tft.print(mySDFiles[edge]); switch (encoderDirection) { case CW: edge--; // Increased edge value break; case CCW: edge++; // Decreased edge value break; default: #ifdef DEBUG Serial.print(" Souldn't be here, edge = "); Serial.println(edge); #endif break; } if (edge >= filesFound) edge = 0; if (edge < 0) edge = filesFound - 1; tft.setTextColor(BLUE, WHITE); tft.setCursor(LEFTMARGIN + 80, offset + edge * 25); tft.print(mySDFiles[edge]); encoderPassCount = 0; encoderDirection = 0; } } } return edge; // The index for the file name } /********************************************************* Purpose: To present a list of the current SD files Parameter list: void Return value: int the number of files on the SD card **********************************************************/ int ShowFiles() { int counter = 0; int offset; tft.setTextColor(GREEN, BACKGND); //BLACK root = SD.open("/"); root.rewindDirectory(); // Begin at the start of the directory while (true) { File entry = root.openNextFile(); if (! entry) { break; } strcpy(mySDFiles[counter++], entry.name()); entry.close(); } offset = TOPMARGIN + 25; tft.setCursor(0, TOPMARGIN); tft.print("SD files:"); for (k = 0; k < counter; k++) { tft.setCursor(LEFTMARGIN + 80, offset + k * 25); tft.print(mySDFiles[k]); } tft.setCursor(0, offset + k * 25); tft.print("List Done!"); root.close(); return counter; } /*************************************************************** Purpose: To set up the default values for graphics structure Parameter list: void Return value: void ****************************************************************/ void InitGraphicsStructure() { myG.x = 60; // CAUTION: Assumes minimum SWRs are in the overlays[] array myG.y = 290; myG.w = 350; myG.h = 260; myG.minX = 7000; // 40M is the default myG.maxX = 7400; myG.minX *= 1000; myG.maxX *= 1000; myG.yInc = .25; ox = myG.minX ; oy = myG.maxY; } /**************************************************************** Purpose: To give a new scan choice to user Parameter list: void Return value: void *****************************************************************/ void DoNewScanChoice() { int len; int localSWR; len = ELEMENTS(menuLevel2); col = 0; ShowSubMenu(menuLevel2, len); #ifdef DEBUG Serial.print("In DoNewScanChoice() "); Serial.print("At line 1842 currentFreq = "); Serial.println(currentFreq); #endif switch (menuDepth) { case 0: // New scan plotActive = 0; // Doing a new scan ox = myG.minX; oy = myG.maxY; NewScanOptions(); delay(250); break; case 1: // Repeat currentFreq = ox = myG.minX; oy = myG.maxY; #ifdef DEBUG Serial.print(" currentFreq = "); Serial.println(currentFreq); #endif NewScanOptions(); delay(250); break; case 2: // Frequency adjust len = ELEMENTS(menuBands) - 1; ShowSubMenu(&menuBands[1], len); SetFixedFrequencyBandEdge(); delay(250); currentFreq = ox = myG.minX; oy = myG.maxY; currentFreq *= 1000.0; #ifdef DEBUG Serial.print("At line 1811 currentFreq = "); Serial.println(currentFreq); #endif switchState = digitalRead(SWITCH); tft.fillRect(0, TOPMARGIN, w, h, BLACK); // Erase screen below top menu tft.setTextSize(3); tft.setTextColor(GREEN, BACKGND); //BLACK tft.setCursor(50, 80); tft.print("Frequency: "); tft.setCursor(235, 80); tft.setTextColor(WHITE, BACKGND); //BLACK tft.print(currentFreq,0); tft.setCursor(85, 130); tft.setTextColor(GREEN, BACKGND); //BLACK tft.print("SWR = 1: "); tft.setTextColor(WHITE, BACKGND); //BLACK tft.setCursor(85, 180); tft.setTextColor(GREEN, BACKGND); //BLACK tft.print("RTL = "); tft.setTextColor(WHITE, BACKGND); //BLACK DDS.setfreq(currentFreq, 0); #ifdef DEBUG Serial.print("Bottom: currentFreq = "); Serial.println(currentFreq); #endif delay(10); // wait for AD9850 output to become stable while (switchState == HIGH) { // Keep reading until a press DDS.setfreq(currentFreq, 0); delay(10); // wait for AD9850 output to become stable localSWR = ReadSWRValue(); tft.setCursor(220, 130); if ( localSWR < 0 ) { tft.print(" < 5 "); tft.setCursor(235, 180); tft.print(" 0.00 "); } else { tft.setCursor(235, 130); tft.print(localSWR * .01); tft.setCursor(235, 180); tft.print(RTL); } switchState = digitalRead(SWITCH); delay(250); } break; } tft.setTextSize(2); } /*********************************************************** Purpose: To set the normal band edges for a selected band Parameter list: void Return value: void ************************************************************/ void SetFixedFrequencyBandEdge() { int edge, offset; tft.fillRect(0, TOPMARGIN, w + 10, h, BACKGND); // //BLACK - Erase screen below top menu tft.setCursor(0, TOPMARGIN); tft.setTextColor(WHITE, BACKGND);//BLACK tft.print(F("Set scan edges, defaults:")); tft.setCursor(0, TOPMARGIN + INTERMENUSPACING); tft.print(F("start: ")); offset = TOPMARGIN + INTERMENUSPACING; // Sets for lower frequency edge = bandEdges[menuDepth * 2]; #ifdef DEBUG Serial.print("In SetFixedBandEdge, edge = "); Serial.println(edge); #endif tft.setTextColor(BLUE, WHITE); tft.setCursor(80, offset); tft.print(edge); encoderPassCount = 0; while (digitalRead(SWITCH) == HIGH) { // Wait for encoder switch change #ifdef DEBUG Serial.println("In SetFixedFrequencyBandEdge"); Serial.print("encoderDirection = "); Serial.print(encoderDirection); Serial.print(" edge = "); Serial.print(edge); Serial.print(" pass = "); Serial.println(encoderPassCount); #endif if (encoderDirection != 0) // If it has rotated... { encoderPassCount++; // Need because there are 2 strobes per detent if (encoderPassCount == 2) { switch (encoderDirection) { case CW: edge -= FIXEDFREQINCR; // Increased edge value break; case CCW: edge += FIXEDFREQINCR; // Decreased edge value break; default: break; } tft.setCursor(80, offset); tft.print(edge); encoderPassCount = 0; encoderDirection = 0; } } } myG.minX = edge; } /************************************************************** Purpose: To read the current SWR for a given frequency Parameter list: float currentFreq the frequency being tested int index the scan point index into the array Return value: int the number of files on the SD card ***************************************************************/ void PrintNextPoint(float currentFreq, int index) { float FWD = 0.0; float REV = 0.0; float VSWR; int phase = 0; // phase for DDS.setfreq function in AD9850SPI library DDS.begin(W_CLK, FQ_UD, RESET); DDS.calibrate(125000000); // change this value if AD9850 calibration is needed DDS.setfreq(currentFreq, phase); delay(10); // wait for AD9850 output to become stable DDS.setfreq(currentFreq, phase); delay(10); // wait for AD9850 output to become stable FREQtmp = currentFreq; swr[index] = ReadSWRValue(); #ifdef DEBUG Serial.print(" SWR: "); Serial.println(swr[index]); #endif } /************************************************************ Purpose: To read one bridge measurement Parameter list: void Return value: int the swr * 1000 so it comes back as an int CAUTION: Assumes that frequency has already been set *************************************************************/ int ReadSWRValue() { int i; float sum[2] = {0.0, 0.0}; float FWD = 0.0; float REV = 0.0; float VSWR; int address = 100; float r; if ( RunOnes != 1 ) // Denne rutine benyttes ikke ved generering { // af kalibreringstabellen // ----------------------------------------------------------- // Hent kalibrering FWD-signal for aktuel frekvens i EEPROM // void LaesEEPROM_Kalibrering(long ADD, int address) // læser og omregner ADD til FWD, FORWARD ved given frekvens //------------------------------------------------------------ LaesEEPROM_Kalibrering( SHORTkalib, address); } // END of if FWD = ADD; FWD = ( FWD / 100 ) - ( -0.43 ); // convert ADD to float FWD med 2 decimaler og juster #ifdef DEBUG Serial.print(" FWD = "); Serial.println(FWD); #endif //-------------------------------------------------------------- // Take multiple samples at each frequency (10) //-------------------------------------------------------------- for (i = 0; i < MAXPOINTSPERSAMPLE; i++) { sum[1] += (float) analogRead(ANALOGREFLECTED); } REV = sum[1] / (float) MAXPOINTSPERSAMPLE; //---------------------------------------------------------------- // Her omregnes fra dBm, som måles med AD8307 til fastsættelse af // FORWARD signalstyrke på målehovedet //---------------------------------------------------------------- // RTL = RETURNLOSS // 2.56 = CPU INTERN REFF - // 1024 = ADCconverterens opløsning // 0.0025 = AD8307's mV pr. dBm // FWD = FORWARD = oscillator signal styrke //---------------------------------------------------------------- dBm = ( ( ( ( ( REV * (-1)) / 10 ) * 2.56 ) / 1023 )) / 0.0025; RTL = ( FWD - dBm ); //---------------------------------------------------------------- // Her omregnes via Reflektionskoefficient til WSWR // ændres til heltal (int) for at kunne gemmes/hentes i/fra EEPROM //---------------------------------------------------------------- r = pow(10, - RTL / 20 ); // Reflektionskoefficient VSWR = (( 1 + r ) / ( 1 - r ) * ( - 1 )); return (int) (VSWR * 100.0); // Save as an integer //---------------------------------------------------------------- } // **************** END of PROGRAM ******************************* /***************************************************************** Purpose: To read the rotary encoder Parameter list: void Return value: int the direction of the rotation, 0 = no rotation 1 = CW -1 = CCW ******************************************************************/ int ReadEncoder() { unsigned char result = rotary.process(); if (result == DIR_CW) { encoderDirection = aVal = -1; } else if (result == DIR_CCW) { encoderDirection = aVal = 1; } return aVal; } //***************************************************************** //=========================== setup() ============================= //***************************************************************** void setup(void) { int eepromFlag; #ifdef DEBUG Serial.begin(115200); #endif pinMode(PINA, INPUT); pinALast = digitalRead(PINA); pinMode(PINB, INPUT); pinMode(SWITCH, INPUT_PULLUP); digitalWrite(SWITCH, HIGH); attachInterrupt(5, ReadEncoder, CHANGE); // These are interrupt numbers, not pin numbers attachInterrupt(4, ReadEncoder, CHANGE); // make sure internal pull up resistors on analog pins are disabled // so that they do not affect analog input readings pinMode(ANALOGFORWARD, INPUT); pinMode(ANALOGREFLECTED, INPUT); // enable MEGA 2.56V internal reference analogReference(INTERNAL2V56); // After changing the analog reference, // the first few readings from analogRead() may not be accurate. // Read and discard 10 readings. for (int i = 0; i < 10; i++) { int fwd = analogRead(ANALOGFORWARD); int rev = analogRead(ANALOGREFLECTED); } DDS.begin(W_CLK, FQ_UD, RESET); DDS.calibrate(125000000); // change this value if AD9850 calibration is needed DDS.setfreq(7000000, 0); // set AD9850 freq and phase to 0 delay(10); // Let it settle DDS.setfreq(7000000, 0); // set AD9850 freq and phase to 0 delay(10); // Let it settle #ifdef DEBUG Serial.println ("DDS Module Initialized ..."); #endif menuIndex = 0; // The highest level; drill down from there menuDepth = 0; plotActive = 0; // We are not leading up to a plot g_identifier = tft.readID(); // Get TFT ID 3.95" = 0x9486, 3.6" = 0x9488 /* if (g_identifier == 0x9486) { // If 3.6" TFT, invert colors BLUE = ~BLUE; GREEN = ~GREEN; RED = ~RED; YELLOW = ~YELLOW; WHITE = ~WHITE; BLACK = ~BLACK; DKGREEN = ~DKGREEN; } */ tft.begin(g_identifier); BACKGND = BLUE; #ifdef DEBUG Serial.print("TFT ID "); Serial.println(g_identifier, HEX); #endif //******************************************************************** switch (g_identifier) { default: // Weird placement, but make small display the default type case 0x154: case 0x9341: case 0x5408: displaySize = SMALL; break; case 0x1581: case 0x5310: // Untested -- I don't have these displays case 0x7796: // Untested case 0x9090: // Untested, but needs colors complemented case 0x9481: // Untested case 0x9487: // Untested case 0x9488: case 0x9486: case 0xD3D3: displaySize = LARGE; break; } //******************************************************************* EEPROM.get(SWRMINSSET, eepromFlag); // Have the minimum SWRs been set? if (eepromFlag != 1) { SetEEPROMMins(); } else { ReadEEPROMMins(); } tft.setRotation(LANDSCAPE); // In Landscape mode, the arguments are: setCursor(col, row) with botton row = w - 20. w = tft.width(), h = tft.height(); InitGraphicsStructure(); spacing = (w - 20) / ELEMENTS(menuLevel1); // Spacing between main menu items tft.setTextSize(2); SetGraphixDefaults(); #ifndef DEBUG Splash(); #endif switchState = digitalRead(SWITCH); delay(150); if (switchState == LOW) { RunOnes = 1; GEN_kalibrering(); return; } tft.fillScreen(BACKGND); //BLACK ShowMenu(menuLevel1, ELEMENTS(menuLevel1)); tft.setTextColor(GREEN, BACKGND); // BLACK tft.setCursor(50, 120); if (!SD.begin(SD_SS)) { tft.println("initialization failed!"); tft.setCursor(50, 145); tft.println("Is SD card in slot?"); } } //******************************************************** //======================= loop() ========================= //******************************************************** void loop(void) { int i, flag, len, oldIndex; int val; float x, y; RunOnes = 0; if (aVal != 0) { #ifdef DEBUG Serial.print("encoderPassCount = "); Serial.print(encoderPassCount); Serial.print(" aVal = "); Serial.print(aVal); Serial.print(F(" menuIndex = ")); Serial.print(menuIndex); Serial.print(" encoderDirection = "); Serial.println(encoderDirection); #endif AlterMenuOption(encoderDirection); encoderDirection = aVal = 0; encoderPassCount = 0; } switchState = digitalRead(SWITCH); if (switchState == LOW) { delay(250); // No debounce on switch menuDepth++; // Add depth check later row += 50; // Do row check later oldIndex = menuIndex; switch (menuIndex) { case 0: //====================== New Scan ========================== DoNewScanChoice(); break; case 1: //====================== Options =========================== DoOptions(); break; case 2: //====================== Min Options ====================== ViewMinimums(); break; default: #ifdef DEBUG Serial.print(F("I shouldn't be here: menuIndex = ")); Serial.println(menuIndex); #endif break; } menuIndex = oldIndex; while (digitalRead(SWITCH) == HIGH) { // Force a pause to view results of above menu selection... switchState = HIGH; } tft.fillScreen(BACKGND); //BLACK menuDepth = 0; encoderDirection = aVal = 0; ShowMenu(menuLevel1, ELEMENTS(menuLevel1) ); } } //******************************************************************** // EEPROM //******************************************************************** void SaveToEEPROM(unsigned int value, int address) { // save a 2 byte int to EEPROM. The MSB is saved to the lowest address address += 1; // for (int i = 0; i < 2; i++) { byte toSave = value & 0xFF; if (EEPROM.read(address) != toSave) // only write if we really need to so we avoid excessive writes { EEPROM.write(address, toSave); } // value = value >> 8; address--; } } //******************************************************************** // SAVE //******************************************************************** void SaveToEEPROM(long value, int address) { // save a 4 byte long to EEPROM. The MSB is saved to the lowest address address += 3; // for (int i = 0; i < 4; i++) { byte toSave = value & 0xFF; if (EEPROM.read(address) != toSave) // only write if we really need to so we avoid excessive writes { EEPROM.write(address, toSave); } // value = value >> 8; address--; } } //******************************************************************** // READ //******************************************************************** // read 2 byte int from EEPROM // unsigned int ReadIntFromEEPROM(int address) { unsigned int value = EEPROM.read(address); value = value << 8; return value | EEPROM.read(address + 1); } // // read 4 byte long from EEPROM long ReadLongFromEEPROM(int address) { long value = 0; for (int i = 0; i < 4; i++) { value = value | EEPROM.read(address); if (i < 3) { value = value << 8; address++; } } return value; } // //*************************************************************************** // Part of SAVE - current frequency to EEPROM //*************************************************************************** void SaveStateToEEPROM() { static int lastSaveMS; // remember when we last did this. //Note: a static variables retains its value on each call of this routine int elapsed = _ms - lastSaveMS; // we only do this every 5 seconds if (elapsed >= 5000) { // only save if the frequency has actually changed if (_frequencyHasChanged) { SaveToEEPROM((long) _frequency, EE_FREQUENCY); _frequencyHasChanged = false; } if (_modeHasChanged) { if (EEPROM.read(EE_MODE) != _mode) { EEPROM.write(EE_MODE, _mode); } _modeHasChanged = false; } lastSaveMS = _ms; } } // //******************************************************************** // Generation Calibrating Tabel //******************************************************************** void GEN_kalibrering() { NEWaddress = 100; //Startadresse for den genererede TABEL RunOnes = 1; int strCnt = 0; float OldADDdata; long int taeller = 100000; tft.fillScreen(BACKGND); //BLACK tft.setTextSize(3); tft.setTextColor(YELLOW, BACKGND); // BLACK tft.setCursor(50, 80); tft.print("Make Calibration DATA"); tft.setCursor(50, 130); tft.print("Frequency = "); tft.setTextColor(GREEN, BACKGND); //BLACKe tft.setCursor(50, 180); tft.print("NO DUT connected!!"); tft.setCursor(50, 230); tft.print("DATA = "); DDS.begin(W_CLK, FQ_UD, RESET); DDS.calibrate(125000000); // change this value if AD9850 calibration is needed DDS.setfreq(7000000, 0); // set AD9850 freq and phase to 0 delay(2); // Let it settle DDS.setfreq(7000000, 0); // set AD9850 freq and phase to 0 delay(2); // Let it settle #ifdef DEBUG Serial.println("Genererede Kalibrerings DATA"); #endif for ( int FRQ = 4; FRQ <= 300; FRQ = FRQ + 2 ) // advance tabel = 200000 Hz { _frequency = ( FRQ * taeller); DDS.setfreq( _frequency,0); delay(2); DDS.setfreq( _frequency,0); delay(2); tft.setCursor(260, 130); tft.print(_frequency); ReadSWRValue(); //ReadSWR(); // Læs REV omsæt til dBm if ( dBm != OldADDdata ) { OldADDdata = dBm * 100; //void SaveToEEPROM(long _frequency, int NEWaddress) //-------------------------------------------------- SaveToEEPROM( _frequency, NEWaddress); Serial.print(" Gemt frekvens = "); Serial.print(_frequency); Serial.print(" Addr = "); Serial.print(NEWaddress); NEWaddress = NEWaddress + 2; // Avancer "adreesse" til næste frie celle ADD = dBm * 100; // Vi vil have 2 decimaler med tft.setCursor(260, 230); tft.print( (float) ADD/100); //void SaveToEEPROM(long ADD, int address) //-------------------------------------------------- SaveToEEPROM( ADD, NEWaddress); Serial.print(" dBm * 100 = "); Serial.print(ADD); Serial.print(" Addr = "); Serial.print(NEWaddress); NEWaddress = NEWaddress + 4; // Avancer "adreesse" til næste frie celle Serial.print(" Addr = "); Serial.println(NEWaddress); strCnt++; // Her tæller vi tabellens længde #ifdef DEBUG Serial.print("Gemt frekvens = "); Serial.print(_frequency); Serial.print(" på adresse = "); Serial.print(NEWaddress); Serial.print(" dBm * 100 = "); Serial.println(ADD); #endif } // END of if } // END of for #ifdef DEBUG Serial.println(strCnt); // udskriv tabellens længde #endif tft.fillScreen(BACKGND); // BLACK tft.setTextSize(2); ShowMenu(menuLevel1, ELEMENTS(menuLevel1)); } // end of Program //******************************************************************** // SLUT på tabelgenerering //******************************************************************** //******************************************************************** // void LaesEEPROM_Kalibrering(long AD8307, int address) // Læs KalibreringsDATA fra EEPROM //******************************************************************** void LaesEEPROM_Kalibrering(long kalib, int address) { NEWaddress = 100; //Startadresse for den genererede TABEL long int My_frequency; long int nxtKalFrek; // advance tabel = 200000 Hz for ( int FRQ = 4; FRQ <= 300; FRQ = FRQ + 2 ) { // Læs kalib = FORWARD-data fra EEPROM //------------------------------------------------ My_frequency = ReadIntFromEEPROM( NEWaddress); // get the frequency from EEPROM kalib = ReadLongFromEEPROM( NEWaddress + 2); nxtKalFrek = ReadIntFromEEPROM( NEWaddress + 6); // get the frequency from EEPRO // omsæt tælleren til fuld frekvens //------------------------------------------------ My_frequency = FRQ * 100000; #ifdef DEBUG Serial.print(" My_frequency = "); Serial.print(My_frequency); // unsigned int Serial.print(" kalib = "); Serial.print(kalib); // long Serial.print(" Next Freq = "); Serial.print(nxtKalFrek); // unsigned int Serial.print(" FREQtmp = "); Serial.println(FREQtmp); #endif //------------------------------------------------ // Test om vi er ved den rigtige frekvens // i så fald er ADD sat lig kalib //------------------------------------------------ if ( FREQtmp >= ( My_frequency ) && ( FREQtmp < ( My_frequency + 200000) ) ) { ADD = kalib; return; } NEWaddress = NEWaddress + 6; // avancer EEPROM-adresse-tæller } } /******************************************************************************************************** This method will draw a vertical bar graph for single input it has a rather large arguement list and is as follows x = position of bar graph (lower left of bar) y = position of bar (lower left of bar w = width of bar graph h = height of bar graph (does not need to be the same as the max scale) loval = lower value of the scale (can be negative) hival = upper value of the scale inc = scale division between loval and hival curval = date to graph (must be between loval and hival) dig = format control to set number of digits to display (not includeing the decimal) dec = format control to set number of decimals to display (not includeing the decimal) barcolor = color of bar graph voidcolor = color of bar graph background bordercolor = color of the border of the graph textcolor = color of the text backcolor = color of the bar graph's background label = bottom lable text for the graph redraw = flag to redraw display only on first pass (to reduce flickering) ******************************************************************************************/ void DrawBarChartV( double x , double y , double w, double h , double loval , double hival , double inc , double curval , int dig , int dec, unsigned int barcolor, unsigned int voidcolor, unsigned int bordercolor, unsigned int textcolor, unsigned int backcolor, String label, boolean & redraw) { // DrawBarChartV( 455, 40, 10, Maxi, 0, 1500 , 100, bvolts , 4 , 0, BLUE, DKGREEN, BLUE, WHITE, BLACK, "Volt", graph_1); double stepval, range; double my, level; double i, data; // int Maxi = 260; // draw the border, scale, and label // avoid doing this on every update to minimize flicker if (redraw == true) { redraw = false; tft.drawRect(x , y , w , h , bordercolor); // step val basically scales the hival and low val to the height // deducting a small value to eliminate round off errors // this val may need to be adjusted stepval = ( inc) * (double (h) / (double (hival - loval))) - .001; //********************************************************************** tft.setTextSize(1); tft.setTextColor(textcolor, BACKGND); for (i = 0; i <= h; i += stepval) { my = y + h + i - 260; // 260 = skala-begrænsning //**************************************************************** tft.drawFastHLine(x-5, my, 5, textcolor); // draw vertical skala-labels //---------------------------- tft.setCursor(x + w -40, my - 3 ); data = (hival - ( i * (inc / stepval ))) / 100; tft.println(Format(data, dig, dec)); //Skala lodret } //******** END of for ****************************************** } //********* END of if ****************************************** // compute level of bar graph that is scaled to the height // and the hi and low vals // this is needed to accompdate for +/- range //******************************************************************** bvolts = analogRead(INPUTPORT); bvolts = bvolts * 256 / 1023; long Mylevel = map( bvolts, 0, 230, 0, 1023 ); level = map( Mylevel, 0, 1023, 0, 260 ); if ( level < 140 ) { MyBar = YELLOW; } // level < 8,00 volt if ( level > 140 ) { MyBar = GREEN; } // level OK! if ( level < 130 ) { MyBar = RED; // level < 6,60 volt tft.setTextColor(RED, BACKGND); tft.setCursor( 80, 140); tft.print("Charge the battery!!!"); } Serial.print("bvolts = "); Serial.print(bvolts); Serial.print(" Mylevel= "); Serial.print(Mylevel); Serial.print(" level= "); Serial.println(level); // draw the bar graph // write a upper and lower bar to minimize flicker cause by blanking out bar and redraw on update tft.fillRect(x, y, w, h - level, WHITE); tft.fillRect(x, y + 260 - level, w, level, MyBar); // write the current value tft.setTextColor(GREEN, BACKGND); tft.setTextSize(2); tft.setCursor(x +30 , y - h - 23); tft.println(Format(curval, dig, dec)); // talrække lodret } // ******************** END of DrawBarChartV ****************** // /**************************************************************** Purpose: To format any double number. ****************************************************************/ String Format(double val, int dec, int dig ) { int addpad = 0; char sbuf[20]; String condata = (dtostrf(val, dec, dig, sbuf)); int slen = condata.length(); for ( addpad = 1; addpad <= dec + dig - slen; addpad++) { condata = " " + condata; } return (condata); } // ****************** END of Format ************************** // // ******************************************************** // END of PROGRAM // ******************************************************** //