added config screen, added game

This commit is contained in:
2022-04-14 17:01:26 +02:00
parent e9582a6dbf
commit 89561ba8d9
18 changed files with 1163 additions and 152 deletions

View File

@@ -1,48 +1,39 @@
#include "display.h" #include "display.h"
e_displayState CurrentScreen;
void handleDisplayGui(void) void handleDisplayGui(void)
{ {
clearDisplay(); clearDisplay();
switch (CurrentScreen) switch (getDisplayState())
{ {
case mainscreen: case mainscreen:
{ {
e_displayState nextScreen = ScreenMainHandle(); ScreenMainHandle();
if(nextScreen != CurrentScreen)
{
setDisplayState(nextScreen);
}
ScreenMainDrawValues();
ScreenMainDrawButtons();
} }
break; break;
case setupscreen: case setupscreen:
{ {
e_displayState nextScreen = ScreenSetupHandle(); ScreenSetupHandle();
if(nextScreen != CurrentScreen)
{
setDisplayState(nextScreen);
} }
ScreenSetupDrawButtons(); break;
case gamescreen:
{
handleScreenGame();
} }
break; break;
} }
}
void setDisplayState(e_displayState newstate)
{
CurrentScreen = newstate;
} }
void initDisplay(void) void initDisplay(void)
{ {
initDisplayHall(); initDisplayHall();
initDisplayMain(); initDisplayMain();
initSetupScreen();
initScreenGame();
setDisplayState(e_displayState::mainscreen);
} }
void handleDisplay(void) void handleDisplay(void)

View File

@@ -5,8 +5,10 @@
#include "display_buttons.h" #include "display_buttons.h"
#include "display_hall.h" #include "display_hall.h"
#include "display_draw.h" #include "display_draw.h"
#include "display_screen.h"
#include "screen_main.h" #include "screen_main.h"
#include "screen_setup.h" #include "screen_setup.h"
#include "screen_game.h"
#include "measure.h" #include "measure.h"
#include "measure_mode.h" #include "measure_mode.h"
@@ -14,5 +16,4 @@ void initDisplay(void);
void handleDisplay(void); void handleDisplay(void);
e_displayState getDisplayState(void); e_displayState getDisplayState(void);
void setDisplayState(e_displayState newstate);

View File

@@ -67,21 +67,28 @@ void c_onScreenButton::handle()
{ {
if (_physButton.isValid()) if (_physButton.isValid())
{ {
_pressed = _physButton.read(); _physButton.read();
_pressed = _physButton.pressedFor(200);
if (_pressed) if (_pressed)
{ {
if(_mode != e_measureMode::mNone) if(_actionFn != NULL && (actionHandled == false))
{ {
setMeasureMode(_mode); _actionFn();
actionHandled = true;
} }
} }
else if (_physButton.releasedFor(500))
{
actionHandled = false;
}
_state = _pressed; _state = _pressed;
} }
else if (_stateFn != NULL) else if (_stateFn != NULL)
{ {
_state = _stateFn(); _state = _stateFn();
} }
log_i("item(%s)=%d",_name.c_str(), _state); log_d("item(%s)=%d",_name.c_str(), _state);
} }

View File

@@ -16,6 +16,7 @@
#define INDICATORWIDTH 29 #define INDICATORWIDTH 29
#define INDICATORHEIGHT 13 #define INDICATORHEIGHT 13
#define INDICATORRADIUS 3 #define INDICATORRADIUS 3
#define HORIZONTALBUTTONS 5
typedef enum typedef enum
{ {
@@ -31,6 +32,8 @@ class c_onScreenButton
const String _name; const String _name;
const uint8_t _index; const uint8_t _index;
bool (*const _stateFn)(); bool (*const _stateFn)();
void (*const _actionFn)();
bool actionHandled = false;
c_button _physButton; c_button _physButton;
@@ -54,9 +57,11 @@ class c_onScreenButton
void setState(bool state) { _state = state; } void setState(bool state) { _state = state; }
public: public:
c_onScreenButton(String name, e_measureMode mode, e_buttonLoc location, uint8_t pin) : _name(name), c_onScreenButton(String name, e_measureMode mode, e_buttonLoc location, uint8_t pin,void (*const action)() ) :
_name(name),
_index((uint8_t)mode), _index((uint8_t)mode),
_stateFn(NULL), _stateFn(NULL),
_actionFn(action),
_physButton(pin, (uint8_t)mode) _physButton(pin, (uint8_t)mode)
{ {
_xpos = 1; _xpos = 1;
@@ -69,9 +74,11 @@ public:
_location = location; _location = location;
} }
c_onScreenButton(String name, uint8_t index, e_buttonLoc location, uint8_t pin) : _name(name), c_onScreenButton(String name, uint8_t index, e_buttonLoc location, uint8_t pin, void (*const action)()) :
_name(name),
_index(index), _index(index),
_stateFn(NULL), _stateFn(NULL),
_actionFn(action),
_physButton(pin, index) _physButton(pin, index)
{ {
_xpos = 1; _xpos = 1;
@@ -87,6 +94,7 @@ public:
c_onScreenButton(String name, uint8_t index, e_buttonLoc location, bool (*stateFn)()) : _name(name), c_onScreenButton(String name, uint8_t index, e_buttonLoc location, bool (*stateFn)()) : _name(name),
_index(index), _index(index),
_stateFn(stateFn), _stateFn(stateFn),
_actionFn(NULL),
_physButton(NOBUTTON, index) _physButton(NOBUTTON, index)
{ {
_xpos = 1; _xpos = 1;

View File

@@ -2,6 +2,7 @@
U8G2_SSD1322 display(U8G2_R2, OLED_MOSI, OLED_SCK, OLED_CS, OLED_DC, OLED_RST); // Enable U8G2_16BIT in u8g2.h U8G2_SSD1322 display(U8G2_R2, OLED_MOSI, OLED_SCK, OLED_CS, OLED_DC, OLED_RST); // Enable U8G2_16BIT in u8g2.h
uint64_t lastDisplayTime = 0; uint64_t lastDisplayTime = 0;
bool maxFPS = false;
U8G2_SSD1322 *getDisplay(void) U8G2_SSD1322 *getDisplay(void)
{ {
@@ -21,7 +22,7 @@ void initDisplayHall()
void handleDisplayHall() void handleDisplayHall()
{ {
uint64_t currentmillis = millis(); uint64_t currentmillis = millis();
if (currentmillis - lastDisplayTime > SCREENREFRESH) if ((currentmillis - lastDisplayTime > SCREENREFRESH) || maxFPS)
{ {
//display.clearBuffer(); // clear the internal memory //display.clearBuffer(); // clear the internal memory
//display.drawStr(0, 10, "Hello World!"); // write something to the internal memory //display.drawStr(0, 10, "Hello World!"); // write something to the internal memory
@@ -30,6 +31,11 @@ void handleDisplayHall()
} }
} }
void SetFPSMax(void)
{
maxFPS = true;
}
void clearDisplay(void) void clearDisplay(void)
{ {
display.clearBuffer(); display.clearBuffer();

View File

@@ -43,3 +43,5 @@ uint16_t getDisplayHeight(void);
U8G2_SSD1322* getDisplay(void); U8G2_SSD1322* getDisplay(void);
void SetFPSMax(void);

View File

@@ -0,0 +1,83 @@
#include "display_screen.h"
e_displayState CurrentScreen = e_displayState::mainscreen;
uint32_t lastScreenchange = 0;
void setDisplayState(e_displayState newstate)
{
log_i("setDisplayState to %d", newstate);
uint32_t timeNow = millis();
// if(timeNow - lastScreenchange > MINSCREENSWITCHDELAY)
// {
CurrentScreen = newstate;
lastScreenchange = timeNow;
//}
}
e_displayState getDisplayState()
{
return CurrentScreen;
}
// class functions
void screen_c::begin(void)
{
uint16_t IndicatorYpos = 0;
uint16_t buttonXpos = 0;
uint16_t buttonwidth = ((getDisplay()->getDisplayWidth() / _buttonCount) - CONTROLLOFFSET * 2 + 1);
for (auto &&item : _items)
{
if (item->getLocation() == LocBottom)
{
uint16_t ypos = getDisplay()->getDisplayHeight() - 1 - CONTROLSLINE_H + 2;
item->begin(buttonXpos, ypos, buttonwidth, CONTROLSLINE_H + 6, CONTROLRADIUS);
buttonXpos+=buttonwidth-1;
item->setVisible(true);
}
else if (item->getLocation() == LocRight)
{
uint16_t IndicatorXpos = getDisplay()->getDisplayWidth() - INDICATORWIDTH;
item->begin(IndicatorXpos, IndicatorYpos, INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
item->setVisible(true);
IndicatorYpos += INDICATORHEIGHT - 1;
log_d("indicators: indicW=%d, indicH=%d ypos,%d", INDICATORWIDTH, INDICATORHEIGHT, IndicatorXpos);
}
else
{
log_e("location not implemented (%d)", item->getLocation());
}
}
_initialized = true;
}
void screen_c::handle(void)
{
if (!_initialized)
{
log_e("screen not initialized! %d", _thisScreen);
return;
}
for (auto &&i : _items)
{
i->handle();
}
}
void screen_c::draw(void)
{
for (auto &&item : _items)
{
item->drawButton();
}
}
void screen_c::activateModeButton(void)
{
for (auto &&item : _items)
{
item->setDisplayState((item->getIndex() == (uint8_t)getMeasureMode()));
}
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include "Arduino.h"
#include <vector>
#include "display_types.h"
#include "display_draw.h"
#include "display_buttons.h"
#include "measure_mode.h"
#define MINSCREENSWITCHDELAY 1000
void setDisplayState(e_displayState newstate);
e_displayState getDisplayState(void);
class screen_c
{
std::vector<c_onScreenButton*> _items;
const e_displayState _thisScreen;
const uint8_t _buttonCount;
bool _initialized = false;
public:
screen_c(e_displayState thisScreen, uint8_t buttonCount):_thisScreen(thisScreen), _buttonCount(buttonCount)
{}
void addItem(c_onScreenButton* pButton)
{
_items.push_back(pButton);
}
void begin(void);
void handle(void);
void draw(void);
void activateModeButton(void);
};

View File

@@ -5,5 +5,6 @@
typedef enum typedef enum
{ {
mainscreen, mainscreen,
setupscreen setupscreen,
gamescreen
} e_displayState; } e_displayState;

View File

@@ -6,13 +6,13 @@
enum e_measureMode enum e_measureMode
{ {
mNone,
mA20, mA20,
mA200, mA200,
mA1000, mA1000,
mAuto, mAuto,
mSetup, mSetup,
mLast, mLast,
mNone
}; };

View File

@@ -38,13 +38,11 @@ void initMeasureState(void)
void handleMeasureState(void) void handleMeasureState(void)
{ {
uint32_t currentTime = millis(); uint32_t currentTime = millis();
bool newState = false;
if (currentTime - lastMeasureTime > MEASURESTATEINTERVAL) if (currentTime - lastMeasureTime > MEASURESTATEINTERVAL)
{ {
if (currentMeasureState != lastMeasureState) if (currentMeasureState != lastMeasureState)
{ {
log_i("measureState: %s", s_measureState[currentMeasureState]); log_d("measureState: %s", s_measureState[currentMeasureState]);
newState = true;
} }
switch (currentMeasureState) switch (currentMeasureState)

View File

@@ -0,0 +1,398 @@
#include "screen_game.h"
#define SCREEN_WIDTH 256 // OLED _display width, in pixels
#define SCREEN_HEIGHT 64 // OLED _display height, in pixels
const char STR_LOADSCREEN_CREATOR[] = "Makeability Lab";
const char STR_LOADSCREEN_APP_NAME_LINE1[] = "Flappy";
const char STR_LOADSCREEN_APP_NAME_LINE2[] = "Bird!";
const char STR_PRESS_FLAP_TO_PLAY[] = "Press flap to play";
const char STR_PRESS_FLAP_TO_EXIT[] = "Press flap to exit";
const char STR_GAME_OVER[] = "Game Over!";
// Define I/O pins
const int FLAP_BUTTON_INPUT_PIN = BUTTON1;
const int TONE_OUTPUT_PIN = BUZZER1;
const int VIBROMOTOR_OUTPUT_PIN = BUZZER1;
// for tracking fps
unsigned long _frameCount = 0;
float _fps = 0;
unsigned long _fpsStartTimeStamp = 0;
uint32_t lastGameUpdate = 0;
// status bar
const boolean _drawFrameCount = false; // change to show/hide frame count
const int DELAY_LOOP_MS = 5;
const int LOAD_SCREEN_SHOW_MS = 750;
class Bird : public Rectangle
{
public:
Bird(int x, int y, int width, int height) : Rectangle(x, y, width, height)
{
}
};
class Pipe : public Rectangle
{
protected:
bool _hasPassedBird = false;
public:
Pipe(int x, int y, int width, int height) : Rectangle(x, y, width, height)
{
}
bool getHasPassedBird()
{
return _hasPassedBird;
}
bool setHasPassedBird(bool hasPassedBird)
{
return _hasPassedBird = hasPassedBird;
}
};
const int BIRD_HEIGHT = 5;
const int BIRD_WIDTH = 10;
const int NUM_PIPES = 3;
const int MIN_PIPE_WIDTH = 8;
const int MAX_PIPE_WIDTH = 18; // in pixels
const int MIN_PIPE_X_SPACING_DISTANCE = BIRD_WIDTH * 3; // in pixels
const int MAX_PIPE_X_SPACING_DISTANCE = 100; // in pixels
const int MIN_PIPE_Y_SPACE = BIRD_HEIGHT * 3;
const int MAX_PIPE_Y_SPACE = SCREEN_HEIGHT - BIRD_HEIGHT * 2;
int _pipeSpeed = 2;
int _gravity = 1; // can't apply gravity every frame, apply every X time
int _points = 0;
unsigned long _gameOverTimestamp = 0;
const int IGNORE_INPUT_AFTER_GAME_OVER_MS = 500; // ignores input for 500ms after game over
// Initialize top pipe and bottom pipe arrays. The location/sizes don't matter
// at this point as we'll set them in setup()
Pipe _topPipes[NUM_PIPES] = {Pipe(0, 0, 0, 0),
Pipe(0, 0, 0, 0),
Pipe(0, 0, 0, 0)};
Pipe _bottomPipes[NUM_PIPES] = {Pipe(0, 0, 0, 0),
Pipe(0, 0, 0, 0),
Pipe(0, 0, 0, 0)};
Bird _bird(5, SCREEN_HEIGHT / 2, BIRD_WIDTH, BIRD_HEIGHT);
enum GameState
{
NEW_GAME,
PLAYING,
GAME_OVER,
};
GameState _gameState = NEW_GAME;
// This is necessary for the game to work on the ESP32
// See:
// - https://github.com/espressif/arduino-esp32/issues/1734
// - https://github.com/Bodmer/TFT_eSPI/issues/189
#ifndef max
#define max(a, b) (((a) > (b)) ? (a) : (b))
#endif
void showLoadScreen();
void initializeGameEntities();
void drawStatusBar();
void nonGamePlayLoop();
void gamePlayLoop();
void calcFrameRate();
void initScreenGame()
{
// if analog input pin 5 is unconnected, random analog
// noise will cause the call to randomSeed() to generate
// different seed numbers each time the sketch runs.
// randomSeed() will then shuffle the random function.
randomSeed(125);
// Show load screen
showLoadScreen();
// Setup pipes
initializeGameEntities();
_fpsStartTimeStamp = millis();
_gameState = NEW_GAME;
}
void handleScreenGame()
{
uint32_t timeNow = millis();
if (timeNow - lastGameUpdate > DELAY_LOOP_MS)
{
//getDisplay()->clearDisplay();
drawStatusBar();
if (_gameState == NEW_GAME || _gameState == GAME_OVER)
{
nonGamePlayLoop();
}
else if (_gameState == PLAYING)
{
gamePlayLoop();
}
calcFrameRate();
}
}
void drawText(int xText, int yText, const char* s, const uint8_t *font)
{
getDisplay()->setFont(font);
getDisplay()->setCursor(xText, yText);
getDisplay()->print(s);
}
void drawCenterText(int yText, const char* s, const uint8_t *font)
{
int w = getDisplay()->getStrWidth(s);
drawText(getDisplay()->getDisplayWidth() / 2 - w / 2, yText, s, font);
}
void nonGamePlayLoop()
{
for (int i = 0; i < NUM_PIPES; i++)
{
_topPipes[i].draw();
_bottomPipes[i].draw();
}
int flapButtonVal = digitalRead(FLAP_BUTTON_INPUT_PIN);
if (_gameState == NEW_GAME)
{
drawCenterText(15,STR_PRESS_FLAP_TO_PLAY,FONT16);
if (flapButtonVal == LOW)
{
_gameState = PLAYING;
}
}
else if (_gameState == GAME_OVER)
{
int yText = 15;
drawCenterText(yText,STR_GAME_OVER,FONT16);
yText = yText + 16 + 2;
drawCenterText(yText,STR_PRESS_FLAP_TO_EXIT,FONT16);
// We ignore input a bit after game over so that user can see end game screen
// and not accidentally start a new game
if (flapButtonVal == LOW && millis() - _gameOverTimestamp >= IGNORE_INPUT_AFTER_GAME_OVER_MS)
{
// if the current state is game over, need to reset
setDisplayState(mainscreen);
}
}
_bird.draw();
}
void initializeGameEntities()
{
_points = 0;
_bird.setY(getDisplay()->getDisplayHeight() / 2 - _bird.getHeight() / 2);
_bird.setDrawFill(true);
const int minStartXPipeLocation = getDisplay()->getDisplayWidth() / 2;
int lastPipeX = minStartXPipeLocation;
for (int i = 0; i < NUM_PIPES; i++)
{
int pipeX = lastPipeX + random(MIN_PIPE_X_SPACING_DISTANCE, MAX_PIPE_X_SPACING_DISTANCE);
int pipeWidth = random(MIN_PIPE_WIDTH, MAX_PIPE_WIDTH);
int yGapBetweenPipes = random(MIN_PIPE_Y_SPACE, MAX_PIPE_Y_SPACE);
int topPipeY = 0;
int topPipeHeight = random(0, SCREEN_HEIGHT - yGapBetweenPipes);
int bottomPipeY = topPipeHeight + yGapBetweenPipes;
int bottomPipeHeight = SCREEN_HEIGHT - bottomPipeY;
_topPipes[i].setLocation(pipeX, topPipeY);
_topPipes[i].setDimensions(pipeWidth, topPipeHeight);
_topPipes[i].setDrawFill(false);
_bottomPipes[i].setLocation(pipeX, bottomPipeY);
_bottomPipes[i].setDimensions(pipeWidth, bottomPipeHeight);
_topPipes[i].setDrawFill(false);
lastPipeX = _topPipes[i].getRight();
}
}
void gamePlayLoop()
{
int flapButtonVal = digitalRead(FLAP_BUTTON_INPUT_PIN);
_bird.setY(_bird.getY() + _gravity);
if (flapButtonVal == LOW)
{
_bird.setY(_bird.getY() - 3);
}
_bird.forceInside(0, 0, getDisplay()->getDisplayWidth(), getDisplay()->getDisplayHeight());
// xMaxRight tracks the furthest right pixel of the furthest right pipe
// which we will use to reposition pipes that go off the left part of screen
int xMaxRight = 0;
// Iterate through pipes and check for collisions and scoring
for (int i = 0; i < NUM_PIPES; i++)
{
_topPipes[i].setX(_topPipes[i].getX() - _pipeSpeed);
_bottomPipes[i].setX(_bottomPipes[i].getX() - _pipeSpeed);
_topPipes[i].draw();
_bottomPipes[i].draw();
Serial.println(_topPipes[i].toString());
// Check if the bird passed by the pipe
if (_topPipes[i].getRight() < _bird.getLeft())
{
// If we're here, the bird has passed the pipe. Check to see
// if we've marked it as passed yet. If not, then increment the score!
if (_topPipes[i].getHasPassedBird() == false)
{
_points++;
_topPipes[i].setHasPassedBird(true);
_bottomPipes[i].setHasPassedBird(true);
}
}
// xMaxRight is used to track future placements of pipes once
// they go off the left part of the screen
if (xMaxRight < _topPipes[i].getRight())
{
xMaxRight = _topPipes[i].getRight();
}
// Check for collisions and end of game
if (_topPipes[i].overlaps(_bird))
{
_topPipes[i].setDrawFill(true);
_gameState = GAME_OVER;
_gameOverTimestamp = millis();
}
else
{
_topPipes[i].setDrawFill(false);
}
if (_bottomPipes[i].overlaps(_bird))
{
_bottomPipes[i].setDrawFill(true);
_gameState = GAME_OVER;
_gameOverTimestamp = millis();
}
else
{
_bottomPipes[i].setDrawFill(false);
}
}
// Check for pipes that have gone off the screen to the left
// and reset them to off the screen on the right
xMaxRight = max(xMaxRight, getDisplay()->getDisplayWidth());
for (int i = 0; i < NUM_PIPES; i++)
{
if (_topPipes[i].getRight() < 0)
{
int pipeX = xMaxRight + random(MIN_PIPE_X_SPACING_DISTANCE, MAX_PIPE_X_SPACING_DISTANCE);
int pipeWidth = random(MIN_PIPE_WIDTH, MAX_PIPE_WIDTH);
int yGapBetweenPipes = random(MIN_PIPE_Y_SPACE, MAX_PIPE_Y_SPACE);
int topPipeY = 0;
int topPipeHeight = random(0, SCREEN_HEIGHT - yGapBetweenPipes);
int bottomPipeY = topPipeHeight + yGapBetweenPipes;
int bottomPipeHeight = SCREEN_HEIGHT - bottomPipeY;
_topPipes[i].setLocation(pipeX, topPipeY);
_topPipes[i].setDimensions(pipeWidth, topPipeHeight);
_topPipes[i].setHasPassedBird(false);
_bottomPipes[i].setLocation(pipeX, bottomPipeY);
_bottomPipes[i].setDimensions(pipeWidth, bottomPipeHeight);
_bottomPipes[i].setHasPassedBird(false);
xMaxRight = _topPipes[i].getRight();
}
}
_bird.draw();
}
void showLoadScreen()
{
// Clear the buffer
getDisplay()->clearDisplay();
// Show load screen
int yText = 10;
drawCenterText(yText,STR_LOADSCREEN_CREATOR,FONT8);
yText = yText + 8 + 1;
drawCenterText(yText,STR_LOADSCREEN_APP_NAME_LINE1,FONT16);
yText = yText + 16 + 1;
drawCenterText(yText,STR_LOADSCREEN_APP_NAME_LINE2,FONT8 );
delay(LOAD_SCREEN_SHOW_MS);
getDisplay()->clearDisplay();
}
/**
* Call this every frame to calculate frame rate
*/
void calcFrameRate()
{
unsigned long elapsedTime = millis() - _fpsStartTimeStamp;
_frameCount++;
if (elapsedTime > 1000)
{
_fps = _frameCount / (elapsedTime / 1000.0);
_fpsStartTimeStamp = millis();
_frameCount = 0;
}
}
/**
* Draws the status bar at top of screen with points and fps
*/
void drawStatusBar()
{
// Draw accumulated points
getDisplay()->setFont(FONT8);
getDisplay()->setCursor(0, 0); // draw points
getDisplay()->print(_points);
// Draw frame count
if (_drawFrameCount)
{
int16_t x1;
getDisplay()->setFont(FONT8);
x1 = getDisplay()->getDisplayWidth() - getDisplay()->getStrWidth("XX.XX fps");
getDisplay()->setCursor(x1,0);
getDisplay()->print(_fps);
getDisplay()->print(" fps");
}
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "Arduino.h"
#include "shape.hpp"
#include "display_screen.h"
void initScreenGame(void);
void handleScreenGame(void);

View File

@@ -1,76 +1,66 @@
#include "screen_main.h" #include "screen_main.h"
c_onScreenButton ma20("20m", mA20, LocBottom, BUTTON1); void setma20(void)
c_onScreenButton ma200("200m", mA200, LocBottom, BUTTON2); {
c_onScreenButton ma1000("1A", mA1000, LocBottom, BUTTON3); setMeasureMode(e_measureMode::mA20);
c_onScreenButton mauto("Auto", mAuto, LocBottom, BUTTON4); }
c_onScreenButton bsetup("Conf", 5, LocBottom, BUTTON5);
void setma200(void)
{
setMeasureMode(e_measureMode::mA200);
}
void setma1000(void)
{
setMeasureMode(e_measureMode::mA1000);
}
void setmAuto(void)
{
setMeasureMode(e_measureMode::mAuto);
}
void buttonSetup(void)
{
setDisplayState(e_displayState::setupscreen);
log_i("Conf button pressed, go to setupscreen");
}
c_onScreenButton ma20("20m", mA20, LocBottom, BUTTON1, &setma20);
c_onScreenButton ma200("200m", mA200, LocBottom, BUTTON2, &setma200);
c_onScreenButton ma1000("1A", mA1000, LocBottom, BUTTON3, &setma1000);
c_onScreenButton mauto("Auto", mAuto, LocBottom, BUTTON4, &setmAuto);
c_onScreenButton bsetup("Conf", 5, LocBottom, BUTTON5, &buttonSetup);
c_onScreenButton errorState("ER", 6, LocRight, &getErrorState); c_onScreenButton errorState("ER", 6, LocRight, &getErrorState);
c_onScreenButton okState("OK", 7, LocRight, &getOkState); c_onScreenButton okState("OK", 7, LocRight, &getOkState);
c_onScreenButton openState("Open", 8, LocRight, &getOpenState); c_onScreenButton openState("Open", 8, LocRight, &getOpenState);
c_onScreenButton wifiState("Wifi", 9, LocRight, &getWifiState); c_onScreenButton wifiState("Wifi", 9, LocRight, &getWifiState);
std::vector<c_onScreenButton*> MainScreen; //std::vector<c_onScreenButton*> MainScreen;
screen_c mainScreen(e_displayState::mainscreen, 5);
const e_displayState thisScreen = e_displayState::mainscreen; const e_displayState thisScreen = e_displayState::mainscreen;
void initDisplayMain(void) void initDisplayMain(void)
{ {
log_i("Setup main screen : ");
uint16_t screenwidth = getDisplay()->getDisplayWidth();
uint16_t buttonwidth = ((screenwidth / mLast) - CONTROLLOFFSET * 2 + 1);
uint16_t currentWidth = 0;
uint16_t ypos = getDisplay()->getDisplayHeight() - 1 - CONTROLSLINE_H + 2;
log_i("buttons: screenW=%d, buttonW=%d, ypos=%d", screenwidth, buttonwidth, ypos);
// setup bottom buttons
ma20.begin(currentWidth, ypos, buttonwidth, CONTROLSLINE_H + 6, CONTROLRADIUS);
ma200.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
ma1000.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
mauto.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
bsetup.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
// setup right side indicators
uint16_t currentYpos = 0;
uint16_t IndicatorXpos = screenwidth - INDICATORWIDTH;
log_i("indicators: indicW=%d, indicH=%d ypos,%d", INDICATORWIDTH, INDICATORHEIGHT, IndicatorXpos);
errorState.begin(IndicatorXpos, currentYpos, INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
okState.begin(IndicatorXpos, currentYpos += (INDICATORHEIGHT - 1), INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
openState.begin(IndicatorXpos, currentYpos += (INDICATORHEIGHT - 1), INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
wifiState.begin(IndicatorXpos, currentYpos += (INDICATORHEIGHT - 1), INDICATORWIDTH, INDICATORHEIGHT, INDICATORRADIUS);
// fill vector // fill vector
log_i("Store main screen items"); log_i("Store main screen items");
MainScreen.push_back(&ma20); mainScreen.addItem(&ma20);
MainScreen.push_back(&ma200); mainScreen.addItem(&ma200);
MainScreen.push_back(&ma1000); mainScreen.addItem(&ma1000);
MainScreen.push_back(&mauto); mainScreen.addItem(&mauto);
MainScreen.push_back(&bsetup); mainScreen.addItem(&bsetup);
MainScreen.push_back(&errorState); mainScreen.addItem(&errorState);
MainScreen.push_back(&okState); mainScreen.addItem(&okState);
MainScreen.push_back(&openState); mainScreen.addItem(&openState);
MainScreen.push_back(&wifiState); mainScreen.addItem(&wifiState);
for (auto &&button : MainScreen) mainScreen.begin();
{
button->setVisible(true);
}
log_i("mainscreen OK"); log_i("mainscreen OK");
} }
void ScreenMainDrawButtons(void)
{
// draw controlstrip indicators
for (auto &&thismode : MainScreen)
{
thismode->setDisplayState((thismode->getIndex() == (uint8_t)getMeasureMode()));
thismode->drawButton();
}
}
void ScreenMainDrawValues(void) void ScreenMainDrawValues(void)
{ {
if (getDisplay() == NULL) if (getDisplay() == NULL)
@@ -92,16 +82,10 @@ void ScreenMainDrawValues(void)
getDisplay()->drawUTF8(60 + stringwidth + 3, 43, "mΩ"); getDisplay()->drawUTF8(60 + stringwidth + 3, 43, "mΩ");
} }
e_displayState ScreenMainHandle(void) void ScreenMainHandle(void)
{ {
for (auto &&i : MainScreen) mainScreen.activateModeButton();
{ mainScreen.handle();
i->handle(); mainScreen.draw();
} ScreenMainDrawValues();
if(bsetup.getState())
{
return e_displayState::setupscreen;
}
return thisScreen;
} }

View File

@@ -4,8 +4,8 @@
#include "display_types.h" #include "display_types.h"
#include "display_draw.h" #include "display_draw.h"
#include "display_buttons.h" #include "display_buttons.h"
#include "display_screen.h"
void initDisplayMain(void); void initDisplayMain(void);
void ScreenMainDrawValues(void); void ScreenMainHandle(void);
void ScreenMainDrawButtons(void);
e_displayState ScreenMainHandle(void);

View File

@@ -1,53 +1,42 @@
#include "screen_setup.h" #include "screen_setup.h"
c_onScreenButton bback("<", mA20, LocBottom, BUTTON1); void buttonExit(void)
c_onScreenButton bforw(">", mA200, LocBottom, BUTTON2); {
c_onScreenButton bOK("OK", mA1000, LocBottom, BUTTON3); setDisplayState(e_displayState::mainscreen);
c_onScreenButton bGame("Game", mAuto, LocBottom, BUTTON4); log_i("Exit button pressed, go to mainscreen");
c_onScreenButton bExit("Exit", 5, LocBottom, BUTTON5); }
std::vector<c_onScreenButton *> SetupScreen; void buttonGame(void)
const e_displayState thisScreen = e_displayState::setupscreen; {
setDisplayState(e_displayState::gamescreen);
SetFPSMax();
initScreenGame();
log_i("game button pressed, go to gamescreen");
}
c_onScreenButton bback("<", 1, LocBottom, BUTTON1, NULL);
c_onScreenButton bforw(">", 2, LocBottom, BUTTON2, NULL);
c_onScreenButton bOK("OK", 3, LocBottom, BUTTON3, NULL);
c_onScreenButton bGame("Game", 4, LocBottom, BUTTON4, &buttonGame);
c_onScreenButton bExit("Exit", 5, LocBottom, BUTTON5, &buttonExit);
screen_c setupScreen(e_displayState::setupscreen, 5);
void initSetupScreen(void) void initSetupScreen(void)
{ {
uint16_t screenwidth = getDisplay()->getDisplayWidth(); setupScreen.addItem(&bback);
uint16_t buttonwidth = ((screenwidth / mLast) - CONTROLLOFFSET * 2 + 1); setupScreen.addItem(&bforw);
uint16_t currentWidth = 0; setupScreen.addItem(&bOK);
uint16_t ypos = getDisplay()->getDisplayHeight() - 1 - CONTROLSLINE_H + 2; setupScreen.addItem(&bGame);
setupScreen.addItem(&bExit);
bback.begin(currentWidth, ypos, buttonwidth, CONTROLSLINE_H + 6, CONTROLRADIUS); setupScreen.begin();
bforw.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
bOK.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
bGame.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
bExit.begin(currentWidth += (buttonwidth - 1), ypos, buttonwidth, CONTROLSLINE_H + CONTROLRADIUS, CONTROLRADIUS);
SetupScreen.push_back(&bback);
SetupScreen.push_back(&bforw);
SetupScreen.push_back(&bOK);
SetupScreen.push_back(&bGame);
SetupScreen.push_back(&bExit);
} }
void ScreenSetupDrawButtons(void) void ScreenSetupHandle(void)
{ {
// draw controlstrip indicators setupScreen.handle();
for (auto &&thismode : SetupScreen) setupScreen.draw();
{
thismode->drawButton();
}
}
e_displayState ScreenSetupHandle(void)
{
for (auto &&i : SetupScreen)
{
i->handle();
}
if(bExit.getState())
{
return e_displayState::mainscreen;
}
return thisScreen;
} }

View File

@@ -3,8 +3,9 @@
#include "Arduino.h" #include "Arduino.h"
#include "display_types.h" #include "display_types.h"
#include "display_buttons.h" #include "display_buttons.h"
#include "display_screen.h"
#include "screen_game.h"
void initSetupScreen(void); void initSetupScreen(void);
e_displayState ScreenSetupHandle(void); void ScreenSetupHandle(void);
void ScreenSetupDrawButtons(void);

View File

@@ -0,0 +1,493 @@
#include "display_hall.h"
class Shape {
protected:
int _x;
int _y;
int _width;
int _height;
bool _drawFill = DEFAULT_DRAW_FILL;
bool _drawBoundingBox = false;
public:
const bool DEFAULT_DRAW_FILL = false;
Shape(int x, int y, int width, int height)
: Shape(x, y, width, height, DEFAULT_DRAW_FILL){
// purposefully empty
}
Shape(int x, int y, int width, int height, bool drawFillOn) {
_x = x;
_y = y;
_width = width;
_height = height;
_drawFill = drawFillOn;
}
/**
* @brief Toggles whether to draw the bounding box around the shape
*
* @param drawBoundingBox If true, then a bounding box will be drawn around the shape
*/
void setDrawBoundingBox(bool drawBoundingBox){
_drawBoundingBox = drawBoundingBox;
}
/**
* @brief Toggles whether to draw the object as filled or just an outline
*
* @param drawFill If true, then the shape will be drawn filled (solid)
*/
void setDrawFill(bool drawFill){
_drawFill = drawFill;
}
/**
* @brief Convenience function to set location of shape. Simply calls setX(x) and setY(y)
*
* @param x The x location of the shape
* @param y The y location of the shape
*/
void setLocation(int x, int y){
setX(x);
setY(y);
}
/**
* @brief Sets the y location of the shape
*
* @param y The y location of the shape
*/
void setY(int y){
_y = y;
}
/**
* @brief Sets the x location of the shape
*
* @param x The x location of the shape
*/
void setX(int x){
_x = x;
}
/**
* @brief Gets the x location of the shape
*
* @return int x location of the shape
*/
int getX() const{
return _x;
}
/**
* @brief Gets the y location of the shape
*
* @return int y locaiton of the shape
*/
int getY() const{
return _y;
}
/**
* @brief A virtual function that should be overloaded by sub-classes. Deriving
* classes should call
*
* @param display A reference to the Adafruit_SSD1306 object
*/
virtual void draw( void) {
if(_drawBoundingBox){
getDisplay()->drawFrame(_x, _y, _width, _height); //drawRect(_x, _y, _width, _height, SSD1306_WHITE);
}
}
/**
* @brief Set the size (width, height) of the shape. This is a virtual
* function. Deriving classes may want to handle setDimensions differently.
* For example, the Circle class forces only uses the 'width' parameter
* and sets height = width.
*
* @param width The width of the shape
* @param height The height of the shape
*/
virtual void setDimensions(int width, int height){
_width = width;
_height = height;
}
/**
* @brief Get the width of the shape
*
* @return int Width of the shape in pixels
*/
int getWidth() const{
return _width;
}
/**
* @brief Get the height of the shape
*
* @return int Height of the shape in pixels
*/
int getHeight() const{
return _height;
}
/**
* @brief Get the left (x) location of the shape, which is just getX()
*
* @return int The left location of the shape
*/
int getLeft() const{
return _x;
}
/**
* @brief Get the right (x) location, which is getX() + getWidth()
*
* @return int The right location (x) of the shape
*/
int getRight() const{
return _x + _width;
}
/**
* @brief Get the bottom of the shape (y), which is getY() + getHeight()
*
* @return int
*/
int getBottom() const{
return _y + _height;
}
/**
* @brief Get the top of the shape, which is just getY()
*
* @return int
*/
int getTop() const{
return _y;
}
/**
* @brief Attempts to force the x,y location of the shape into the
* provided rectangular boundary (x, y, width, height)
*
* @param x X location of boundary
* @param y Y location of boundary
* @param width Width of boundary
* @param height Height of boundary
*/
void forceInside(int x, int y, int width, int height){
if(getTop() <= y){
setY(y);
}else if(getBottom() >= y + height){
setY((y + height) - getHeight() - 1);
}
if(getLeft() <= x){
setX(x);
}else if(getRight() >= x + width){
setX((x + width) - getWidth() - 1);
}
}
/**
* @brief Checks to see if the current shape overlaps with the provided shape.
* Overlap is defined as any pixel between this shape touching (equaling)
* the passed in shape
*
* @param shape The shape to check for overlap
* @return true If the shape overlaps
* @return false If the shape does not overlap
*/
virtual bool overlaps(const Shape& shape) const{
//Serial.println("We are in overlaps shape!");
// based on https://stackoverflow.com/a/4098512
return !(getRight() < shape._x ||
getBottom() < shape._y ||
_x > shape.getRight() ||
_y > shape.getBottom());
}
/**
* @brief Checks if this shape contains the x,y location. If so, returns true.
*
* @param x The X location to check
* @param y The Y location to check
* @return true If this shape contains the x,y
* @return false If this shape does NOT contain the x,y
*/
virtual bool contains(int x, int y) const {
return x >= _x && // check within left edge
x <= (_x + _width) && // check within right edge
y >= _y && // check within top edge
y <= (_y + _height); // check within bottom edge
}
/**
* @brief Gets the name of this object. Sub-classes should override this.
*
* @return String
*/
virtual String getName() const{
return "Shape";
}
/**
* @brief Gets a basic toString() for the object. Sub-classes should override
* to get a more fitting description
*
* @return String
*/
virtual String toString() const{
return (String)"x: " + _x + " y: " + _y + " width: " + _width + " height: " + _height;
}
/**
* @brief Static function that calculates the Euclidean distance between two points
*
* @param x1
* @param y1
* @param x2
* @param y2
* @return float
*/
static float distance(int x1, int y1, int x2, int y2){
return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
}
};
/**
* @brief A Rectangle shape class that extends Shape.
*
*/
class Rectangle : public Shape {
public:
Rectangle(int x, int y, int width, int height)
: Rectangle(x, y, width, height, DEFAULT_DRAW_FILL)
{
// purposefully empty
}
Rectangle(int x, int y, int width, int height, bool drawFillOn)
: Shape(x, y, width, height)
{
// purposefully empty
}
/**
* @brief Draw the rectangle
*
* @param display
*/
void draw (void) override{
// Draw rectangle takes in (xTop, yTop, width, height)
// https://learn.adafruit.com/adafruit-gfx-graphics-library/graphics-primitives#rectangles-2002784-10
if(_drawFill){
getDisplay()->drawFrame(_x, _y, _width, _height);
}else{
getDisplay()->drawBox(_x, _y, _width, _height);
}
// We don't call the parent class draw() call because
// the bounding box is simple another rectangle
// Shape::draw(disp);
}
String getName() const override{
return "Rectangle";
}
};
/**
* @brief A circular shape class that extends Shape
*
*/
class Circle : public Shape {
public:
Circle(int xCenter, int yCenter, int radius) :
Circle(xCenter, yCenter, radius, DEFAULT_DRAW_FILL)
{
// purposefully empty
}
Circle(int xCenter, int yCenter, int radius, bool drawFillOn) :
Shape(xCenter - radius, yCenter - radius, (radius * 2) + 1, (radius * 2) + 1, drawFillOn)
{
// purposefully empty
}
/**
* @brief Draw the circle
*
* @param display
*/
void draw() override{
// Draw circle takes in (xCenter, yCenter, radius)
// https://learn.adafruit.com/adafruit-gfx-graphics-library/graphics-primitives#circles-2002788-14
int radius = getRadius();
if(_drawFill){
getDisplay()->drawDisc(_x + radius, _y + radius, radius);
}else{
getDisplay()->drawCircle(_x + radius, _y + radius, radius);
}
// Call super method
Shape::draw();
}
/**
* @brief Calculates the overlap between two circular objects
*
* @param circle
* @return true
* @return false
*/
bool overlaps(const Circle& circle) const {
int distanceFromCenterPoints = Shape::distance(getCenterX(), getCenterY(), circle.getCenterX(), circle.getCenterY());
return distanceFromCenterPoints <= getRadius() + circle.getRadius();
}
/**
* @brief Calculates the overlap between two shapes
*
* @param shape
* @return true
* @return false
*/
bool overlaps(const Shape& shape) const override{
if(getName().equals(shape.getName())){
return this->overlaps((Circle&)shape);
}
// Default to parent overlaps function
return Shape::overlaps(shape);
}
/**
* @brief Uses the width value to set the circle diameter. The height parameter is ignored.
*
* @param width sets the circle diameter
* @param height ignored
*/
void setDimensions(int width, int height) override{
Shape::setDimensions(width, width);
}
/**
* @brief Get the X center of the circle
*
* @return int
*/
int getCenterX() const{
return _x + getWidth() / 2;
}
/**
* @brief Get the Y center of the circle
*
* @return int
*/
int getCenterY() const{
return _y + getWidth() / 2;
}
/**
* @brief Moves the location of the object so that the center is at x,y
*
* @param x
* @param y
* @return int
*/
void setCenter(int x, int y){
int radius = getRadius();
setLocation(x - radius, y - radius);
}
/**
* @brief Get the radius of the circle
*
* @return int
*/
int getRadius() const{
return getWidth() / 2;
}
/**
* @brief Set the radius of the circle
*
* @param radius
*/
void setRadius(int radius){
int size = 2 * radius;
setDimensions(size, size);
}
/**
* @brief Get the name of the circle
*
* @return String
*/
String getName() const override{
return "Circle";
}
};
class Ball : public Circle{
protected:
int _xSpeed = 0;
int _ySpeed = 0;
public:
Ball(int xCenter, int yCenter, int radius) : Circle(xCenter, yCenter, radius)
{
}
boolean checkYBounce(int yMin, int yMax){
return getTop() <= yMin || getBottom() >= yMax;
}
boolean checkXBounce(int xMin, int xMax){
return getLeft() <= xMin || getRight() >= xMax;
}
void setSpeed(int xSpeed, int ySpeed){
_xSpeed = xSpeed;
_ySpeed = ySpeed;
}
int getXSpeed() const{
return _xSpeed;
}
int getYSpeed() const{
return _ySpeed;
}
void update(){
_x += _xSpeed;
_y += _ySpeed;
}
int reverseYSpeed(){
_ySpeed = _ySpeed * -1;
return _ySpeed;
}
int reverseXSpeed(){
_xSpeed = _xSpeed * -1;
return _xSpeed;
}
String getName() const override{
return "Ball";
}
};