User Interface (Part 1)

Part 1234

The goal for this user interface is to use the least number of pins from Arduino, leaving as many pins as possible for the application itself.

Taking advandage of my early works on rotary encoders I decided to use this type of control in addition to an LCD display.

This type of combination involves the design of a menu driven application. The principle of operation is fairly simple. A click on the encoder calls the main menu on display: the first line tells about the menu content; the menu items are scrolled with the encoder on the second line. An other click selects the current menu item. And so on. The last scrollable menu item is reserved for the EXIT command for returning to upper menu levels.

For the purpose of testing the interface, I designed a basic blinker application which can be controlled in terms of

  • start/stop blinking
  • blink cycle duration
  • cycle modulation

The critical components from the interface are located in the following routine:

void commandProcessor(void) {
	int lastCounts;
	int counts;
	boolean firstCount = true;
	if (Encoder.buttonDown()) { // Read push button status (Ground true => 0=down)
		while (Encoder.buttonDown()); // Do  nothing while button is depressed
		// Display menu
		LCD.printString(MSG_FCT_SELECT, 1);
		counts = 0;
		Encoder.setCounter(counts, 0, 3);
		firstCount = true;
		while (!Encoder.buttonDown()) { // While button is not depressed
			lastCounts = counts;
			counts = Encoder.getCounter(); // Get counter value	
			if ((lastCounts != counts) || firstCount) { // If counter has changed since last display
				firstCount = false;
				LCD.printString(FunctionIndexToString(counts), 2);
			}
		}
		while (Encoder.buttonDown());  // Wait button release
		switch(counts) {
		// Run control		
		case 0: 
			LCD.printString(MSG_FCT_RUN_CONTROL, 1);
			counts = 0;
			Encoder.setCounter(counts, 0, 1);
			firstCount = true;
			while (!Encoder.buttonDown()) { // While button is not depressed
				lastCounts = counts;
				counts = Encoder.getCounter(); // Get counter value
				if ((lastCounts != counts) || firstCount)  { // If counter has changed since last display
					firstCount = false;
					LCD.printString(RunControlIndexToString(counts), 2);
				}
			}
			while (Encoder.buttonDown())  // Do  nothing while button is depressed
			run_state = counts;
			break;
		// Function: cycle duration
		case 1: 
			LCD.printString(MSG_FCT_DURATION, 1);
			counts = tim_cycleDuration;
			Encoder.setCounter(counts, 100, 10000);
			firstCount = true;
			while (!Encoder.buttonDown()) { // While button is not depressed
				lastCounts = counts;
				counts = Encoder.getCounter(); // Get counter value
				if ((lastCounts != counts) || firstCount) { // If counter has changed since last display
					firstCount = false;
					counts = lastCounts + pow((counts - lastCounts), 3); // Non linear counter (counts booster): Powering to 3 prevents from caring about the sign
					Encoder.setCounter(counts); // Update counter
					// Display frequency
					LCD.eraseBuffer();
					LCD.insertInteger(counts, 5);
					LCD.insertString("ms", 7, true);
					LCD.printBuffer(2);
				}
				delay(50);	
			}
			while (Encoder.buttonDown()); // Wait for button release			
			tim_cycleDuration = counts;
			computeDurations();
			break;
		// Function: on/off ratio
		case 2: 
			LCD.printString(MSG_FCT_ON__OFF_RATIO, 1);
			counts = tim_onOffRatio;
			Encoder.setCounter(counts, 1, 99);
			firstCount = true;
			while (!Encoder.buttonDown()) { // While button is not depressed
				lastCounts = counts;
				counts = Encoder.getCounter(); // Get counter value
				if ((lastCounts != counts) || firstCount) { // If counter has changed since last display
					firstCount = false;
					Encoder.setCounter(counts); // Update counter
					// Display frequency
					LCD.eraseBuffer();
					LCD.insertInteger(counts, 5);
					LCD.insertString("%", 7, true);
					LCD.printBuffer(2);
				}	
			}
			while (Encoder.buttonDown()); // Wait for button release			
			tim_onOffRatio = counts;
			computeDurations();
			break;
		// Exit
		case 3: 
			break;
		}		
		LCD.printString(MSG_FIRMWARE, 1);
		LCD.printString("                ", 2);
	}
}

the

commandProcessor();

command shall be inserted in the loop() routine.
You can download a compatible version of my PlainEncoder Library from here. Any LCD library would work.

Conclusion
This interface uses only 6 I/O ports for the LCD and 3 I/O ports for the encoder, which leaves 8 + 3 I/O Ports (Or even 2 more if you consider using PORTD0:1) for the application. There is surely room for code improvment (e.g. Storing menu item strings in EEPROM instead of RAM), however, it works great for simple applications.

Next Post on same subject

3 Comments

  1. [...] http://arduino.cc/playground/Main/RotaryEncoders http://www.arduinoos.com/?p=643 Gefällt mir:Gefällt mirBe the first to like this. Dieser Beitrag wurde unter Arduino, Hardware [...]

  2. e-van says:

    Hi, great site! sorry, but where i found de PlainEncoder Library? The above link say “ERROR 404 – NOT FOUND”.
    Thanks!

Leave a Reply

You must be logged in to post a comment.