Programming: Arduino

Check out my other articles:


ADC Graph

After writing a few games I thought it was time to write something a little more useful and an ADC Graph application seemed like the perfect idea. Inspired by the oscilloscope project that comes with the SmartGPU2 sample code, I set about to build something that was both useful and took advantage of the SmartGPU2's screen. The end result works on both the 3.5" 480 x 320 and 4.3" 420 x 272 screens.

The Arduino UNO comes equipped with four 10bit analogue to digital converters that convert a signal between 0V and 5V to a corresponding number between 0 and 1023. More advanced Arduino models sport better analogue to digital features than the Uno - the Mega comes with 16 x 10bit ADCs whereas the Due comes with 12 x 12bit ADCs (where 12bit provides a range from 0 to 4095). The code below code easily be converted to support these models if needed ..


ADC Graph in Action

Right from the start I wanted the application to be configurable - allowing the user to select the number of series to display and to be able to adjust key components of the graph overall and the series individually. When the application starts, the user is presented with a setup screen as shown below.

The screen is split into two major sections that allow configuration of the graph itself (on the right) and up to four data series (on the left). The series correspond to the analogue pins 0 - 3 on the Arduino and can be enabled or disabled by clicking the checkbox adjacent to the series name. The series name can be altered by selecting the heading, the colour changed and the graph type changed between a bar or a line graph.

Δ Top

When altering the series name, a keyboard pops up allowing entry. This functionality is based on the keyboard sketch provided in the sample code with the SmartGPU2. I have changed the code to present the keyboard as a pop-up as well as fixed a few minor bugs.

Δ Top

Once configured, the data collection and graphing can be launched by clicking the OK button. Depending on the options chosen, a legend will be draw on the right hand side of the screen with details of each series - including the input voltage and digital equivalent value between 0 and 1023 - updated in real time.

A single graph can have a combination of bar and line graphs, as shown below. Optionally, the data can be offset by 2.5V in the Y axis effectively changing the scale from -512 to 512.

Δ Top


Code Fragments

The following code highlights areas of interest in the application. It is not intended to be a tutorial or a complete solution. I have provided the complete code for download and you may use or modify it as required.


A Better Mouse Trap?

The SmartGPU2 library comes with a collection of GUI components - checkboxes, sliders and so forth - so why did I feel the need to rewrite some of them? The answer is that the standard controls all render in white and I wanted to be able to render them in grey to indicate that they were disabled. Secondly, the control set does not include a radio button which I wanted to represent a set of mutually exclusive options and - finally - the provided checkbox does not render well at its smallest size (16 x 16 pixels).

The screen images above show the effect that I was after. When an option (Sereis 3 and 4 in the left hand image) is selected the sub options are rendered in a light colour to indicate they too are available for selection. When the option is deslected, the sub options are greyed out and unavailable to select.

Δ Top

A Better Checkbox

I decided to make my controls fixed sized - unlike the SmartGPU2 versions that are scalable - and this made the task really simple, as shown below. The draw_objCheckbox() function accepts parameters for the upper left coordinates, the rendering colour and the checkbox state. The function draws the outer square (lines 003 - 004) and if selected the check mark (lines 008 - 014). If the checkbox is unchecked, the centre of the square is cleared to black (line 019 - 020) allowing the checkbox to be redrawn over itself when its state is changed.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
void draw_objCheckbox(int x, int y, int colour, ACTIVE isActive) {
    
  lcd.drawRectangle(x + 1, y + 1, x + CHK_SIZE - 1, y + CHK_SIZE - 1,  
                    colour, UNFILL);
  
  if (isActive == SELECTED) {
  
    lcd.drawLine(x +4, y +3,x + CHK_SIZE -3, y + CHK_SIZE -4,colour);
    lcd.drawLine(x +3, y +4,x + CHK_SIZE -4, y + CHK_SIZE -3,colour);
    lcd.drawLine(x +4, y +4,x + CHK_SIZE -4, y + CHK_SIZE -4,colour);
  
    lcd.drawLine(x +3, y + CHK_SIZE -4, x + CHK_SIZE -4, y +3, colour);
    lcd.drawLine(x +4, y + CHK_SIZE -3, x + CHK_SIZE -3, y +4, colour);
    lcd.drawLine(x +4, y + CHK_SIZE -4, x + CHK_SIZE -4, y +4, colour);
    
  }
  else {

    lcd.drawRectangle(x + 3, y + 3, x + CHK_SIZE - 3, y + CHK_SIZE - 3, 
                      BLACK, FILL);    
  }

}

Detecting that the checkbox was clicked and toggling the value can be performed by the code below. The checkbox state is stored in a variable of type ACTIVE (line 001) which can hold a value of SELECTED or DESELECTED. If the checkbox is clicked, the value is toggled (line 005) and the object redraw (line 006).

001
002
003
004
005
006
007
008
ACTIVE chkValue = SELECTED;

if (checkbox clicked)   { 

  chkValue = (chkValue == SELECTED ? DESELECTED : SELECTED);  
  draw_objCheckbox(x, y, chkValue); 
  
}
Δ Top

A Better Radio Button

The draw_objRadioButton is simplicity itself. The outer circle is draw in the selected colour but not filled - the inner circle is drawn in a solid colour - either the nominated colour or black - depending on the provided state. Like the draw_objCheckbox() function, this object can be drawn over itself as its state is changed.

001
002
003
004
005
006
007
void draw_objRadioButton(int x, int y, int colour, ACTIVE isActive) {
 
  lcd.drawCircle(x + 8, y + 8, 8, colour, UNFILL); 
  lcd.drawCircle(x + 8, y + 8, 5, 
                 (isActive == SELECTED ? colour : BLACK), FILL); 
 
}

Sample code to render and update a group of radio buttons is shown below. Lines 003 - 009 defines a function to render a set of radio buttons whose value is stored in the value optValue. When rendering each radio button, the value of the variable is compared to its ordinal value in the group to determine whether to render the radio button in a selected or deselected state

When a radio button is clicked, the optValue variable is updated with the index of the radio button in the group and the group itself redrawn.

001
002
003
004
005
006
007
008
009
010
011
012
013
int optValue = 0;

void draw_objRadioButtons() {

  draw_objRadioButton(x0, y0, (optValue == 0, SELECTED, DESELECTED);
  draw_objRadioButton(x1, y1, (optValue == 1, SELECTED, DESELECTED);
  draw_objRadioButton(x2, y2, (optValue == 2, SELECTED, DESELECTED);

}

if (radio button 0 clicked)   { optValue = 0; draw_objRadioButtons(); }
if (radio button 1 clicked)   { optValue = 1; draw_objRadioButtons(); }
if (radio button 2 clicked)   { optValue = 2; draw_objRadioButtons(); }
Δ Top

A Better Colour Picker

The draw_objColourPicker is a control I made up that allows a user to select a colour from a palette. It is simply rendered as an outer border with a solid, colour swab in the middle. When clicked, the colour selection palette is revealed (see below).

001
002
003
004
005
006
007
void draw_objColourPicker(int x, int y, int border, int selected) {
  
  lcd.drawRectangle(x, y, x + CHK_SIZE, y + CHK_SIZE, border, UNFILL);
  lcd.drawRectangle(x + 2, y + 2, x + CHK_SIZE - 2, y + CHK_SIZE - 2, 
                    selected, FILL);

}
Δ Top

The colour picker displays a palette of colours in a 4 x 3 grid with the existing, nominated colour pre-selected. The chooseColour() function is invoked when one of the four colour pickers are selected. In addition to the display coordinates the series number is passed and this is used to determine what colour to highlight initially (lines 014 - 021). The colour swabs are rendered in lines 026 to 043 and then the selected cells is highlighted in lines 048 to 052 via a call to the highlightColour() function (lines 001 - 006).

The code then loops waiting for the user to select a colour (lines 054 - 079) before breaking out of the loop. Finally, the chosen colour is reassigned to the correct series variable (lines 081 - 088) before returning.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
void highlightColour(int x, int y) {

  lcd.drawRectangle(x - 3, y - 3, x + 25, y + 25, GREY6, UNFILL);
  lcd.drawRectangle(x - 4, y - 4, x + 26, y + 26, GREY6, UNFILL);
  
}

void chooseColour(int x, int y, int series) {

  int selectedColour = 0;
  point.x = 0;
  point.y = 0;
  
  switch (series) {
    
    case 1:  selectedColour = series1_Colour;  break;
    case 2:  selectedColour = series2_Colour;  break;
    case 3:  selectedColour = series3_Colour;  break;
    case 4:  selectedColour = series4_Colour;  break;

  }


  // Draw border and drop shadow .. 
    
  lcd.drawRectangle(x, y, x + 128, y + 98, BLACK, FILL);                      
  lcd.drawRectangle(x, y, x + 128, y + 98, GREY6, UNFILL);
  lcd.drawRectangle(x + 129, y + 3, x + 131, y + 98 + 3, GREY3, FILL);
  lcd.drawRectangle(x + 3, y + 99, x + 131, y + 101, GREY3, FILL);


  // Draw first row of colours ..
  
  lcd.drawRectangle(x + 8, y + 8, x + 30, y + 30, WHITE, FILL);              
  lcd.drawRectangle(x + 38, y + 8, x + 60, y + 30, RED, FILL);               
  lcd.drawRectangle(x + 68, y + 8, x + 90, y + 30, GREEN, FILL);             
  lcd.drawRectangle(x + 98, y + 8, x + 120, y + 30, BLUE, FILL);             


  // Draw second row of colours ..
  
  lcd.drawRectangle(x + 8, y + 38, x + 30, y + 60, YELLOW, FILL);            
  ..         


  // Highlight the selected colour ..

  if (selectedColour == WHITE)     { highlightColour(x + 8, y + 8); }
  if (selectedColour == RED)       { highlightColour(x + 38, y + 8); }
  if (selectedColour == GREEN)     { highlightColour(x + 68, y + 8); }
  if (selectedColour == BLUE)      { highlightColour(x + 98, y + 8); }
  ...

  while (true) {
    
    if (lcd.touchScreen(&point) == VALID) {  

      if (point.x >= x + 8 && point.x <= x + 30 && 
          point.y >= y + 8 && point.y <= y + 30)  
          
               { selectedColour = WHITE; break; }
          
      else if (point.x >= x + 38 && point.x <= x + 60 && 
               point.y >= y + 8 && point.y <= y + 30) 
                              
               { selectedColour = RED; break; }
               
      else if (point.x >= x + 68 && point.x <= x + 90 && 
               point.y >= y + 8 && point.y <= y + 30)                
               
               { selectedColour = GREEN; break; }
               
      else if ..

    }      
 
    delay(100); 
  
  }
   
  switch (series) {
    
    case 1:  series1_Colour = selectedColour;  break;
    case 2:  series2_Colour = selectedColour;  break;
    case 3:  series3_Colour = selectedColour;  break;
    case 4:  series4_Colour = selectedColour;  break;

  }
  
}

As with the checkbox and radio button, the code for drawing the colour pickers is simple with all four controls being refreshed simultaneously. When the user selects any of the colour pickers, they are presented with the selector and on exit the four controls are refreshed. Simple.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
int series1_Colour = RED;
int series2_Colour = BLUE;
int series3_Colour = GREEN;
int series4_Colour = YELLOW;

void drawPickers() {

  draw_objColourPicker(x1, y1, GREY7, series1_Colour);
  draw_objColourPicker(x2, y2, GREY7, series2_Colour);
  draw_objColourPicker(x3, y3, GREY7, series3_Colour);
  draw_objColourPicker(x4, y4, GREY7, series4_Colour);

}
 
if (colour selector 1 checked)  { chooseColour(x, y, 1); drawPickers(); }
if (colour selector 2 checked)  { chooseColour(x, y, 2); drawPickers(); }
if (colour selector 3 checked)  { chooseColour(x, y, 3); drawPickers(); }
if (colour selector 4 checked)  { chooseColour(x, y, 4); drawPickers(); }
Δ Top

Possible Improvements

Where to start as there are so many possible improvements that could be made .. the code is quite complete but could do with many enhancements. If anyone wants to complete any of the ideas below, I am happy to incorporate them into the distributable with full credits to you!

  • Save the configuration to the EEPROM. Obviously if someone was using this code for a real application (!) they would want the configuration they set initially to be applied after a restart. A quick look at the code and accompanying code sampled in Sudoku game will show you how to do this. Additional code in the Farkle code shows how to save and restore string / character arrays to the EEPROM.
  • Allow the Y scale to be adjusted. I have assumed that signals will take advantage of the full 0V to 5V range however you may be measuring smaller signals that range from say 0V to 3V. The ability to adjust the scale of the Y axis would allow these signals to be rendered across the full height of the screen.
  • Provide two rendering patterns - the existing approach which repaints the graph while shifting it to the left for each new data point or update the screen as an oscilloscope does by progressively updating the screen from left to right as data is read. This approach can be seen in the oscilloscope project that comes with the SmartGPU2 sample code. This second approach removes a lot of the flashing as the screen is repainted as only a small section of the screen is refreshed each time.
Δ Top

Wiring Diagram

The diagram below show the wiring diagram when using a standard Arduino Uno (or compatible) and the SmartGPU2. Click on the image to see a detailed drawing.


Δ Top

Downloads

Feel free to download, modify and adapt this code to your needs. Please credit me if you use some or all of this code in your own project. If you alter the code to fix bugs or add functionality, please submit them back to me and I will add them to the download.

   Arduino Source Code
Δ Top

Comments?

 Anything to say?
Name :
Email :
Comments :
Confirmation: