///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////