///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// README /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Apple I Weather Stations Demo Software Developed by:

Andre' LaMothe
[email protected]
Cell: 408.835.7584

*** PLEASE READ THIS SETUP COMPLETELY IF YOU WANT TO PLAY WITH PUTTY TERMINAL, CONNECT THE HARDWARE, AND MAKE CHANGES TO FIRMWARE ***

UPDATED LAST: 9/8/2020

HARDWARE AND SOFTWARE SETUP

*/


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>


// Custom 3rd party libraries
#include <Adafruit_Si7021.h>
#include <Adafruit_BME280.h>
#include <HCSR04.h>


////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// DEFINES, MACROS, etc. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// used to compute the min and max of two expressions
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
#define SIGN(a) ( ((a) > 0) ? (1) : (-1) )
#define CLAMP(x,l,u) { if ( (x) > (u) ) x = (u); else if ( (x) < (l) ) x = (l); }

// a more useful random function
#define RAND_RANGE(x,y) ( (x) + (rand()%((y)-(x)+1)))

// used for swapping algorithm
#define SWAP(a,b,t) {t=a; a=b; b=t;}

// binary macro
#define BINARY8( b7, b6, b5, b4, b3, b2, b1, b0 ) ( (b7 << 7) | (b6 << 6) | (b5 << 5) | (b4 << 4) | (b3 << 3) | (b2 << 2) | (b1 << 1) | (b0 << 0) )
#define SETBIT( shift ) ( 1 << shift )

#define DSP_BUFFER_SIZE   8         // Used for DSP algorithms on directional A/D data

// Extended ASCII art printing characters
#define ASCII_BLOCK_DITHERED_LIGHT  176
#define ASCII_BLOCK_DITHERED_MEDIUM 177
#define ASCII_BLOCK_DITHERED_DARK   178
#define ASCII_BLOCK_SOLID           219


// ASCII codes for ease of parser development
#define ASCII_A       65
#define ASCII_F       ('F')
#define ASCII_Z       90

#define ASCII_a       97
#define ASCII_z       122

#define ASCII_0       48
#define ASCII_9       57

#define ASCII_BS      127  // backspace   
#define ASCII_LF      0x0A // line feed 
#define ASCII_CR      0x0D // carriage return
#define ASCII_ESC     0x1B // escape
#define ASCII_HEX     0x24 // $ for hex
#define ASCII_BIN     0x25 // % for binary
#define ASCII_LB      0x5B // [ 
#define ASCII_SEMI    0x3A // ; 
#define ASCII_EQUALS  0x3D // = 
#define ASCII_PERIOD  0x2E // .
#define ASCII_COMMA   0x2C // ,
#define ASCII_SHARP   0x23 // #
#define ASCII_NULL    0x00 // null character
#define ASCII_SPACE   0x20 // space

// subset of VT100 terminal attributes/codes
#define VT100_ATTR_OFF    0
#define VT100_BOLD        1
#define VT100_USCORE      4
#define VT100_BLINK       5
#define VT100_REVERSE   7
#define VT100_BOLD_OFF    21
#define VT100_USCORE_OFF  24
#define VT100_BLINK_OFF   25
#define VT100_REVERSE_OFF 27
#define VT100_ESCAPE_CODE 27

// Useful commands
const PROGMEM char VT100_HIDE_CURSOR[] = "?25l";
const PROGMEM char VT100_SHOW_CURSOR[] = "?25h";

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TYPES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PROTOTYPES /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GLOBALS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// VT100 terminal globals
const char VT100_ESCAPE_SEQ_STRING[ ] = { VT100_ESCAPE_CODE, '[', 0 }; 

char gStringBuffer[ 256 ];              // Buffer for building strings...

int gDirDSPBuffer[ DSP_BUFFER_SIZE ];     // Buffers directional A/D samples
int gSpeedDSPBuffer[ DSP_BUFFER_SIZE ];   // Buffers speed A/D samples
float gRangeDSPBuffer[ DSP_BUFFER_SIZE ]; // Buffers range floating values 


bool gEnableHeater = false;             // Many humidity sensors require a heating element, it must be periodically turned on/off
int16_t gHeaterCounter = 0;             // These are used to track state and count for that process

#if 0
    // Si7021 vars
    // Create a sensor object
    Adafruit_Si7021 gSensorSi7021 = Adafruit_Si7021();
#endif


#if 1
    // BME280
    #define SEALEVELPRESSURE_HPA (1013.25)
    Adafruit_BME280 gSensorBME280; // I2C
#endif

// Ultrasonic range sensor
HCSR04 gSensorHCSR04(5,6);//initialisation class HCSR04 (trig pin , echo pin)


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS/METHODS///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void DrawBlockLineAtCursor( char blockChar, int lineLength, int lineFill )
{
/*   
  DESCRIPTION:  This function draws a line of graphic characters from the current terminal cursor location, so it
                the responsibility of caller to handle cursor positioning before and after (for now).
  PARAMETERS:          
     
  RETURN:       Nothing.   
*/
 
// Create string  
memset( gStringBuffer, blockChar, lineLength );

// Now, fill end with spaces to overwrite previous render

memset( gStringBuffer + lineLength , ' ' , lineFill );

// Null terminate
gStringBuffer[ lineLength + lineFill ] = 0;

// Now draw line...
Serial.print( gStringBuffer );

} // end DrawBlockLineAtCursor

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// UART VT100 TERMINAL EMULATION HELPER FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void PrintArtASCII( const char *string )
{

/*
#define ASCII_BLOCK_DITHERED_LIGHT  176
#define ASCII_BLOCK_DITHERED_MEDIUM 177
#define ASCII_BLOCK_DITHERED_DARK   178
#define ASCII_BLOCK_SOLID           219

*/

static int renderMode = 0; // tracks if we are in ASCII art mode or text mode

// Print string out to ASCII VT100 UART with transcoding of characters
for ( int32_t stringIndex = 0; stringIndex < (int32_t)strlen( string ); stringIndex++ )
    {
  
    // Test which render mode we are in?

    if ( renderMode == 0 )
      {
      switch( string[ stringIndex ] )  
            {
            case 'W': Serial.write( ASCII_BLOCK_DITHERED_DARK ); break;
            case 'X': Serial.write( ASCII_BLOCK_SOLID ); break;
            case 'Y': Serial.write( ASCII_BLOCK_DITHERED_LIGHT ); break;
            case 'Z': Serial.write( ASCII_BLOCK_DITHERED_MEDIUM ); break;
            
            case '$': // Toggle render mode
                  {
                  if ( !renderMode ) 
                    renderMode = 1;
                  } break;
        
            default: Serial.write( string[ stringIndex ]  ); break;
  
            } // end switch
  
      } // end if
    else
      { // Normal mode, do not draw ASCII art
  
      // Toggle out of text mode?
      if ( string[ stringIndex ] == '$' )
        renderMode = 0;
      else  
        Serial.write( string[ stringIndex ]  );
      
      } // end else

    } // end for stringIndex

// Finish line
Serial.println("");

} // end PrintArtASCII

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void VT100_Logo( void )
{
Serial.println("");

PrintArtASCII("               XX                                                               ");                                               
PrintArtASCII("             XXXX                                                               ");                                                
PrintArtASCII("            XXXX                                                                ");                                               
PrintArtASCII("            XXX                  $MITCH WAITE'S$                                ");                                                
PrintArtASCII("     XXXXX  XX XXXXXXX                             XX            XXXXXXXXX      ");                                               
PrintArtASCII("   XXXXXXXXX  XXXXXXXXX                            XX            XXXXXXXXX      ");
PrintArtASCII("  YYYYYYYYYYYYYYYYYYYY   XXXXXXX XXXXXXXX XXXXXXXX XX  XXXXXXXX     XXX         ");
PrintArtASCII("  YYYYYYYYYYYYYYYYYY    XX    XX XXXXXXXX XXXXXXXX XX XX   XXXX     XXX         ");
PrintArtASCII("  ZZZZZZZZZZZZZZZZZ     XX    XX XX    XX XX    XX XX XX  XXXX      XXX         ");
PrintArtASCII("  ZZZZZZZZZZZZZZZZZZ    XX    XX XX XXXXX XX XXXXX XX XX            XXX         ");
PrintArtASCII("  WWWWWWWWWWWWWWWWWWW   XXXX  XX XX       XX       XX XXXXXXXX   XXXXXXXXX      ");
PrintArtASCII("   WWWWWWWWWWWWWWWWWWW   XXX  XX XX       XX       XX   XXXXXX   XXXXXXXXX      ");
PrintArtASCII("    XXXXXXXXXXXXXXXXX                                                           ");
PrintArtASCII("     XXXXXXXXXXXXXXX                                    Serial Number 0065      ");
PrintArtASCII("      XXXX    XXXX                                                              ");

Serial.println("                            Apple I Weather Station                            ");
Serial.println("");
Serial.println("                           // A re-creation of the original 1977 Apple I       ");      
Serial.println("                           // Weather Station on Mitch's Greenbrae Houseboat.  ");
Serial.println("");
Serial.println("");  

} // end VT100_Logo

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void VT100_SendCmd( char *cmdString )
{
/*
DESCRIPTION:    This function sends a VT100 command by pre-pending "ESC [ " followed by the command
PARAMETERS:     None.
RETURNS:        Nothing.
*/

// build command up by pre-pending escape sequence 
sprintf( gStringBuffer, "%s%s", VT100_ESCAPE_SEQ_STRING, cmdString );
 
// and now finish by writting the VT100 command
Serial.print( gStringBuffer ); 

} // end VT100_SendCmd

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void VT100_Init( void )
{
/*
DESCRIPTION:    Initializes terminal to power on mode.
PARAMETERS:     None.
RETURNS:        Nothing.
*/

// ESC c
char cmdStr[] = { VT100_ESCAPE_CODE, 'c', 0 };

Serial.print( cmdStr );

} // end VT100_Init

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void VT100_ClearScreen( void )
{
/*
DESCRIPTION:    Clears the screen.
PARAMETERS:     None.
RETURNS:        Nothing.
*/

// ESC [ 2 j
char cmdStr[] = { VT100_ESCAPE_CODE, '[', '2', 'J', 0 };

Serial.print( cmdStr );

} // end VT100_ClearScreen

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void VT100_SetCursor( unsigned char line, unsigned char column )
{
/*
DESCRIPTION:    Places cursor at sent column and line.

PARAMETERS:     line - vertical screen line to place the cursor at.
                column - horizontal screen column to place the cursor at.
RETURNS:        Nothing.
*/

// ESC [ Pl ; Pc H

char cmdStr[ 16 ];

// build the string
sprintf( cmdStr, "%c[%d;%dH",VT100_ESCAPE_CODE, line, column);

// send to USART
Serial.print( cmdStr );

} // end VT100_SetCursor

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void VT100_SetAttr( unsigned char attr )
{

/*
DESCRIPTION:    Changes text attribute, only works on some terminals.
PARAMETERS:     attr - the attribute to change the text mode to.
RETURNS:        Nothing.
*/

char cmdStr[ 16 ];

// build the string
sprintf( cmdStr, "%c[%dm",VT100_ESCAPE_CODE,attr);

// ESC [ Ps m
Serial.print( cmdStr );

} // end VT100_SetAttr


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void AppleBootScreen( int page )
{
  
if ( page == 0)
    {
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    Serial.println("_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ");
    } // end if
else
if ( page == 1)
    {
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    Serial.println("_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0");
    } // end if
else
if ( page == 2 )
    {
    for ( uint8_t charIndex = 32; charIndex < 128; charIndex++)
        {
        Serial.print( (char)charIndex );
        delay( 50 );
        } // end for charIndex
 
    } // end if

} // end AppleBootScreen

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void AppleI_Fake_Boot()
{

VT100_Init( );
VT100_ClearScreen( );
VT100_SetCursor(0,0); 
delay( 1000 );
  
// Flash on/off image

for (int index = 0; index < 3; index++ )
    {
    VT100_SetCursor(0,0);       
    AppleBootScreen( 0 );
    delay( 500 );
    
    VT100_SetCursor(0,0); 
    AppleBootScreen( 1 );
    delay( 500 );
    
    } // end for index

VT100_ClearScreen( );
VT100_SetCursor(0,0); 

AppleBootScreen( 2 );
delay( 2000 );

VT100_ClearScreen( );

for (int index = 0; index < 3; index++)
  {
  VT100_SetCursor(0,0); 
  sprintf( gStringBuffer, "]%c", ASCII_BLOCK_DITHERED_LIGHT );
  Serial.print( gStringBuffer );
  
  delay( 500 );

  VT100_SetCursor(0,0); 
  sprintf( gStringBuffer, "]%c", 32 );
  Serial.print( gStringBuffer );
  delay( 500 );  
  
  } // end for index

VT100_SetCursor(0,0); 
sprintf( gStringBuffer, "]R%c", ASCII_BLOCK_DITHERED_LIGHT );
Serial.print( gStringBuffer );
delay( 500 );

VT100_SetCursor(0,0); 
sprintf( gStringBuffer, "]RU%c", ASCII_BLOCK_DITHERED_LIGHT );
Serial.print( gStringBuffer );
delay( 700 );

VT100_SetCursor(0,0); 
sprintf( gStringBuffer, "]RUN%c", ASCII_BLOCK_DITHERED_LIGHT );
Serial.print( gStringBuffer );
delay( 400 );

for (int index = 0; index < 3; index++)
  {
  VT100_SetCursor(0,0); 
  sprintf( gStringBuffer, "]RUN%c", ASCII_BLOCK_DITHERED_LIGHT );
  Serial.print( gStringBuffer );
  
  delay( 500 );

  VT100_SetCursor(0,0); 
  sprintf( gStringBuffer, "]RUN%c", 32 );
  Serial.print( gStringBuffer );
  delay( 500 );  
  
  } // end for index

delay( 500 );

VT100_Init( );
VT100_ClearScreen( );
VT100_Logo( );

delay( 8000 );

VT100_ClearScreen();
VT100_SetCursor(0,0); 

} // end AppleI_FakeBoot

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

char *windDirections[] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW" };

char *WindDirectionToString( float angle )
{
#define WIND_DIR_NORTH        0  
#define WIND_DIR_NORTH_EAST   1
#define WIND_DIR_EAST         2
#define WIND_DIR_SOUTH_EAST   3
#define WIND_DIR_SOUTH        4
#define WIND_DIR_SOUTH_WEST   5
#define WIND_DIR_WEST         6
#define WIND_DIR_NORTH_WEST   7

// Compute octant
angle =  ( angle + 22.5 );

if ( angle >= 360.0f )
  angle -= 360.0f;

angle /= 45.0f;

return ( windDirections[ (int)( angle + 0.0f ) % 8 ] );
  
} // end WindDirectionToString

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SETUP/LOOP /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// the setup function runs once when you press reset or power the board
void setup() 
{
// Start the serial port up
Serial.begin(115200, SERIAL_8N1);

// Enable analog and set to 5V
analogReference( DEFAULT );

// Enable the humidity sensor

#if 0
    gSensorSi7021.begin();
#endif

#if 1
    gSensorBME280.begin();  
#endif


// Initialize DSP buffers to 0, note float 0.0 happens to be int 0, so this works as expected
memset( gDirDSPBuffer, 0, sizeof( int )     * DSP_BUFFER_SIZE );
memset( gSpeedDSPBuffer, 0, sizeof( int )   * DSP_BUFFER_SIZE );
memset( gRangeDSPBuffer, 0, sizeof( float ) * DSP_BUFFER_SIZE );


// Call the fake boot for fun :)
AppleI_Fake_Boot();

//VT100_Init( );
//VT100_ClearScreen( );
//VT100_SetCursor(0,0); 

} // end setup

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// the loop function runs over and over again forever
void loop() 
{
// Working vars for reading weather state
float instantHumidity;
float instantTemp;
float instantPressure;    
float instantAltitude;    
float instantWaterLevel;

// Process directional DSP buffer...

// Shift DSP buffer data...
memmove( &gDirDSPBuffer[ 0 ] , &gDirDSPBuffer[ 1 ], sizeof( int ) * (  DSP_BUFFER_SIZE - 1 )  );

// Read next sample from directional A/D into DSP buffer
// 0 - 5V maps to 0 to 1023 -> 0 to 360 degrees
gDirDSPBuffer[ DSP_BUFFER_SIZE - 1 ] = analogRead( A0 );

// And now average the data for readout...
float dirAverage = 0;

// Compute sum...
for (int index = 0; index < DSP_BUFFER_SIZE; index++)
  {
  dirAverage += gDirDSPBuffer[ index ];
  } // end for index

//  And finally, average
dirAverage /= DSP_BUFFER_SIZE;

//--------------------------------------------------------------

// Process speed DSP buffer...

// Shift DSP buffer data...
memmove( &gSpeedDSPBuffer[ 0 ] , &gSpeedDSPBuffer[ 1 ], sizeof( int ) * (  DSP_BUFFER_SIZE - 1 )  );

// Read next sample from directional A/D into DSP buffer
// 0 - 5V maps to 0 to 1023 -> Speed = (V/5)*MAX_SPEED_M/S + MIN_SPEED_M/S, which for our sensor is (V/5)*30 + 0
// Looking at above equation, we simply need to normalize V[0-5V] and multiply by 30
gSpeedDSPBuffer[ DSP_BUFFER_SIZE - 1 ] = analogRead( A1 );

// And now average the data for readout...
float speedAverage = 0;

// Compute sum...
for (int index = 0; index < DSP_BUFFER_SIZE; index++)
  {
  speedAverage += gSpeedDSPBuffer[ index ];
  } // end for index

//  And finally, average
speedAverage /= DSP_BUFFER_SIZE;

// And now convert to final velocity in proper units and convert m/s to mph
#define MS_TO_MPH       2.23694f

speedAverage = ( ( speedAverage / 1024.0f ) * 30 ) * MS_TO_MPH;

//--------------------------------------------------------------

#if 0

    // Read the Si7021
    instantHumidity =   gSensorSi7021.readHumidity();
    instantTemp     =   gSensorSi7021.readTemperature();
    
    // Turn heater on/off
    if ( ++gHeaterCounter > 100 )
      {
      // Toggle state variable and set heater on/off
      gEnableHeater = !gEnableHeater;
      gSensorSi7021.heater( gEnableHeater );
    
      // Reset heater
      gHeaterCounter = 0;
     
      } // end if

#endif

//--------------------------------------------------------------

// Process range DSP buffer...

// Shift DSP buffer data...
memmove( &gRangeDSPBuffer[ 0 ] , &gRangeDSPBuffer[ 1 ], sizeof( float ) * (  DSP_BUFFER_SIZE - 1 )  );

// Read the distance from the range sensor, returns a float in cm
gRangeDSPBuffer[ DSP_BUFFER_SIZE - 1 ] =    gSensorHCSR04.dist();

#define MAX_HCSR04_RANGE_CM   (500.0f)

// Clamp it
if ( gRangeDSPBuffer[ DSP_BUFFER_SIZE - 1 ] > MAX_HCSR04_RANGE_CM )
  gRangeDSPBuffer[ DSP_BUFFER_SIZE - 1 ] = MAX_HCSR04_RANGE_CM;
    
// And now average the data for readout...
float rangeAverage = 0;

// Compute sum...
for (int index = 0; index < DSP_BUFFER_SIZE; index++)
  {
  rangeAverage += gRangeDSPBuffer[ index ];
  } // end for index

//  And finally, average
rangeAverage /= DSP_BUFFER_SIZE;

//--------------------------------------------------------------



#if 1

    // Read values from BME280
    instantHumidity   = gSensorBME280.readHumidity();
    instantTemp       = gSensorBME280.readTemperature();
    instantPressure   = gSensorBME280.readPressure() / 100.0F;
    instantAltitude   = gSensorBME280.readAltitude(SEALEVELPRESSURE_HPA);

#endif

//--------------------------------------------------------------

// Render results

VT100_SetCursor(0,0);

Serial.println( "Apple I - Weather Station Readings" );
Serial.println( "________________________________________" );
Serial.println("");

int intAngle = ( 0.5f + (360.0f * dirAverage) / 1024.0f);

sprintf( gStringBuffer, "\n\rWind Direction: %d%c %s              ", intAngle, 248, WindDirectionToString( intAngle ) );
Serial.print( gStringBuffer );

sprintf( gStringBuffer, "\n\rWind Speed:     %d mph               ", (int)( 0.5f + speedAverage) );
Serial.print( gStringBuffer );

sprintf( gStringBuffer, "\n\rTemperature:    %d%cF | %d%cC        ", (int)(instantTemp*1.8f + 32 + 0.5f), 248, (int)(instantTemp + 0.5f), 248 );
Serial.print( gStringBuffer );

if ( gEnableHeater )
  sprintf( gStringBuffer, "\n\rHumidity:       %d%% RH.             ", (int)(instantHumidity + 0.5f) );
else
  sprintf( gStringBuffer, "\n\rHumidity:       %d%% RH              ", (int)(instantHumidity + 0.5f) );
Serial.print( gStringBuffer );

sprintf( gStringBuffer, "\n\rPressure:       %d hPa                 ", (int)(instantPressure + 0.5f) );
Serial.print( gStringBuffer );

sprintf( gStringBuffer, "\n\rAltitude:       %d m above sea level   ", (int)(instantAltitude + 0.5f) );
Serial.print( gStringBuffer );

sprintf( gStringBuffer, "\n\rWater Level:    %d cm below sensor      ", (int)(rangeAverage + 0.5f) );
Serial.print( gStringBuffer );

Serial.print( "\n\r                [" );

// Draw line to represent sea level
int lineLength = (int)(rangeAverage / 10.0f);
int lineFill = 50 - lineLength;

DrawBlockLineAtCursor( ASCII_BLOCK_DITHERED_MEDIUM, lineLength, lineFill );
Serial.print( "] " );
} // end loop

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HACKING CODE ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////