383 lines
14 KiB
C++

/* ===============================================
* - THIS IS TAKEONE ESP32 BOARD -
* ===============================================
* MPU9250 Address : 0X69 (ADDRESS)
* OLED 128x64 Address : 0X3C (ADDRESS)
* -----------------------------------------------
* Description : PIN GPIO#
* -----------------------------------------------
* NEOPIXEL LED STRIP : A0/GPIO 26
* PIEZO SENSOR : A2/GPIO 34 - ANALOG ONLY INPUT
* RANDOM SEED : A3/GPIO 39 - ANALOG ONLY INPUT
* BUTTONS : TLB (35), BLB (15), CB (33), BRB (27), TRB(12)
* I2C PIN : SDA 21, SCL 22 (DIGITAL)
* SPI PIN : CS 5, SCK 18, MISO 19, MOSI 23 (DIGITAL)
* BOOST CONTROL PIN : 14 (DIGITAL) OUTPUT ON PADS (TP1 +5V, TP2 GND)
* LED PIN : 13 (DIGITAL)
* BATTERY MONITOR PIN : 35 (ANALOG)
* -----------------------------------------------
* BUTTONS PINOUTS : PIN GPIO
* -----------------------------------------------
* TOP LEFT BUTTON : 15 - SWITCH PREVIOUS MENU / CHANGE PARAMETER
* TOP RIGHT BUTTON : 12 - SWITCH NEXT MENU / CHANGE PARAMETER
* BOTTOM LEFT BUTTON : 32 - POWER INC & BACK & INC PARAMETER
* BOTTOM RIGHT BUTTON : 27 - POWER DEC & SAVE & DEC PARAMETER
* CENTER BUTTON : 33 - POWER ON & SELECT
* -----------------------------------------------
* Exclusive Features Of TAKEONE BOARD
* -----------------------------------------------
* ESP32 TAKEONE Board has some unique hardware features
* 1. Battery Voltage Level Monitoring Via Analog Pin 35
* 2. Boost Converter In Case We Need 5V Supply From Li-Ion Battery
* 3. I2C : Motion Sensor IMU/MPU9250 9 Degrees Of Fredom
* 4. SPI : SDCARD Reader */
//======================================================
// LIBRARIES
//======================================================
#include <Wire.h>
#include <EEPROM.h>
#include <Button.h>
#include <TimeTrigger.h>
#include <ArduinoJson.h>
#include <BluetoothSerial.h>
#include <Adafruit_NeoPixel.h>
//======================================================
// OVER THE AIR PROGRAMMING
//======================================================
// #include <WiFi.h>
// #include <ESPmDNS.h>
// #include <WiFiUdp.h>
// #include <ArduinoOTA.h>
//======================================================
// PUBLIC VARIABLES
//======================================================
uint8_t MENU_COUNT = 1;
bool BLUETOOTH_STATUS = false;
bool CHARGE_STATE = false;
bool DISPLAY_CHANGED = false;
//======================================================
// HARDWARE DEFINITIONS
//======================================================
// Nessesary Definition For Bluetooth Library
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
//------------------------------------------------------
// Serial Terminal
//------------------------------------------------------
#define Terminal Serial // SERIAL PORT USED AS TERMINAL WINDO FOR DEBUG
#define isTerminal Terminal.available() // Terminal Serial Buffer Available
#define flushTerminal while(isTerminal) { Terminal.read(); }
//------------------------------------------------------
// Bluetooth Terminal
//------------------------------------------------------
BluetoothSerial Bluetooth; // Here We Use the Native Bluetooth In The ESP32
#define isBluetooth Bluetooth.available() // Bluetooth Serial Buffer Available
#define flushBluetooth while(isBluetooth) { Bluetooth.read(); }
//------------------------------------------------------
// Buttons
//------------------------------------------------------
Button BTN_PREVIOUS(15); // TL : 15 - SWITCH PREVIOUS MENU / CHANGE PARAMETER
Button BTN_NEXT(12); // TR : 12 - SWITCH NEXT MENU / CHANGE PARAMETER
Button BTN_CENTER(33); // CT : 33 - POWER INC & BACK & INC PARAMETER
Button BTN_INCREASE(32); // BL : 32 - POWER DEC & SAVE & DEC PARAMETER
Button BTN_DECREASE(27); // BR : 27 - POWER ON & SELECT
//------------------------------------------------------
// Boost 3V3 To 5V
//------------------------------------------------------
#define BOOST_PIN 14
#define BOOST_SETUP pinMode(BOOST_PIN, OUTPUT)
#define BOOST_ON digitalWrite(BOOST_PIN, HIGH)
#define BOOST_OFF digitalWrite(BOOST_PIN, LOW)
#define BOOST_STU digitalRead(BOOST_PIN)
//------------------------------------------------------
// Keep Power On
//------------------------------------------------------
#define POWER_PIN 13
#define POWER_SETUP pinMode(POWER_PIN, OUTPUT)
#define POWER_OFF digitalWrite(POWER_PIN, LOW)
#define POWER_ON digitalWrite(POWER_PIN, HIGH)
TimeTrigger POWER_PRESS(4000); // The Interval In mSeconds
//------------------------------------------------------
// Piezo Transducer
//------------------------------------------------------
#define VTH 15 // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR
#define PIEZO_PIN 34 // IMPACT SENSOR
#define RANDOM_SOURCE 39 // RANDOM NUMBER GENERATOR SOURCE
#define PIEZO_READ analogRead(PIEZO_PIN)
#define PIEZO_MAP map(PIEZO_READ, VTH, 4095, 0, 100)
//------------------------------------------------------
// Battery Level Monitoring
//------------------------------------------------------
#define BATTERY_PIN 35
#define BATTERY (float)(((analogRead(BATTERY_PIN) * (3.3 / 4096)) * 2) + 0.31)
#define CHARGE round(mapf(BATTERY, 3.27, 4.31, 0, 100), 1)
//------------------------------------------------------
// Power Charging Status
//------------------------------------------------------
#define STAT_PIN 25
#define STAT_SETUP pinMode(STAT_PIN, INPUT)
#define STAT_READ digitalRead(STAT_PIN)
//------------------------------------------------------
// Neo Pixel LED Strip
//------------------------------------------------------
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
#define PIN 6 // On Trinket or Gemma, suggest changing this to 1
#define NUMPIXELS 16 // Number Of LED's
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
//------------------------------------------------------
// JSON Object & Memory
//------------------------------------------------------
#define CAPACITY 200
DynamicJsonDocument JSON(CAPACITY); // Building JSON Buffer
// FIXED OPERATION MODE MESSAGES ~~~~~~~~~~~~~~~~~~~~~~
#define BLUETOOTH_START "{\"status\":\"BLUETOOTH STARTED\"}"
#define BLUETOOTH_FAIL "{\"status\":\"BLUETOOTH FAILED\"}"
#define BLUETOOTH_CONNECTED "{\"status\":\"BLUETOOTH CONNECTED\"}"
#define BLUETOOTH_DISCONNECTED "{\"status\":\"BLUETOOTH DISCONNECTED\"}"
#define STAND_ALONE_MODE "{\"status\":\"STAND ALONE MODE\"}"
// FIXED MESSAGES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define POWERUP "{\"status\":\"POWER UP\"}"
#define POWEROFF "{\"status\":\"POWER OFF\"}"
#define UNDERSTOOD "{\"status\":\"OK\"}"
#define JSON_ERROR "{\"status\":\"ERROR JSON\"}"
#define TARGET_MET "{\"status\":\"TARGET MEET\"}"
#define GAME_OVER "{\"status\":\"GAME OVER\"}"
#define TIME_OVER "{\"status\":\"TIME OVER\"}"
#define UNKNOWN_GAME "{\"status\":\"UNKNOWN GAME\"}"
// GAME NAMES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define GM_COUNTER "{\"gm\":\"COUNTER\"}"
#define GM_TATTACK "{\"gm\":\"TIME ATTACK\"}"
#define GM_REACTION "{\"gm\":\"REACTION\"}"
#define GM_DECISION "{\"gm\":\"DECISION\"}"
#define GM_STAMINA "{\"gm\":\"STAMINA\"}"
// COMMAND LIST ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define CM_START "{\"cm\":\"START\"}"
#define CM_RESET "{\"cm\":\"RESET\"}"
#define CM_STOP "{\"cm\":\"STOP\"}"
#define CM_EXIT "{\"cm\":\"EXIT\"}"
//======================================================
// MAIN SETUP
//======================================================
void setup()
{
// Power Hold
POWER_SETUP;
POWER_ON;
// Charging Status Pin Setup
STAT_SETUP;
// I2C Module & OLED
Wire.begin();
delay(5);
OLED_INIT();
OLED_CLEAR();
OLED_SHOW();
// IF Charger Connected Dont Allow Any One To Play
// Turn On Booster
BOOST_SETUP;
BOOST_ON;
// Start Buttons Function
BTN_PREVIOUS.begin();
BTN_NEXT.begin();
BTN_CENTER.begin();
BTN_INCREASE.begin();
BTN_DECREASE.begin();
// Communication Ports
Terminal.begin(9600); Terminal.println();
// To Detect Connection / Disconnection Event
Bluetooth.register_callback(callback);
// Starting Bluetooth Wireless With Name (KICKER)
if(!Bluetooth.begin("KICKER")) {
terminal(POWERUP);
terminal(BLUETOOTH_FAIL);
} else {
dualcomm(POWERUP);
dualcomm(BLUETOOTH_START);
}
// The Random Number Source
randomSeed(analogRead(RANDOM_SOURCE));
// Display Welcome Screen
KICKER_LOGO();
delay(2000);
}
//======================================================
// MAIN PROGRAM LOOP
//======================================================
void loop()
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This Is For Bluetooth Mode
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if(BLUETOOTH_STATUS == true) {
// Initialization Of Bluetooth Connected Mode
OLED_BLUETOOTH_MODE(); // Display Bluetooth Connected Mode
DISPLAY_CHANGED = false; // Give Permition To Change The Content
delay(3000); // Hold The Display Content For 3 Seconds
// While You Are On Bluetooth Connected Mode
while(BLUETOOTH_STATUS == true) {
ListenToBluetooth(); // Reading Only From Bluetooth
}
// Give Permition To Change The Content
DISPLAY_CHANGED = false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This Is For Stand Alone Mode
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
else {
// Initialization Of Stand Alone Mode
OLED_STANDALONE_MODE(); // Display Stand Alone Mode
DISPLAY_CHANGED = false; // Give Permition To Change The Content
delay(3000); // Hold The Display Content For 3 Seconds
// While You Are On Stand Alone Mode
while(BLUETOOTH_STATUS == false) {
ListenToStandAlone(); // Enter Listen To Stand Alone Buttons
}
// Once The Stand Alone Mode Is Over
DISPLAY_CHANGED = false; // Give Permition To Change The Display
}
}
//------------------------------------------------------
// LISTEN TO BLUETOOTH
//------------------------------------------------------
void ListenToBluetooth()
{
if(isBluetooth)
{
// Reading Message From Bluetooth
String msg = Bluetooth.readStringUntil('\n');
flushBluetooth;
msg.trim();
// Display On Serial Monitor For Debuging
terminal(msg);
// If It Was A Command To Power OFf
if(msg == POWEROFF) {
OLED_POWERING_DOWN();
}
// Deserialize the JSON document
DeserializationError error = deserializeJson(JSON, msg);
// If The System Understood The Message Clearly
if(!error)
{
// Sending Status Message
dualcomm(UNDERSTOOD);
// Reading Game Name & Settings
String game = JSON["gm"];
String settings = JSON["set"];
// Clearing JSON Buffer
JSON.clear();
// Entering To Game Selector
bluetooth_game_selector(game, settings);
}
else
{
terminal(JSON_ERROR);
}
}
}
//------------------------------------------------------
// LISTEN TO STAND ALONE
//------------------------------------------------------
void ListenToStandAlone()
{
// SHIFT TO PREVIOUS GAME MENU
if(BTN_PREVIOUS.pressed())
{
// Checking The Condition Of Order Number
if(MENU_COUNT == 1 || MENU_COUNT < 1) {
MENU_COUNT = 3; // If The Value Equals To Zero Then Roll Back To 4
} else {
MENU_COUNT -= 1;
}
// Give Permition To Change The Screen
DISPLAY_CHANGED = false;
}
// SHIFT TO NEXT GAME MENU
else if(BTN_NEXT.pressed())
{
// Checking The Condition Of Order Number
if(MENU_COUNT == 3 || MENU_COUNT > 3)
{
MENU_COUNT = 1; // If The Value Exceeds 4 Then Roll Back To 1
} else {
MENU_COUNT += 1;
}
// Give Permition To Change The Screen
DISPLAY_CHANGED = false;
}
// SELECT CURRENT DISPLAYED MENU
else if(BTN_CENTER.pressed())
{
// IF LONG PRESS -> POWER OFF KICKER
DETECT_LONG_PRESS();
// IF SHORT PRESS -> PLAY GAME CORESPONDING TO NUMBER
switch(MENU_COUNT)
{
case 1: // Play Counter Game
Terminal.println("SELECTED -> GAME COUNTER");
game_counter_standalone();
break;
case 2: // Play Time Attack Game
Terminal.println("SELECTED -> GAME TIME ATTACK");
time_attack_standalone();
break;
case 3: // Play Reaction Game
Terminal.println("SELECTED -> GAME REACTION");
game_reaction_standalone();
break;
}
}
// Display The Game Pages
if(DISPLAY_CHANGED == false)
{
// IF Previous / Next Buttons Are Pressed -> Display Menu Selection On Terminal
switch(MENU_COUNT)
{
case 1: // Play Counter Game
//Terminal.println("MENU #" + String(MENU_COUNT) + " - " + "GAME COUNTER");
GAME_TITLE_DISPLAY(MENU_COUNT, "COUNTER");
break;
case 2: // Play Time Attack Game
//Terminal.println("MENU #" + String(MENU_COUNT) + " - " + "GAME TIME ATTACK");
GAME_TITLE_DISPLAY(MENU_COUNT, "TIME ATTACK");
break;
case 3: // Play Reaction Game
//Terminal.println("MENU #" + String(MENU_COUNT) + " - " + "GAME REACTION");
GAME_TITLE_DISPLAY(MENU_COUNT, "REACTION");
break;
}
}
}