Simple GUI - multiplexed LEDs controlled by two buttons

Hi

I’m currently working on a data logger that uses an external 12bit ADC and 3 internal ADC channels. Before it starts logging the user has to choose the interval time required for logging. and which channels he wants to log. I’m using 16 LEDs multiplexed with a 74HC595 as shown in this tutorial: http://www.instructables.com/id/Multipl … /?ALLSTEPS

Now, the board also uses two buttons. One is a “plus” button and the other one is a “function” button. The plus button is used to toggle between 13 interval time LEDs and then, after accepting with the function button, use 4 out of 13 of these LEDs to represent the desired channel (and toggle in the same way). The other 3 LEDs are the state LEDs.

Here’s how it looks on the PCB side of things:

http://i.imgur.com/pq9nsJx.jpg

and here’s my code so far:

//include the SD card library
#include <SD.h>
#include <SPI.h>
//define SPI interface pins
#define MOSI 11
#define MISO 12
#define SCK 13
#define SD_CS 9
#define ADC_CS 10
//define the internal ADC pins
#define ADC1 A0
#define ADC2 A1
#define ADC3 A2
//define the 74HC595 pins
#define latchPin A3
#define clockPin A4
#define dataPin A5
//define the battery measuring input
#define BATT A6
//define the remaining pins
#define SD_DET 8 //SD card detection
#define SD_WP A7 //SD write protect
//Switches
#define FUN_SW 3
#define PLUS_SW 4

//looping variables
byte i;
byte j;

int ROW1;
int ROW2;
int ROW3;
int ROW4;

//storage for led states, 4 bytes
byte ledData[] = {ROW1, ROW2, ROW3, ROW4};
//define the plus button variables
int plusPushCounter = 0;
int plusState = 0;
int lastPlusState = 0;


int interval;

void setup () {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  
  pinMode(SD_CS, OUTPUT);
  pinMode(ADC_CS, OUTPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SCK, OUTPUT);
  pinMode(MISO, INPUT);
  
  pinMode(BATT, INPUT);
  
  pinMode(SD_DET, INPUT);
  pinMode(SD_WP, INPUT);
  
  pinMode(FUN_SW, INPUT);
  pinMode(PLUS_SW, INPUT);
  
  pinMode(ADC1, INPUT);
  pinMode(ADC2, INPUT);
  pinMode(ADC3, INPUT);
  
  Serial.begin(9600);
  
  delay(100);
  
  if (analogRead(BATT) <400) {
    //TURN ON THE BAT LED
    int ROW4 = 1;
  }
   
  
  if (!SD.begin(SD_CS)) {
    Serial.println("Card failed, or not present");  
    int ROW4 = 3;
    // don't do anything more:
    return;
  }
  Serial.println("Card ready");
  //BUTTON, INTERVAL SELECTION, CHANNEL SELECTION, START LOGGING IN THE LOOP
  
  
  
}

void loop() {
    for (i=0;i<4;i++){
    
    //send data from ledData to each row, one at a time
    byte dataToSend = (1 << (i+4)) | (15 & ~ledData[i]);
      
    // setlatch pin low so the LEDs don't change while sending in bits
    digitalWrite(latchPin, LOW);
    
//    // shift out the bits of dataToSend to the 74HC595
//    shiftOut(dataPin, clockPin, LSBFIRST, dataToSend);
// the code below is the equivalent of the two lines above
    for (j=0;j<8;j++){
      digitalWrite(clockPin,LOW);
      digitalWrite(dataPin,((dataToSend>>j)&1));
      digitalWrite(clockPin,HIGH);
     
    }
}

}

Now I would appreciate some help with my code. How do I actually get the button to toggle between those LEDs? (I want the LEDs to flash when a given interval time or channel is selected for edit and stay on, when you accept it with the function button, also I want them to all turn off when the logging is started to reduce current consumption). I assume all of this has to be done in the void setup () part of the code, as it actually is setting up the data logger before logging, right? The main problem is that it has to toggle like this :

  1. ROW1 = 1, then 2, then 4, then 8 (the corresponding LED is blinking)

  2. ROW2 = 1, then 2, then 4, then 8

  3. ROW3 = 1, then 2, then 4, then 8

  4. ROW 4 = 4 (the other values are the 3 state LEDs)

and go back to the first row.

5.When a user presses the function button it accepts it as the interval, it turns on the LED (stops the blinking) and goes to the channel selection mode

  1. ROW1 = 1 - 1st channel

  2. ROW2 = 8 - 2nd channel

  3. ROW3 = 8 - 3rd channel

  4. ROW4 = 4 - 4th channel

The channel LED should blink, which means it can be edited and stay on when the channel is turned on

Something like this: http://www.youtube.com/watch?v=aF0z0qT7 … be&t=2m24s

  1. After long pressing the function button the data logger starts logging (sets up the SPI interface for the ADC, creates the file on the SD card etc.)

My other concern is, that the code will end up being too big for the 30kB of flash on Arduino UNO.

All help is much appreciated.

What does the ‘log’ state LED indicate ? I’d guess whether it’s logging or not. Once logging how does it get out of that state ? By a button press ? If so you’ve just said your user interface (UI) of button reading must be in the loop(). I don’t think your UI code should be all that difficult nor all that large. How are the switches wired into the Arduino, straight in or in some matrix ?

What I’m thinking is that your interval choice is a state machine. Your buttons are read in and if the + button is still activated and enough time has passed, it’s state advances by 1. Alternately each press and release of the + button advances the state, your choice. The states correspond to the time intervals. You may want to look at this…

http://arduino.cc/en/Reference/SwitchCase

The state can then be mapped into which bits are on/off in your ROWS arrays.

Selection of channels would work similarly, as another SM. Lastly the state of your logger is another SM. It’s states might be; choosing chan, choosing interval, logging, post logging, reset, ??? The state of the logger then determines which of the other SM’s are running.

I’ll think on it some more but the above should give you some ideas.

The LOG LED is supposed to come on for about 200 milliseconds (this might be a problem for the 0.1s interval, but I’ll worry about that later) every time the logger save a measurement. Alternatively just blink the corresponding interval time LED from time to time. This thing is designed to stay on even for a couple of days/weeks. I wanted it to give some sign of life whilst not taking a lot of battery current.

Actually, once the logger starts logging it’s supposed to keep logging until the power is disconnected. The switches are connected to separate inputs like this (SW2 and SW3 go to separate inputs, 3 and 4 to be precise):

http://i.imgur.com/ucgIeL1.jpg

Here’s some “pseudo-code” to illustrate my prior thoughts. See if it works for you. Keep, add, mod or delete as you see fit.

loop(){
  //put code to read, debounce and store button state here
  
  if(millis() - stateTime > waitTime){  //allow a short delay before actually changing logState
    logState = nextlogState;            //so user can see LEDs are correct
  }
  switch (logState){
  case channel:
    //put channel state machine code here
    //use +button to move chan -> chan via chanCtr
    if(plusButton == pressed){
      chanCtr +=;
      if(chanCtr > numChans){  //check for press to loop back
        chanCtr = 1;           //loop back from 4 to 1
      }
    }
    if(funcButton == pressed){
      chanAccept = !chanAccept;  //switch state of channel accepted
      nextlogState = timing;     //move to set timing when done
      stateTime = millis();      //store time when next stated commanded
    }
    switch (chanCtr){
    case 1:
      ROW1 = 1;
      ROW2 = 0;
      ROW3 = 0;
      ROW4 = 0;
      if(chanAccept == true){
        //put code here that sets up channel #1
      }
      break;
    case 2:
      ROW1 = 0;
      ROW2 = 8;
      ROW3 = 0;
      ROW4 = 0;
      if(chanAccept == true){
        //put code here that sets up channel #2
      }
      break;
    case 3:
      ROW1 = 0;
      ROW2 = 0;
      ROW3 = 8;
      ROW4 = 0;
      if(chanAccept == true){
        //put code here that sets up channel #3
      }
      break;
    case 4:
      ROW1 = 0;
      ROW2 = 0;
      ROW3 = 0;
      ROW4 = 8;
      if(chanAccept == true){
        //put code here that sets up channel #4
      }
      break;
    case default:
      //put code here that resets all channels
      //perhaps goes to nextlogState = reset ??
      break;
    }
    break;
  case timing:
    //put interval state machine code here
    //use +button to move interval -> interval via intrvCtr
    if(plusButton == pressed){
      intrvlCtr +=;
      if(intrvlCtr > numIntervals){  //check for press to loop back
        intrvlCtr = 1;               //loop back from 4 to 1
      }
    }
    if(funcButton == pressed){
      intrvlAccept = !intrvlAccept;  //switch state of channel accepted
      nextlogState = logging;         //move to enable logging when done
      stateTime = millis();      //store time when next stated commanded
    }
    switch (intrvlCtr){
    case 1:
      ROW1 = 1;
      ROW2 = 0;
      ROW3 = 0;
      ROW4 = 0;
      if(intrvlAccept == true){
        //put code here that sets up timers to do 0.1s
      }
      break;
    case 2:
      ROW1 = 2;
      ROW2 = 0;
      ROW3 = 0;
      ROW4 = 0;
      if(intrvlAccept == true){
        //put code here that sets up timers to do 0.5s
      }
      break;

      //add cases for other intervals here

    case default:
      //put default code here
      break;
    }
  case logging:
    //put logging code here
    //turn on "log" led
    if(plusButton == pressed){
      //enable timers to start logging
      //turn off "log" led
      }
    }
    if(funcButton == pressed){
      nextlogState = reset;         //move to reset all when done
      stateTime = millis();      //store time when next stated commanded
    }
    break;
  case reset:
    //put reset, if needed, code here
    //turn off all LEDs
    if(plusButton == pressed){
      //disable timers
      ROW1 = 0;
      ROW2 = 0;
      ROW3 = 0;
      ROW4 = 0;
      //do something w/SD card ??
      }
    }
    if(funcButton == pressed){    //allow re-entry into selections w/o resetting all
      nextlogState = channel;         //move back to set channel when done
      stateTime = millis();      //store time when next stated commanded
    }
    break;
  case default:
    //should never get here, do some AFU code
    break;
  }
  //put code to read logState and "accept flags" and set blinking on/off
  //put code to shiftout ROWS array here
}

Thanks, I’ll see how this works for me.