Text-screen-based display and key-based user entry. Now with moving ball.

You can open a terminal emulator in most any OS that will give you fore and background color selections as well as cursor control and yes you can clear the freaking terminal screen with a command.

I experimented a bit just to see if it would work and came up with some functions.

edit: moving ball code is down the page a ways.

byte curL, curC;  // cursor Line and Column

void clrscr()  // clear screen
{
  Serial.print( F( "\033[2J" ));
}

void clr2eol()  // clears from cursor to end of line (newline or carriage return or screen edge?)
{
  Serial.print( F( "\033[K" ));
}

void poscur( byte line, byte col )  // position cursor 
{
  Serial.print( F( "\033[" ));
  Serial.print( line );
  Serial.print( F( ";" ));
  Serial.print( col );
  Serial.print( F( "H" ));
}

void savepos()  // I have no idea if the saves can stack, would be cool.
{
  Serial.print( F( "\033[s" ));
}

void restpos()  // restore the save
{
  Serial.print( F( "\033[u" ));
}

All of these work.

What's possible is that you can put text labels on-screen and display changing data in the same on-screen place and not scrolling up if you never print newline. Whole data-entry screens can store in PROGMEM, clear screen and print X lines at max rate then go to the first item and wait for user entry to complete.

Something a good terminal emulator will do is send keys to serial as they are struck on the keyboard. Arduino reads serial when available one char at a time, each in its own time. The user can press a key besides Enter and the Arduino will know faster than debouncing buttons.

User IO can become instant from the user's POV. When entering a number and o gets typed instead of 0, the terminal beeps and an error name pops up for 1 second then entry picks back up without the o. That's less distracting on the user, quicker back on track.

Only with key-entry can you do a proper auto-complete.

There is likely a terminal capable of large-block custom fonts.

You might make a game on the Arduino that runs on a PC terminal emulator for display and console.

As to the terminal part, on Winblows you have PuTTY and some others. You might need to set PuTTY to VT-100.
Linux comes with a terminal used as a console, it scrolls but it also allows cursor control.

PuTTY does come with docs. It's for Windoze. I used it with XP.

Now I have Linux with LXTerminal, I use cu to link my terminal to the Arduino board. This can't work with Serial Monitor and it has to be off when downloading a sketch but once the sketch is running open a terminal and set it up.

Here's the cu howto:

on the Arduino Playground...............

The interface you are going to run ('cu') is so simple that every keypress will be sent to the serial device without echo, and every char sent by Arduino will be sent to your screen. There is no chance to stop 'cu', only by killing the Terminal window you are running.

Type in the terminal:

$ cu -l /dev/ -s

In my case:

$ cu -l /dev/ttyACM0 -s 115200

All done!

If 'cu' is not installed, type in the Terminal:

$ sudo apt-get install cu

That's all folks! 9-jun-2011 - by The Dare Guy (http://twitter.com/thedareguy)

Mine shows up on ttyACM0 too.
Screen size in Lines and Columns can be set in LXTerminal Edit->preferences.

Interesting.

I will bookmark this to look at tomorrow when I have more time.

...R

PS code tags !

Edit: latest -- color works for me, in code below -- also added some data watch values just to show yeah you can watch data.
One place to pick some up -- Terminal codes (ANSI/VT100) introduction [Bash Hackers Wiki]

Here is an example that will only display proper on a terminal with 80 columns, something set in the terminal likely preferences.

It needs more comments, a whole spiel about terminals or a link to one if I find it done.

This for making text blink to test led blink code. I don't have to wire 100 leds to test patterns for 100 leds.

// add-a-sketch_text_blinkers 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Dec 14/2018 by GFS. Compiled on Arduino IDE 1.8.5.
// This sketch requires connection to a terminal emulator on your PC.
// On Linux, cu lets you connect the terminal and Arduino.
// more l8r

// multi_blinkers vars
const byte blinkers = 6;
byte blinkerPin[ blinkers ] = { 2, 3, 4, 5, 6, 7 };
byte blinkerState[ blinkers ]; // better to use bits but not beginner friendly
word startBlink[ blinkers ];  // 2 millis timers per blinker
const word waitBlinkMs[ blinkers ] = { 1000, 580, 780, 1230, 1470, 1610 };
word waitBlink[ blinkers ];
// type word (16 bit unsigned) good to 65,535. as millis it is > 1 hour.

// multi_blink switching vars
byte blinkCounter[ blinkers ], lastBlinkerState[ blinkers ];
byte switchBlinks[ blinkers ] = { 5, 12, 9, 6, 10, 7 }; // blink X times then pause
word startOffTime[ blinkers ];
word waitOffTime[ blinkers ] = { 5000, 4000, 1200, 3300, 6000, 2000 }; // pause millis

const char blinkText[ blinkers + 1 ] =
{ 'A', 'B', 'C', 'D', 'E', 'F', ' ' }; // ' ' erases
const byte blinkPos[ blinkers ][ 2 ] =
{ 12, 10, 12, 22, 12, 34, 12, 46, 12, 58, 12, 70 };


void blackFG()
{
  Serial.print( F( "\033[30m" ));

}

void cyanBKG()
{
  Serial.print( F( "\033[46m" ));

}

byte curL, curC;

void clrscr()
{
  Serial.print( F( "\033[2J" ));
}

void clr2eol()
{
  Serial.print( F( "\033[K" ));
}

void poscur( byte line, byte col )
{
  Serial.print( F( "\033[" ));
  Serial.print( line );
  Serial.print( F( ";" ));
  Serial.print( col );
  Serial.print( F( "H" ));
}

void savepos()
{
  Serial.print( F( "\033[s" ));
}

void restpos()
{
  Serial.print( F( "\033[u" ));
}

#define microsInOneSecond 1000000UL

const byte loopCountTextPos[ 2 ] = { 1, 60 };

void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
  static unsigned long count, countStartMicros; // only this function sees these

  count++; // adds 1 to count after any use in an expression, here it just adds 1.
  if ( micros() - countStartMicros >= microsInOneSecond ) // 1 second
  {
    countStartMicros += microsInOneSecond; // for a regular second
    savepos();
    poscur( loopCountTextPos[ 0 ], loopCountTextPos[ 1 ] );
    Serial.println( count ); // 32-bit binary into decimal text = many micros
    restpos();
    count = 0; // don't forget to reset the counter
  }
}


void MultiBlinkers() // you change the blink by changing blinkWait[ blinker ]
{
  static byte index; // static vars keep their data. this 'index' is for this function

  if ( waitBlink[ index ] > 0 ) // this is the on/off switch
  {
    // word( millis()) gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startBlink[ index ] >= waitBlink[ index ] )
    { // difference in time given by subtracting start from end
      blinkerState[ index ] = !blinkerState[ index ];  // ! is logical NOT, make opposite
      digitalWrite( blinkerPin[ index ], blinkerState[ index ] );
      poscur( blinkPos[ index ][ 0 ], blinkPos[ index ][ 1 ] );
      if ( blinkerState[ index ] > 0 )
      {
        Serial.write( blinkText[ index ] );
      }
      else
      {
        Serial.write( blinkText[ blinkers ] );  // should be ' ' to erase
      }
      startBlink[ index ] += waitBlink[ index ]; // next blink starts when it should.
    }
  }
  else if ( digitalRead( blinkerPin[ index ] ) > 0 ) // waitBlink == 0 turns blinking off
  {
    digitalWrite( blinkerPin[ index ], LOW ); //  if it's on, turn the led is OFF
    poscur( blinkPos[ index ][ 0 ], blinkPos[ index ][ 1 ] );
    Serial.write( blinkText[ blinkers ] );  // should be ' ' to erase
    poscur( loopCountTextPos[ 0 ], loopCountTextPos[ 1 ] );
  }

  // this adds 1 to index then compares to blinkers, keeps index inside the array
  if ( ++index >= blinkers )    index = 0; // if at array end, loop around to start
  // note that only 1 blinker is checked per call. Don't Block, make loop run fast.
}

void MultiBlinkSwitcher()
{
  static byte index; // static vars keep their data. this 'index' is for this function

  // this task pauses the blinker after 5 blinks then turns blinking back on, repeat.
  if ( waitBlink[ index ] > 0 ) // while the led is blinking
  {
    if ( blinkerState[ index ] != lastBlinkerState[ index ] ) // blink has just transitioned
    {
      if ( blinkerState[ index ] == 0 ) // count a blink only if turned off, blink is finished
      {
        blinkCounter[ index ]++;
        if ( blinkCounter[ index ] == 5 ) // 5 blinks then none for 3 seconds
        {
          blinkCounter[ index ] = 0; // the if has the ++ first, count stats at 1.
          waitBlink[ index ] = 0; // turn the blinking off, start off timing
          startOffTime[ index ] = millis(); // set start to off time
          poscur( blinkPos[ index ][ 0 ] + 3, blinkPos[ index ][ 1 ] );
          for ( byte z = 0; z < 5; z++ )
          {
            Serial.write( ' ' );  // should be ' ' to erase
          }
          poscur( blinkPos[ index ][ 0 ] + 3, blinkPos[ index ][ 1 ] );
          Serial.print( 0 );  // should be ' ' to erase
          poscur( loopCountTextPos[ 0 ], loopCountTextPos[ 1 ] );
        }
      }
      lastBlinkerState[ index ] = blinkerState[ index ];
    }
  }
  else // this only runs when blinking is off
  {
    // word( millis()) -- gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startOffTime[ index ] >= waitOffTime[ index ] )
    {
      startBlink[ index ] = millis();
      waitBlink[ index ] = waitBlinkMs[ index ];
      poscur( blinkPos[ index ][ 0 ] + 3, blinkPos[ index ][ 1 ] );
      for ( byte z = 0; z < 5; z++ )
      {
        Serial.write( ' ' );  // should be ' ' to erase
      }
      poscur( blinkPos[ index ][ 0 ] + 3, blinkPos[ index ][ 1 ] );
      Serial.print( waitBlink[ index ] );  // should be ' ' to erase
      poscur( loopCountTextPos[ 0 ], loopCountTextPos[ 1 ] );
    }
  }

  byte rtn = index;
  if ( ++index >= blinkers )    index = 0; // if at array end, loop around to start
}


void setup()
{
  Serial.begin( 115200 );
  cyanBKG();
  blackFG();
  clrscr();
  poscur( 1, 50 );
  Serial.print( F( "Loop Hz" ));
  poscur( 5, 12 );
  Serial.println( F( "Blink Text and Leds, free by GoForSmoke\n" ));

  for ( byte i = 0; i < blinkers; i++ )
  {
    waitBlink[ i ] = waitBlinkMs[ i ];
    pinMode( blinkerPin[ i ], OUTPUT );
  }
}


void loop()
{
  LoopCounter();
  // this is the interruptable blink task
  MultiBlinkers(); // sets 'now' only when blinking
  // this task pauses the blinker after 5 blinks then turns blinking back on, repeat.
  MultiBlinkSwitcher();
}

What I can't seem to do is read what char is on-screen at any position.

The terminal can save lines at wish, there's commands to scroll up and down (not sure about left and right, prolly not). A maze or map could be loaded into the terminal and it'd really be nice to read the screen but no, seems not.

Here is the last with a minor bugfix and also a bouncing ball in a box that can be acted upon using number keys.

I had to put a 50ms timer on the ball so you'd see it move. Arduino PONG is definitely doable.

However the sketch is now too long to post.

addasketch_text_blinkers.ino (8.94 KB)