Text-based User Interface library new release

Text area with scroll bar to the right, up/down buttons scroll the text:

Top menu with current/total item indicator on top right (you can put it anywhere) and arrow/dot indicator or highlighted item. Indicator is customizable:

Top menu with 12345 index and the arrow is the indicator of the highlighted item

A sub menu:

I'm happy to release a new version of the phi_prompt Text-based User Interface (TUI) library, which I have been working on lately.

This new release has the following features that helps an Arduino project developer quickly whip up a professional-looking user interface, that otherwise would take at least weeks to get if starting from scratch. Using the library, you essentially decouple coding your project's functions and coding the user interface. Like when you program in Java, you wouldn't be making buttons and lists but using buttons classes and list classes instead. You can have phi_prompt handle the user interface with lots of customizable features anytime you want it and handle it your own way at other times if you want. Here are what phi_prompt offers:

  • Supports multiple-level menu. See the menu example code how to construct a menu from a list of menu items. Use my template to save you time.
  • Supports display of lists with a variety of customizable features.

This is the option byte:

Scrolling bar	Center choice	Flashing cursor	Auto scroll	Current /total	Index list 1 thru 0 	Arrow/dot item indicator
  • Supports text areas to display long messages. You don’t have to clip your messages in small chunks to display on 16X2 or 20X4 displays. Text areas do that for you. The user can use up/down keys to scroll up and down to read the entire message. This version has text_area_P(), which directly displays message from PROGMEM to LCD, saving tons of RAM.

  • Scrolling texts horizontally if you want to display long messages on one line or if you have a long item on a list that won’t fit on one line.

  • Function to display texts automatically center-aligned. No need to count characters.

  • Supports integer entry, floating point number entry, and text entry. Easy to construct a password panel or ask user to enter a file name. Use my template to save time.

  • Lists are stored in PROGMEM, saving RAM for variables. Long messages can also be printed from PROGMEM.

  • Displays a vertical scroll bar anywhere you want.

  • Displays yes/no and ok dialog that automatically scales to the size of your screen.

  • Collect user button presses with wait_on_escape(). You don’t have to write your own code anymore. The return value tells you which button was pressed.

  • Showing off your project with professional-looking interfaces!

Here is a video showing off most of its functions. The 20X4 display is definitely nice: I'll add annotations soon.
More clear video from today:

Somewhat unclear video a few days ago:

This is a more complete video:

I've uploaded the library to my blog but yet need time for complete documentation.

Really nice, love it!
This is going to save me a lot of time!

But does this work on graphic LCDs too?

Neat !!

Planning to use this library in the future.
Will follow the development and made a bookmark to your blog..

Thanks for sharing

Soranne:
Really nice, love it!
This is going to save me a lot of time!

But does this work on graphic LCDs too?

As I was re-writing my codes, I found I used the following functions from the standard LiquidCrystal library:

clear() to clear screen
setCursor() to set location of the output
print(char*) to print a string of characters
write(char) to print one character
and createChar(char*) for scroll bar
blink() and noBlink() for blinking box

So if you use a GLCD, does the GLCD support these above functions? I'm planning to port my library to KS0108-compatible displays but need some help figuring out what the GLCD can do.

NietGiftig:
Neat !!

Planning to use this library in the future.
Will follow the development and made a bookmark to your blog..

Thanks for sharing

Hey you're welcome. I've spent many hours on this so may as well share it. I will write more documentation as I go. I still have a few things I would like to implement for the next revision but won't cause major change of function calling protocol anymore. I got many suggestions from the forum on features to include so feel free to ask!

Added pictures.

Dude... this is INCREDIBLE! Seriously. Awesome work.

I think you'll be delighted to know I'm planning on implementing phi_prompt in what could be Arduino's single most real-world-useful with mass-market-appeal project I've still ever seen an Arduino do: in the OpenGauge MPGuino Revision 2 project. The UI of MPGuino is just butt-ugly and near unusable, and my focus is on maintaining its rock-solid stability and incredible accuracy, and even keeping the existing hardware design (since the original developer isn't really on-board with the project since I'm taking it back to its Arduino roots)... but wholly gutting and rebuilding its user interface.

Take a look: Release Two Workspace (/Fork name TBD) - Fuel Economy, Hypermiling, EcoModding News and Forum - EcoModder.com

I had a little bit of difficulty starting off, when I first played around with it in conjunction with a shift-register-based LCD driver library. The library is hard-coded to accept a LiquidCrystal object, but not only that, even if I try to use something else, it still tries to #include LiquidCrystal.h anyway (which "can't be found" if I don't #include it in my own program anyway - so why have the reference?). So I had to edit the actual library code to swap LiquidCrystal for ShiftLCD... was kindofan uncomfortable thing to do, so after testing it and seeing it worked great, I switched it back to LiquidCrystal... but it'd really be nice to have some way to add the LCD library functionality in there. Maybe leave them undefined, and rely on the program to #define the function names itself? Like:

#define phi_lcdPrint mylcd.print
#define phi_lcdClear mylcd.clear
#define phi_lcdSetCursor mylcd.setCursor
#define phi_lcdWrite mylcd.write
#define phi_lcdCreateChar mylcd.createChar
#define phi_lcdBlink mylcd.blink
#define phi_lcdNoBlink mylcd.noBlink

... or maybe in an array in the same way as passing the buttons:

phi_lcd *lcd[] = {mylcd.print, mylcd.clear, mylcd.setCursor, mylcd.write, mylcd.createChar, mylcd.blink, mylcd.noBlink};

I think that'd help make it more flexible for different LCD formats, which I've seen has been a bit of a hangup for some people :wink:

Little LCD quirks aside, I think this is a truly incredible little library, and I can't wait to see what it can do for our little project on my car's dash :smiley:
edit: woops, wrong link, it's opengauge.org :blush:

FalconFour,

I'm glad that you found my library useful. I am trying to make a text-based user interface that the Arduino platform absolutely needs. I believe too many hours have been spent on many peoples' project interfaces where they can be better spent on the project functionality.

I am thinking about porting my library to GLCD and possibly ShiftLCD. Your way of #define is interesting. I could expand it into something like this in case other lcd libraries have different parameter orders or different number of parameters:

#define phi_lcd_setCursor(x,y) lcd.setCursor(x,y) // For standard LCD
#define phi_lcd_setCursor(x,y) glcd.CursorTo(x,y) // For KS0108 GLCD
#define phi_lcd_setCursor(x,y) ShiftLCD.something(x,y) // For shift register LCD, I don't know too much about it yet.

I got a few questions for you :slight_smile:

Which Shift LCD version are you using, link?
What functions do you use in the library?
Anything you wish the library had?

Just in case your project code is kept proprietary instead of released to the public for educational and hobby use, please contact me about licensing. My library is free for educational and hobby use and its credits within the files should be kept.

Of course :smiley: The only reason I'm able to do work on it is because it's open-source... otherwise the guy doesn't seem like he'd be too willing to let other people mess with it, haha... several people have made attempts at extending and updating the interface, but none have really made a fully functional "new version".

So far I haven't really had a chance to give it a go, as I've been busy trying to consolidate the existing code and come up with a good program structure that still allows the timing to have its way (e.g. updating stats on a regular basis, maintaining the software RTC, checking for system-idle conditions, etc... basically "doing things in the background"). It's kind of a monolithic mess right now, everything in one huge file that compiles to about 18kb of code on-chip. That's with the old UI, though. After gutting the old UI down to its monitoring/processing core (with no output), it only takes about 12kb. Still a lot!

I originally started with this shift LCD code: Arduino Adventures: Controlling an LCD Display with a Shift Register - but I wanted an 8-bit interface (since the shift register is an 8-bit chip, it just makes sense). I don't mind carrying 4 pins: RS, Enable, Clock, and Data (my shifter doesn't have a latch, only reset and A/B, I only use A). So I figured, if it shifts 8 bits out with every shift, it'll never get out of sync. I tweaked the hell outta the code, and tested it. I was right: shifting 8 bits at a time in a tight, no-delays loop updating the LCD using only "lcd.setCursor(0,1); lcd.print(micros(),DEC);", no timing errors and no need for latch/reset. But it's just a modification-of-a-modification of LiquidCrystal, so nothing's really different there, other than the name of the class (ShiftLCD instead of LiquidCrystal). To make it work, I just had to change LiquidCrystal to ShiftLCD in the phi_prompt code :wink:

As for what I wish the library had, well... I haven't dabbed much in it so far, but it was a bit of a PITA to get the demo phi_prompt_example_menu code to work. After I got ShiftLCD working, the initial credits page went bonkers... it'd start on the first line, go to the next automatically (Ooh, autoscrolling? Neat!), then again a bit quicker... then again and again and (whoa whoa, hey what?) again and agaignaginagianblblblblbl(end)... om... that's odd. I went in and tried to find the button definitions in the example, but there was nothing to be found (it looked pretty... desolate). Odd. OK, so I poked around the other files and found where the buttons seem to be defined. I changed them to my pins, and it... well, didn't help. Changed to 4-button mode (I only have 4 buttons on my test board), and it did something else: blippiting through the menus like I'm holding "OK" down. Nooot quite right. Then I found the definitions were in 2 places: one for phi-1 and one for phi-2 shields. Urk? Hmm, well, OK, I comment out the #define phi_2_shield line and copy over the button/buzzer definitions to my appropriate values. Bam, everything worked. But it was a bumpy learning curve since I'm used to single-file, monolithic examples and sketches, and this example is sprawled out over 4 files :o

Just thought it'd be worth mentioning my experience... I learned now, but I wonder if there are others going through the same thing when they test it out. =P

As for the functions I plan on using, it'll likely be everything phi_prompt has to offer... I'll need menus as the primary means of navigation and configuration, number entries to enter the engine/timing parameters (stored in EEPROM), and I might need to tweak it a bit to extend it to setting the software RTC (that was a PITA to implement in the old code - the configuration page, that is). The primary "monitoring" display is probably going to be a modified selection list, which may get to be a curious affair. It's supposed to update the display twice a second, and I was hoping to be able to use the arrow/select keys to pick a parameter on-screen to change, as it runs. So, in normal running mode (the mode all modes will fall back to on input timeout or "return to main"), it'll have 4 parameters on-screen, updating every 500ms. The cursor would select a parameter on this screen, and when you select it, you get a list of alternatives you can put in its place (e.g. swap from miles/hr to current MPG). There's also a "big font" mode that shows a single parameter across 2 rows in large font, like phi_big_font, only for numbers, and with a bit smoother-looking custom character set (which I'm sure you can borrow, it was a user-contributed mod anyway). All this should be selectable from the main menu, which I hope to activate by holding the select/OK button for >1s. And when the device is idle (no engine/speed pulses) for over 5 minutes, it dims/shuts off the light (configurable) and displays an idle screen with a clock and a 4x2 animation of some kind using custom character mapping (all pixels of 4x2 are capable of animation by placing characters 0...7 on-screen and tweaking the custom characters - just gotta figure out how to create the animation effect).

All in all, it seems like phi_prompt really saves about 60-75% of the daunting work I was going to have to do... I barely even knew where to begin with the new interface. I was planning on making it some sort of extensible tab-stop-based system, where each "screen mode" had a list of tab stops where the cursor would stop when you use the left/right buttons, and when you "select", that tab stop would be passed to a handler function for that screen that would perform the intended action (change the screen, update a number, make a selection, etc). I just got hung up on the whole... you know... "doing it" thing. Making a servo play Fur Elise seemed more entertaining. :wink:

I need user inputs like yours to improve the library and its examples. I wrote the button definitions all according to my phi-2 shield hardware so I don't know what problems others encounter until they try the library. Now I'll put "better example code" on my revision notes. Initially you must have defined the up button as auto_button. It's a thing one member requested so minimal input pins can be used. You can use as few as one button to operate the entire thing if you set the "right button" as the only real button with a digital pin, then the up button as auto button, and other buttons as null_button. This way your program doesn't have to be re-vised if you want to lose a few buttons to save pins :slight_smile: I guess it worked out to be an obstacle for first timers. I'll make a note in my documentation, which is 75% complete. My documents are usually very detailed so they take forever to finish.

I had a phi-1 shield before the phi-2 shield. Unfortunately two pins had to be swapped between the two versions to make the phi-2 shield compatible with Ethernet shield with sd card, thus the defs. You can ignore them all and make your definition in your main program. Monolithic program is not good for 18K of code, it's about 1,800 lines. I rather break it down into small modules so if I had time I would periodically revise each small module. Plus, Arduino IDE is not built to handle long codes. You can't fold codes. I do have tricks to help you though:

There are a few posts on organizing your program, optimizing and my trick of the trade included as the very first post. You'll like it.

BTW, in my near future quick add-on update (a few functions that you can use to simplify your programming), I will have things like:
text_area_maximize(Char* long_message) so all you need to do is to supply a string and the text area is maximized on the display.

You're using 16X2 display, right? A text area is really nice on 20X4 display :slight_smile:
I will also have simple functions like float get_number(char * prompt_message) so you will have a generic input panel with a line of prompt like "Input speed:" and the function returns a number. It's simple. I'll like it if I were beginner or don't care where exactly the number is displayed. With the full input_integer you can have a series of inputs on one screen for all date and time (see my original phi-1 shield alarm clock videos). But if you want simplicity, you can call a get_number function :slight_smile:

Oh, trust and believe, if I weren't sweating every $30 purchase, I'd've bought a Phi-2 shield in a heartbeat. Unfortunately my primary development platform on Arduino is my Radio Shack "electronics learning lab" board, which is amazing for Arduino development after adding an LCD and using a USB BoArduino from Adafruit... but not shield-compatible, so whenever I need to use a shield, I have to break out the Duemilanove and juggle wires with a ScrewShield. Maybe in the near future, though... in fact, the Phi-2 shield MAY actually be a perfect fit for the MPGuino platform, as a user could just wire up the VSS (vehicle speed sensor) and injector inputs on a protoshield and stack the Phi-2 on top of it... bam, a perfectly clean MPGuino build, backlit LCD, buttons, and all! I'd have to make an alternate build for it with different pin definitions, but I think it should work great, especially with the added buttons. The MPGuino board uses a 20MHz clock, though, which is an arguably silly modification, but just needs a few parameter tweaks (I still haven't found if the clock delays are accurate with the Arduino codes, needs a little more testing).

I also think it's a good idea to split the code up into multiple files, which I'll spend a bit of time on with MPGuino to make it more readable. As it is now, it has very clearly defined "segments" of the program (the counters, the trip classes, the user interface, math functions, etc), that could very easily be branched out to separate files for readability. That's probably the first thing I'll do, if just so I could avoid wearing out my scroll wheel so much! It's seriously a PITA to look for a function in that code.

I'm just curious, though... do you do everything in the Arduino IDE? I hear a lot about people using alternate IDEs with Arduino, and with how frustrating Arduino can be sometimes, I wonder if it's worth a try. Like, if I want to reference another project to remember how I did something ("was it PORTD or PORTB? Or is it PIND?", "what pins do I need to use here?", etc), I can't just hit File-Open, because it unpredictably wants to close my current project to open that one, so I often have to open another Arduino instance then open the other project. Or god, the "Open" menu, after I've collected a number of projects. First, the examples went off the bottom of the screen, and I have to use the "Examples" submenu (which, itself, is timed to close WAY too fast, so as I zip my cursor down to grab a lower library name, it closes the menu on me). Now, even my sketches list is going off the end of the screen. Augh! =P

edit: Oh, and yeah, all I've got are 16x2 LCDs... one (without backlight) wired up in my Radio Shack board, one in the MPGuino device in my car, and one on its way from Adafruit right now. I keep forgetting there are more styles available when I shop for 'em, haha... I really should try out a 20x4 LCD. :slight_smile:

Well, here at 3am, thought I'd share my first project experience with phi_prompt...

... I originally started at about 6pm :frowning:

The biggest issue I ran into... an insurmountable, unbelievably huge issue in my programs' design... is that I can't use phi_prompt AT ALL to select options that are generated or changed in the program. For example, if I calculate some possible values and want to make a 2x2 (2x3, etc) list, I just... literally... can't. The render_list function is hard-coded to retrieve its values from progmem, instead of allowing the flexibility of, say, "is this an int? float? string?", etc., on an item-by-item basis. I think C allows that kind of type detection through pointer-passing, wouldn't you just have to define multiple versions of the function for different data types coming into the function?

I spent about 3 of those hours writing up a hybrid custom-written + phi_prompt system, where phi_prompt still handles that button detection and scrollbar, but all the LCD drawing, action-processing (up, down, select), and item generation are handled by the function's code. It was really quite a bit of work, not the least of which because I was working directly at the bit-level (I used a 16-byte bitmask to store scan results, then the "list" function would iterate through the bitmask and associate the original address with the bit as it loops and I could make a selection as to which address I want to choose.

The other issue was the lack of documentation, but you've already mentioned you're working on that. I had the phi_prompt source open in the background in Notepad++, and I'd constantly go through there and look for button references, what the return values are, how the parameters are interpreted ("step" was the most confusing), etc. That was fun.

However, at the end of it all, there is a HUGE payoff: the first interactive laptop battery interface for Arduino. Using the menu, I can select to read charging statistics like volts, amps, percentage, and temperature (updated twice a second). Or select "Battery ID" and it'll read out the manufacturer, model, and chemistry. "Statistics" shows the manufacturing date and cycle count. "Control" allows you to send hex commands directly to the battery: write word, read word, read block, using input_panel for all values (0-9 & A-F, memcpy into a 0x0000 template and strtoul to convert to "unsigned int"). It has an SMBus scanner that can locate unknown battery controllers' SMBus addresses, and that's the one that was the hardest to implement (the custom UI), and select it for use in the program.

I'll be posting its own topic shortly, as well as the source code (compiles to 16224 bytes, but I should add "credits" back in there, I took it out since I was originally going to make this a really quick-n-dirty adaptation for personal use). It's a heck of a cool thing to play around with! Bust out some old laptop batteries (partially charged of course) and you can read what's going on in its mind :wink:

edit: I also had a huge number of issues with button actions between functions... if I make a menu (list) selection, it immediately passes the action to the select () construct, where it activates the appropriate function... and often does its own button-checking with wait_on_escape(). Problem is, it's usually checking to see if something was pushed. Or held. Hmm. Well, if it's either one ("released" -or- "still being held"), it just zippo's right past the check and flies right into the next action without releasing the button. Errp! So I had to do a lot of this:

while (wait_on_escape(25)) ; // wait for buttons to be up, may have residual press from menu
while (wait_on_escape(500) == 0) ; // wait for button press
return;

Is there a "wait for clear interface" or "wait for any key" function I'm missing? :confused:

Hey FalconFour,

Long replies :slight_smile:

About the RadioShack platform vs. shield: I have a proto-shield that you can use to pass all arduino Duemilanove connections to a breadboard. I'm getting parts so that this shield can be stacked below any shield. This way you get the benefits of shields and breadboards.

This way you don't have to make all those jumper wire and screw blocks connections every time you connect your Duemilanove to the RadioShack platform.

It would be very nice to have the phi-2 shield run the front end of your system. The side of the phi-2 shield has two RJ45 connections to provide you 16 connections to anything. I'm making a 20X4 display version of the shield. Check it out in a few weeks. Besides, it also has a breakout connection to a popular GPS module, nice addition to your system :wink:

I was using Arduino IDE almost 100% time except when I edited my library files since Arduino IDE has no library editor.

You really worked very hard on your project! I only did it up to 2am, only because my wife was prep.ing for exams.

When I first started, I thought about a variable list but I dismissed it as "Not typical for most projects", well, :fearful:
I'll put a RAM list in for the next revision, now that you have done it yourself. Most typically numbers are converted into strings before they become list items since most list implementations I know of return a number from 0 to number of item-1 so I'll add a RAM list but the contents are still your work to create. XD

On the other hand, if you have numbers that are evenly spaced out like 0,5,10,15, you can use integer with step of 5.

Right, the step is a menace right now, due to saving memory. My document covers it but is still not complete =( 20 pages already. I've posted the incomplete version on my phi_prompt page. Check it out.

http://liudr.files.wordpress.com/2011/06/phi_prompt-documentation-20110523-incomplete.pdf

It's awesome that you managed to power through so much difficulty and made phi_prompt work for you! You got to post some videos!!

I wasn't aware that sometimes the buttons didn't release. Did you use internal pull-up resistors for the buttons? I didn't have any problems with tactile switches.

If you have phi_buttons bt1(pin_1,LOW) then the active state is LOW, just FYI.

Yeah, I'd just been geeked out a bit with this whole interface thing... it just makes so much sense, it's practically addicting! Half the problem is still getting the values onto the screen the right way, or adapting the data types to the types it's expecting to see (like a pointer... to an array of pointers... to ROM locations that have the actual data, haha). On several occasions I had to make one-off sketches with serial output just as a quick-and-dirty way to figure out what it's actually returning in its values (Serial.print() is INCREDIBLY reliable and honest).

I've been extending the hell out of the simple little battery editor code... right now it has an address selector as well as auto-scan, a full embedded list of the commands (and descriptions) from the datasheet, timeout detection in many places, a crapload of input-smoothing, and... well... about that commands list! Yeah, I copied the names, codes, and return value modes of each of the ~40-odd controller commands, and put them in one huge 1x2 phi_prompt list. The return value of that menu is mapped to the 2-dimensional PROGMEM array of the commands' addresses and return types (like mAh, percent, string, etc). Then, a big switch() handles each case individually, writing to a buffer that's sent to the LCD at the end. Whoo. Best part is, it works! And people can add their own command sets with their own descriptions (just add new command IDs and add a new case in the tiny helper functions).

It compiles to 19kb now, the biggest Arduino program I'd ever written (which to me is a good thing since the poor ATMega chip never gets to use its upper flash area). 580 bytes in command descriptions and codes. And it seems pretty stable...

About the buttons though, it's not that they trigger on their own (and they're defined properly), it's just that wait_on_escape() exits its control when the button is still held down, which is also what's used to check for inputs on a new menu screen. So if that button's still being held down, it'll still accept that input on the new menu, even if that's within 50ms of pressing the button for the last screen. So I have to do a lot of "wait for the button to be released" hangs, which doesn't affect the UI speed at all (thanks to the way wait_on_escape() works), but just seems like kindova silly functionality-or-structure tradeoff :wink:

I think I'll try to do some videos right now... I just FINALLY got float-to-buffer conversion out of the way (seriously, AVR guys? No standard float-to-buffer function? I can lcd.print, or I can Serial.print, but I can't get that in-between step on its own?) after about 2 hours of tinkering with double and float types... and yeah, I actually got some infuriating compile errors about invalid conversion from double to float, even though it's the same thing here. Yugh. Man, data handling in this thing is a PITA. I'd think the compiler would be able to tell me information about the highest RAM address allocated in the program (i.e. how much memory will be "free") without finding out the hard way (thankfully, I haven't run into that yet).

Oh, and sorry for the long posts... I just like spilling everything. xD

edit: clicks PDF... scrollll... headdesk. Oh, I so coulda used that like 20 hours ago =P

As promised!
http://arduino.cc/forum/index.php/topic,62955.0.html

Now if I just had a way to create a list from generated strings... :wink: Next on my "oh, that'd be cool" list is an SD card interface where we could browse through the folder structure using the LCD... using it with the WaveShield, it could be a "holy crap, a single microcontroller is doing all that?!" party trick :smiley:

I think one way we could use generated lists is with a single buffer delimited by >ASCII characters, like 0xFF. Or a buffer header indicating how many items to render from the buffer. Then use a select method to catch why the function returned (like, was left pressed to go to the next screen? draw more items... or was an item selected? do something with it, etc...). The program could quickly do whatever it needed with the buffer, then loop back to the menu ("phi_prompt_struct.low.i" is keeping track of the position anyway).

The selection list is definitely the most useful part I've seen so far... I don't often have a need to display text, because 32 characters isn't even a full phrase sometimes (plus, without word-wrap, the display admittedly looks kinda goofy), and I don't often need to prompt for a string. I mostly need to pick from a list of options, so I don't always have to watch over the serial console (which is how that battery program was originally designed).

I think the second-biggest request I've got for the library (buffer-generated lists being the first) is that scrolling item selections behave better... right now, the scroll is based on the timer clock, so every item unpredictably jumps around the screen when it's deemed necessary to scroll. Then, it scrolls the whole string off the screen before coming back on, so I often end up seeing "mation"... "ation"... "tion"... "ion"... ugh "on"... "n"... "A"... "Au"... "Aut"... "Auto"... "Autom"... /headdesk. It'd be less of an issue if the scroller left just 3 spaces between the end of the line before looping back over on itself, like "tion | Au"... "ion | Aut"... "on | Auto"... "n | Autom", etc. That'd be a nice fix :slight_smile:

Also, since custom characters can be updated while they're on-screen, creating a sort of animation effect, I think the scrollbar can be very much improved (not putting down what we already have, it's professional fare for most 16x2 devices I'd seen, which is great!) by procedurally generating the scrollbar characters instead of using static custom chars... for example, a custom char consists of 8 bytes of 5 bits each - copy a PROGMEM arrow-tip template to a char array of the same size, then write 0x1F to a series of bytes past the tip to indicate both the size of the content (may have to add another parameter to replace "percent", like "items" and "total"), as well as the scroll position. Then just lcd.createChar(0,modifiedChar); and the screen will immediately update :smiley:

Just a few ideas...

I haven't delved too deep into this yet, but looks pretty cool - should certainly save me a lot of time for something I'm working on at the mo.

Was just wondering if you, or anybody, had tested to see if it was working with the ShiftRegLCD library (this one) using 74HC164 shift reg.

Alright, I've been addicted to Arduino lately, partially as a result of this darn library... haha...

As an avid, gracious, and happy user, I just wanted to post a little list of the issues I'd been having with phi_prompt, mainly out of frustration that as a library I really can't do anything to fix them myself. But in an effort to help along, I'll post the changes I'd like to make to the code to make it work :wink:

  • Menu up/down is reversed.
    um... wat? I know, it's weird. But it's true, as soon as I attached an "up" button to my setup (a little pushbutton on the breadboard), I found out that the menu direction is reversed... I sure don't remember editing this myself! It makes sense to increment a list item when you push "up", but when you're printing a list with item 0 on the first line, 1 on the next, 2, 3, etc., you'd increment to go up the list :wink: Just buttons being reversed, that's all. Lines 657-667 in phi_prompt.cpp, swap case 1 and case 2. :slight_smile:
  • Buttons can't be dynamically reassigned
    This became trouble when I had a program that had 4 buttons, needed a menu input and a number input, with heavy left/right and up/down use. I wanted to reassign L/R to U/D when entering a menu, as I would use the 4th button to exit (up/down -> select "load slot" -> up/down to choose number -> select to perform = 3 buttons). The program itself made use of left/right/select functionality (with "up" as a shortcut). I ended up having to assign the physically-placed left/right buttons as phi_prompt's up/down, and having the program read for up/down in place of L/R. Hackish. I originally tried writing different button pointers to btns[] and calling ini_phi_prompt() but it just crashed phi_prompt (basically). When I enter the selection that redefined the buttons, the whole thing ate itself, dancing through the menus on its own then corrupting the screen. Ick. Not sure how to handle this one, maybe provide a btn_switch() function that reassigns the pointers internally in a controlled manner? This would allow programmers to make better use of limited buttons as would be expected on most implementations, I'd imagine...
  • ok_dialog and yn_dialog only take strings; menus and lists only take PROGMEM
    Kind of a big one. Either we're gonna go with RAM strings or we're going with ROM strings, but unpredictably shuffling them around is kinda... "boo". I had a bit of an undiscovered bug in my program where I did "yn_dialog(PSTR("Error! Retry?"));", and it kinda ate itself when I encountered it. On the other hand, if I need to create a list of items generated in the program, like filling a buffer with values and needing to pick one, I'm SOL, because it's hard-coded to read from PROGMEM. Eep. That should be relatively simple, just add a para->option that selects pgm_read_word or direct access... maybe "if option 1, read values into buffers, else use buffers" sort of thing at the beginning of the function. Looking over it, I think that might reduce complexity overall :wink:
  • code is squished and sprawled a bit...
    OK, this is really just a tiny programmer's gripe, but just figured I'd mention it lightheartedly... just looking over the code while I'm writing this, and I'm getting a headache from the use of two-line "} ....... else ....... {" statements, then right below, a crunched up "lcd->setCursor(para->col+((i-_first_item)/rows)*(para->width+1), para->row+(i-_first_item)%rows)" statement. In an effort to make things as readable as possible (for my future use, at least), I try to collapse standard syntax and expand non-standard crunching. For example, repetitive commands like "lcd.write(' '); lcd.write('/'); lcd.write(' ');", I put on the same line since it's pretty easy to see what's being done. And I try to use single-line while/if/for statements whenever possible (if the conditional statement is one line, you don't need braces). So instead of "for (j=0;jwidth;j++) ........... { ............. list_buffer[j]=' '; ............... }", I could just write "for (j=0;jwidth;j++) list_buffer[j] = ' ';" and save 3 lines for visual flow's sake :slight_smile: It just makes it a lot easier to follow, IMO... especially when trying to debug :slight_smile:
  • Huge functions like sprintf (over 500 bytes) and sscanf (over 1,000 bytes)
    This is kind of an annoyance more than anything, since the program still works fine. Problem is, if a function's even referenced once, it's included in the code sent to the Arduino. So even though we may only use it once in the whole thing, it still includes all 1000+ bytes of code for that function. I'd think there'd be a better way to strip out the integer and decimal parts of the number and recycle the functions used in input_integer. They're nice and all, but I dunno if I can tackle a huge project like mpguino with "only 32kb" of Flash (which now becomes "only" after seeing how big the battery hacker is!). =P
  • Menu index list is left-aligned on right side of screen - can't think of a good way to fix this, so I'm not really too concerned... just gotta work around it (e.g. in long list with 10+ items).
  • phi_prompt_struct is hyooge and confusing...
    I finally found that I can just "setupMenu = mainMenu;" to copy all its parameters into a new menu, which is nice, but I really do take second thoughts about creating a new phi_prompt_struct for a new program. I'm more of the function-parameters kinda guy... I'd rather send a function like "switch (select_list(listPtr, 0,0, 15,1, listIdx, listOptions)) {..." which would present a menu with listPtr's items at 0,0, stretch to 15,1, and have listIdx selected by default with listOptions applied to the menu. I'd do that with no second thoughts... I need to select something, just barf up a quick list (maybe of pointers) and throw select_list at it. I think input_integer is a prime example of this... I should just be able to "int myVar = input_integer(0,100, 7,1, 3, myVar, options)" to prompt for an integer between 0 and 100, located at 7,1 on the display, 3 digits long, with myVar as the initial value, using "options" as the options (like zero padding). Would make it a lot more appealing, I think :wink:

I think that's about it... hopefully this is more of a nodding-in-agreement sort of thing than a ugh-newbie-trying-to-tell-me-how-to-write-software kinda thing! Just throwing this out here since you'd said you wanted feedback from users :slight_smile:

Thanks for all the hard work and hours of debugging... I'm in the same boat with my stuff too! :smiley:

FalconFour,

Thank you! Lot to absorb so I don't attempt to simply reply right away. BTW, I've finished the documentation 33 page long:

I used to do the switch (select_list(listPtr, 0,0, 15,1, listIdx, listOptions)) but I gave it up when writing the phi_prompt library. There are too many parameters and their orders matter unlike the structs with name identifying which parameter gets what value. It might fair better to newbies that may just be intimidated by the enormous list of parameters in the parentheses.

My other problem is whether to do:

result=get_number("Please input number", result);

or do:

get_number("Please input number", &result);

The first one needs no "&" so is newbie friendly but does waste 2 bytes on the stack and the second one is more elegant to more experienced programmers and makes less sense to beginners. I went for the second way but want to revert back with additional "simplified versions" of the more complex versions such as input_integer().